@dmop/puru 0.1.10 → 0.1.11
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/AGENTS.md +136 -1
- package/README.md +120 -165
- package/dist/index.cjs +472 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +265 -3
- package/dist/index.d.ts +265 -3
- package/dist/index.js +461 -6
- package/dist/index.js.map +1 -1
- package/llms-full.txt +174 -7
- package/llms.txt +9 -5
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -44,6 +44,28 @@ interface Channel<T extends ChannelValue> {
|
|
|
44
44
|
/** Resolves with the next value, or `null` if the channel is closed. */
|
|
45
45
|
recv(): Promise<T | null>;
|
|
46
46
|
close(): void;
|
|
47
|
+
/** Number of values currently buffered. Like Go's `len(ch)`. */
|
|
48
|
+
readonly len: number;
|
|
49
|
+
/** Buffer capacity. Like Go's `cap(ch)`. */
|
|
50
|
+
readonly cap: number;
|
|
51
|
+
/** Returns a send-only view of this channel. Like Go's `chan<- T`. */
|
|
52
|
+
sendOnly(): SendOnly<T>;
|
|
53
|
+
/** Returns a receive-only view of this channel. Like Go's `<-chan T`. */
|
|
54
|
+
recvOnly(): RecvOnly<T>;
|
|
55
|
+
[Symbol.asyncIterator](): AsyncIterator<T>;
|
|
56
|
+
}
|
|
57
|
+
/** Send-only view of a channel. Like Go's `chan<- T`. */
|
|
58
|
+
interface SendOnly<T extends ChannelValue> {
|
|
59
|
+
send(value: T): Promise<void>;
|
|
60
|
+
close(): void;
|
|
61
|
+
readonly len: number;
|
|
62
|
+
readonly cap: number;
|
|
63
|
+
}
|
|
64
|
+
/** Receive-only view of a channel. Like Go's `<-chan T`. */
|
|
65
|
+
interface RecvOnly<T extends ChannelValue> {
|
|
66
|
+
recv(): Promise<T | null>;
|
|
67
|
+
readonly len: number;
|
|
68
|
+
readonly cap: number;
|
|
47
69
|
[Symbol.asyncIterator](): AsyncIterator<T>;
|
|
48
70
|
}
|
|
49
71
|
/**
|
|
@@ -76,6 +98,79 @@ interface Channel<T extends ChannelValue> {
|
|
|
76
98
|
*/
|
|
77
99
|
declare function chan<T extends ChannelValue>(capacity?: number): Channel<T>;
|
|
78
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Hierarchical cancellation, deadlines, and request-scoped values — modeled after Go's `context` package.
|
|
103
|
+
*
|
|
104
|
+
* Context is the glue that makes cancellation and timeouts composable. Derive child
|
|
105
|
+
* contexts from a parent: when the parent is cancelled, all children cancel automatically.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* // Timeout a group of tasks
|
|
109
|
+
* const [ctx, cancel] = withTimeout(background(), 5000)
|
|
110
|
+
* const wg = new WaitGroup()
|
|
111
|
+
* wg.spawn(() => longRunningWork())
|
|
112
|
+
* ctx.done().then(() => wg.cancel())
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* // Nested deadlines
|
|
116
|
+
* const [parent, cancelParent] = withCancel(background())
|
|
117
|
+
* const [child, _] = withTimeout(parent, 1000)
|
|
118
|
+
* // child cancels after 1s OR when cancelParent() is called — whichever comes first
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* // Request-scoped values
|
|
122
|
+
* const reqCtx = withValue(background(), 'requestId', 'abc-123')
|
|
123
|
+
* reqCtx.value('requestId') // 'abc-123'
|
|
124
|
+
*/
|
|
125
|
+
/** Returned by `ctx.err` when the context was explicitly cancelled. */
|
|
126
|
+
declare class CancelledError extends Error {
|
|
127
|
+
constructor(message?: string);
|
|
128
|
+
}
|
|
129
|
+
/** Returned by `ctx.err` when the context's deadline has passed. */
|
|
130
|
+
declare class DeadlineExceededError extends Error {
|
|
131
|
+
constructor();
|
|
132
|
+
}
|
|
133
|
+
type ContextError = CancelledError | DeadlineExceededError;
|
|
134
|
+
interface Context {
|
|
135
|
+
/** AbortSignal that fires when this context is cancelled or its deadline expires. */
|
|
136
|
+
readonly signal: AbortSignal;
|
|
137
|
+
/** The deadline for this context, or `null` if none was set. */
|
|
138
|
+
readonly deadline: Date | null;
|
|
139
|
+
/** The cancellation error, or `null` if the context is still active. */
|
|
140
|
+
readonly err: ContextError | null;
|
|
141
|
+
/** Retrieves a value stored in this context or any of its ancestors. */
|
|
142
|
+
value<T = unknown>(key: symbol | string): T | undefined;
|
|
143
|
+
/** Returns a promise that resolves when the context is cancelled. */
|
|
144
|
+
done(): Promise<void>;
|
|
145
|
+
}
|
|
146
|
+
type CancelFunc = (reason?: string) => void;
|
|
147
|
+
/**
|
|
148
|
+
* Returns the root context. It is never cancelled, has no deadline, and carries no values.
|
|
149
|
+
* All other contexts should derive from this.
|
|
150
|
+
*/
|
|
151
|
+
declare function background(): Context;
|
|
152
|
+
/**
|
|
153
|
+
* Returns a child context and a cancel function. Calling `cancel()` cancels the child
|
|
154
|
+
* and all contexts derived from it. The child also cancels when the parent does.
|
|
155
|
+
*/
|
|
156
|
+
declare function withCancel(parent: Context): [Context, CancelFunc];
|
|
157
|
+
/**
|
|
158
|
+
* Returns a child context that automatically cancels at the given `deadline`.
|
|
159
|
+
* If the parent has an earlier deadline, that deadline is inherited.
|
|
160
|
+
* The returned cancel function can cancel early and clears the timer.
|
|
161
|
+
*/
|
|
162
|
+
declare function withDeadline(parent: Context, deadline: Date): [Context, CancelFunc];
|
|
163
|
+
/**
|
|
164
|
+
* Returns a child context that automatically cancels after `ms` milliseconds.
|
|
165
|
+
* Equivalent to `withDeadline(parent, new Date(Date.now() + ms))`.
|
|
166
|
+
*/
|
|
167
|
+
declare function withTimeout(parent: Context, ms: number): [Context, CancelFunc];
|
|
168
|
+
/**
|
|
169
|
+
* Returns a child context carrying a key-value pair.
|
|
170
|
+
* Values are retrieved with `ctx.value(key)` and looked up through the ancestor chain.
|
|
171
|
+
*/
|
|
172
|
+
declare function withValue<T = unknown>(parent: Context, key: symbol | string, value: T): Context;
|
|
173
|
+
|
|
79
174
|
/**
|
|
80
175
|
* Run a function in a worker thread. Returns a handle with the result promise and a cancel function.
|
|
81
176
|
*
|
|
@@ -121,6 +216,7 @@ declare function spawn<T extends StructuredCloneValue, TChannels extends Record<
|
|
|
121
216
|
priority?: 'low' | 'normal' | 'high';
|
|
122
217
|
concurrent?: boolean;
|
|
123
218
|
channels?: TChannels;
|
|
219
|
+
ctx?: Context;
|
|
124
220
|
}): SpawnResult<T>;
|
|
125
221
|
|
|
126
222
|
type SpawnChannels$1 = Record<string, Channel<ChannelValue>>;
|
|
@@ -157,6 +253,8 @@ type SpawnChannels$1 = Record<string, Channel<ChannelValue>>;
|
|
|
157
253
|
declare class WaitGroup<T extends StructuredCloneValue = StructuredCloneValue> {
|
|
158
254
|
private tasks;
|
|
159
255
|
private controller;
|
|
256
|
+
private ctx?;
|
|
257
|
+
constructor(ctx?: Context);
|
|
160
258
|
/**
|
|
161
259
|
* An `AbortSignal` shared across all tasks in this group.
|
|
162
260
|
* Pass it into spawned functions so they can stop early when `cancel()` is called.
|
|
@@ -223,11 +321,23 @@ declare class ErrGroup<T extends StructuredCloneValue = StructuredCloneValue> {
|
|
|
223
321
|
private controller;
|
|
224
322
|
private firstError;
|
|
225
323
|
private hasError;
|
|
324
|
+
private ctx?;
|
|
325
|
+
private limit;
|
|
326
|
+
private inFlight;
|
|
327
|
+
private waiting;
|
|
328
|
+
constructor(ctx?: Context);
|
|
226
329
|
get signal(): AbortSignal;
|
|
330
|
+
/**
|
|
331
|
+
* Set the maximum number of tasks that can run concurrently.
|
|
332
|
+
* Like Go's `errgroup.SetLimit()`. Must be called before any `spawn()`.
|
|
333
|
+
* A value of 0 (default) means unlimited.
|
|
334
|
+
*/
|
|
335
|
+
setLimit(n: number): void;
|
|
227
336
|
spawn<TChannels extends SpawnChannels = Record<never, never>>(fn: (() => T | Promise<T>) | ((channels: TChannels) => T | Promise<T>), opts?: {
|
|
228
337
|
concurrent?: boolean;
|
|
229
338
|
channels?: TChannels;
|
|
230
339
|
}): void;
|
|
340
|
+
private doSpawn;
|
|
231
341
|
wait(): Promise<T[]>;
|
|
232
342
|
cancel(): void;
|
|
233
343
|
}
|
|
@@ -269,6 +379,98 @@ declare class Mutex {
|
|
|
269
379
|
withLock<T>(fn: () => T | Promise<T>): Promise<T>;
|
|
270
380
|
get isLocked(): boolean;
|
|
271
381
|
}
|
|
382
|
+
/**
|
|
383
|
+
* Async read-write mutex. Multiple readers can hold the lock simultaneously,
|
|
384
|
+
* but writers get exclusive access. Modeled after Go's `sync.RWMutex`.
|
|
385
|
+
*
|
|
386
|
+
* Use this instead of `Mutex` when reads vastly outnumber writes — concurrent
|
|
387
|
+
* readers improve throughput without sacrificing write safety.
|
|
388
|
+
*
|
|
389
|
+
* Like `Mutex`, this operates on the main thread only. For cross-thread
|
|
390
|
+
* coordination, use channels instead.
|
|
391
|
+
*
|
|
392
|
+
* @example
|
|
393
|
+
* const rw = new RWMutex()
|
|
394
|
+
*
|
|
395
|
+
* // Multiple readers can run concurrently
|
|
396
|
+
* const data = await rw.withRLock(async () => {
|
|
397
|
+
* return await db.get('config')
|
|
398
|
+
* })
|
|
399
|
+
*
|
|
400
|
+
* // Writers get exclusive access
|
|
401
|
+
* await rw.withLock(async () => {
|
|
402
|
+
* await db.set('config', newValue)
|
|
403
|
+
* })
|
|
404
|
+
*/
|
|
405
|
+
declare class RWMutex {
|
|
406
|
+
private readers;
|
|
407
|
+
private writing;
|
|
408
|
+
private readQueue;
|
|
409
|
+
private writeQueue;
|
|
410
|
+
rLock(): Promise<void>;
|
|
411
|
+
rUnlock(): void;
|
|
412
|
+
lock(): Promise<void>;
|
|
413
|
+
unlock(): void;
|
|
414
|
+
withRLock<T>(fn: () => T | Promise<T>): Promise<T>;
|
|
415
|
+
withLock<T>(fn: () => T | Promise<T>): Promise<T>;
|
|
416
|
+
get isLocked(): boolean;
|
|
417
|
+
private wakeReaders;
|
|
418
|
+
private wakeWriter;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Condition variable for async coordination. Modeled after Go's `sync.Cond`.
|
|
423
|
+
*
|
|
424
|
+
* A `Cond` is associated with a `Mutex`. Goroutines (async tasks) can:
|
|
425
|
+
* - `wait()` — atomically release the lock and suspend until signaled, then re-acquire the lock
|
|
426
|
+
* - `signal()` — wake one waiting task
|
|
427
|
+
* - `broadcast()` — wake all waiting tasks
|
|
428
|
+
*
|
|
429
|
+
* Always check the condition in a loop — spurious wakeups are possible if
|
|
430
|
+
* multiple waiters compete for the lock after `broadcast()`.
|
|
431
|
+
*
|
|
432
|
+
* @example
|
|
433
|
+
* const mu = new Mutex()
|
|
434
|
+
* const cond = new Cond(mu)
|
|
435
|
+
* let ready = false
|
|
436
|
+
*
|
|
437
|
+
* // Waiter
|
|
438
|
+
* await mu.lock()
|
|
439
|
+
* while (!ready) {
|
|
440
|
+
* await cond.wait()
|
|
441
|
+
* }
|
|
442
|
+
* console.log('ready!')
|
|
443
|
+
* mu.unlock()
|
|
444
|
+
*
|
|
445
|
+
* // Signaler (from another async context)
|
|
446
|
+
* await mu.lock()
|
|
447
|
+
* ready = true
|
|
448
|
+
* cond.signal()
|
|
449
|
+
* mu.unlock()
|
|
450
|
+
*
|
|
451
|
+
* @example
|
|
452
|
+
* // Broadcast to wake all waiters
|
|
453
|
+
* await mu.lock()
|
|
454
|
+
* ready = true
|
|
455
|
+
* cond.broadcast()
|
|
456
|
+
* mu.unlock()
|
|
457
|
+
*/
|
|
458
|
+
declare class Cond {
|
|
459
|
+
private mu;
|
|
460
|
+
private waiters;
|
|
461
|
+
constructor(mu: Mutex);
|
|
462
|
+
/**
|
|
463
|
+
* Atomically releases the mutex, suspends the caller until `signal()` or `broadcast()`
|
|
464
|
+
* is called, then re-acquires the mutex before returning.
|
|
465
|
+
*
|
|
466
|
+
* Must be called while holding the mutex.
|
|
467
|
+
*/
|
|
468
|
+
wait(): Promise<void>;
|
|
469
|
+
/** Wake one waiting task (if any). */
|
|
470
|
+
signal(): void;
|
|
471
|
+
/** Wake all waiting tasks. */
|
|
472
|
+
broadcast(): void;
|
|
473
|
+
}
|
|
272
474
|
|
|
273
475
|
/**
|
|
274
476
|
* Run a function exactly once, even if called concurrently.
|
|
@@ -302,7 +504,9 @@ declare class Once<T = void> {
|
|
|
302
504
|
reset(): void;
|
|
303
505
|
}
|
|
304
506
|
|
|
305
|
-
type
|
|
507
|
+
type RecvCase<T = StructuredCloneValue> = [Promise<T>, (value: T) => void];
|
|
508
|
+
type SendCase = [Promise<void>, () => void];
|
|
509
|
+
type SelectCase<T = StructuredCloneValue> = RecvCase<T> | SendCase;
|
|
306
510
|
/**
|
|
307
511
|
* Options for `select()`.
|
|
308
512
|
*
|
|
@@ -318,10 +522,13 @@ interface SelectOptions {
|
|
|
318
522
|
* Each case is a `[promise, handler]` tuple. The handler for the first settled
|
|
319
523
|
* promise is called with its value. All other handlers are ignored.
|
|
320
524
|
*
|
|
525
|
+
* **Recv cases:** `[ch.recv(), (value) => ...]` — handler receives the value.
|
|
526
|
+
* **Send cases:** `[ch.send(value), () => ...]` — handler is called when the send completes.
|
|
527
|
+
*
|
|
321
528
|
* If `opts.default` is provided, `select` becomes non-blocking: if no promise
|
|
322
529
|
* is already resolved, the default runs immediately (Go's `select { default: ... }`).
|
|
323
530
|
*
|
|
324
|
-
* Commonly used with `ch.recv()`, `after()`, and `spawn().result`.
|
|
531
|
+
* Commonly used with `ch.recv()`, `ch.send()`, `after()`, and `spawn().result`.
|
|
325
532
|
*
|
|
326
533
|
* @example
|
|
327
534
|
* // Block until a channel message arrives or timeout after 5s
|
|
@@ -338,6 +545,13 @@ interface SelectOptions {
|
|
|
338
545
|
* )
|
|
339
546
|
*
|
|
340
547
|
* @example
|
|
548
|
+
* // Select with send case — try to send or timeout
|
|
549
|
+
* await select([
|
|
550
|
+
* [ch.send(value), () => console.log('sent!')],
|
|
551
|
+
* [after(1000), () => console.log('send timed out')],
|
|
552
|
+
* ])
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
341
555
|
* // Race two worker results against a deadline
|
|
342
556
|
* const { result: fast } = spawn(() => quickSearch(query))
|
|
343
557
|
* const { result: deep } = spawn(() => deepSearch(query))
|
|
@@ -417,6 +631,54 @@ declare class Ticker {
|
|
|
417
631
|
*/
|
|
418
632
|
declare function ticker(ms: number): Ticker;
|
|
419
633
|
|
|
634
|
+
/**
|
|
635
|
+
* A one-shot timer that can be stopped and reset. Like Go's `time.Timer`.
|
|
636
|
+
*
|
|
637
|
+
* Unlike `after()` which is fire-and-forget, `Timer` gives you control:
|
|
638
|
+
* - `stop()` cancels a pending timer
|
|
639
|
+
* - `reset(ms)` reschedules without allocating a new object
|
|
640
|
+
*
|
|
641
|
+
* The `channel` property is a promise that resolves when the timer fires.
|
|
642
|
+
* After `stop()`, the promise never resolves. After `reset()`, a new `channel`
|
|
643
|
+
* promise is created.
|
|
644
|
+
*
|
|
645
|
+
* @example
|
|
646
|
+
* // Basic timeout with ability to cancel
|
|
647
|
+
* const t = new Timer(5000)
|
|
648
|
+
* await select([
|
|
649
|
+
* [ch.recv(), (v) => { t.stop(); handle(v) }],
|
|
650
|
+
* [t.channel, () => console.log('timed out')],
|
|
651
|
+
* ])
|
|
652
|
+
*
|
|
653
|
+
* @example
|
|
654
|
+
* // Reset a timer (e.g., debounce pattern)
|
|
655
|
+
* const t = new Timer(300)
|
|
656
|
+
* onInput(() => t.reset(300))
|
|
657
|
+
* await t.channel // fires 300ms after last input
|
|
658
|
+
*/
|
|
659
|
+
declare class Timer {
|
|
660
|
+
private timer;
|
|
661
|
+
private _stopped;
|
|
662
|
+
/** Promise that resolves when the timer fires. Replaced on `reset()`. */
|
|
663
|
+
channel: Promise<void>;
|
|
664
|
+
constructor(ms: number);
|
|
665
|
+
private schedule;
|
|
666
|
+
/**
|
|
667
|
+
* Stop the timer. Returns `true` if the timer was pending (stopped before firing),
|
|
668
|
+
* `false` if it had already fired or was already stopped.
|
|
669
|
+
*
|
|
670
|
+
* After stopping, the current `channel` promise will never resolve.
|
|
671
|
+
*/
|
|
672
|
+
stop(): boolean;
|
|
673
|
+
/**
|
|
674
|
+
* Reset the timer to fire after `ms` milliseconds.
|
|
675
|
+
* If the timer was pending, it is stopped first. Creates a new `channel` promise.
|
|
676
|
+
*/
|
|
677
|
+
reset(ms: number): void;
|
|
678
|
+
/** Whether the timer has fired or been stopped. */
|
|
679
|
+
get stopped(): boolean;
|
|
680
|
+
}
|
|
681
|
+
|
|
420
682
|
/**
|
|
421
683
|
* Define a reusable task that runs in a worker thread.
|
|
422
684
|
*
|
|
@@ -506,4 +768,4 @@ type Capability = 'full-threads' | 'single-thread';
|
|
|
506
768
|
declare function detectRuntime(): Runtime;
|
|
507
769
|
declare function detectCapability(): Capability;
|
|
508
770
|
|
|
509
|
-
export { type Capability, type Channel, ErrGroup, Mutex, Once, type PoolStats, type PuruConfig, type Runtime, type SelectOptions, type SpawnResult, Ticker, WaitGroup, after, chan, configure, detectCapability, detectRuntime, resize, select, shutdown, spawn, stats, task, ticker };
|
|
771
|
+
export { type CancelFunc, CancelledError, type Capability, type Channel, Cond, type Context, type ContextError, DeadlineExceededError, ErrGroup, Mutex, Once, type PoolStats, type PuruConfig, RWMutex, type RecvOnly, type Runtime, type SelectOptions, type SendOnly, type SpawnResult, Ticker, Timer, WaitGroup, after, background, chan, configure, detectCapability, detectRuntime, resize, select, shutdown, spawn, stats, task, ticker, withCancel, withDeadline, withTimeout, withValue };
|
package/dist/index.d.ts
CHANGED
|
@@ -44,6 +44,28 @@ interface Channel<T extends ChannelValue> {
|
|
|
44
44
|
/** Resolves with the next value, or `null` if the channel is closed. */
|
|
45
45
|
recv(): Promise<T | null>;
|
|
46
46
|
close(): void;
|
|
47
|
+
/** Number of values currently buffered. Like Go's `len(ch)`. */
|
|
48
|
+
readonly len: number;
|
|
49
|
+
/** Buffer capacity. Like Go's `cap(ch)`. */
|
|
50
|
+
readonly cap: number;
|
|
51
|
+
/** Returns a send-only view of this channel. Like Go's `chan<- T`. */
|
|
52
|
+
sendOnly(): SendOnly<T>;
|
|
53
|
+
/** Returns a receive-only view of this channel. Like Go's `<-chan T`. */
|
|
54
|
+
recvOnly(): RecvOnly<T>;
|
|
55
|
+
[Symbol.asyncIterator](): AsyncIterator<T>;
|
|
56
|
+
}
|
|
57
|
+
/** Send-only view of a channel. Like Go's `chan<- T`. */
|
|
58
|
+
interface SendOnly<T extends ChannelValue> {
|
|
59
|
+
send(value: T): Promise<void>;
|
|
60
|
+
close(): void;
|
|
61
|
+
readonly len: number;
|
|
62
|
+
readonly cap: number;
|
|
63
|
+
}
|
|
64
|
+
/** Receive-only view of a channel. Like Go's `<-chan T`. */
|
|
65
|
+
interface RecvOnly<T extends ChannelValue> {
|
|
66
|
+
recv(): Promise<T | null>;
|
|
67
|
+
readonly len: number;
|
|
68
|
+
readonly cap: number;
|
|
47
69
|
[Symbol.asyncIterator](): AsyncIterator<T>;
|
|
48
70
|
}
|
|
49
71
|
/**
|
|
@@ -76,6 +98,79 @@ interface Channel<T extends ChannelValue> {
|
|
|
76
98
|
*/
|
|
77
99
|
declare function chan<T extends ChannelValue>(capacity?: number): Channel<T>;
|
|
78
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Hierarchical cancellation, deadlines, and request-scoped values — modeled after Go's `context` package.
|
|
103
|
+
*
|
|
104
|
+
* Context is the glue that makes cancellation and timeouts composable. Derive child
|
|
105
|
+
* contexts from a parent: when the parent is cancelled, all children cancel automatically.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* // Timeout a group of tasks
|
|
109
|
+
* const [ctx, cancel] = withTimeout(background(), 5000)
|
|
110
|
+
* const wg = new WaitGroup()
|
|
111
|
+
* wg.spawn(() => longRunningWork())
|
|
112
|
+
* ctx.done().then(() => wg.cancel())
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* // Nested deadlines
|
|
116
|
+
* const [parent, cancelParent] = withCancel(background())
|
|
117
|
+
* const [child, _] = withTimeout(parent, 1000)
|
|
118
|
+
* // child cancels after 1s OR when cancelParent() is called — whichever comes first
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* // Request-scoped values
|
|
122
|
+
* const reqCtx = withValue(background(), 'requestId', 'abc-123')
|
|
123
|
+
* reqCtx.value('requestId') // 'abc-123'
|
|
124
|
+
*/
|
|
125
|
+
/** Returned by `ctx.err` when the context was explicitly cancelled. */
|
|
126
|
+
declare class CancelledError extends Error {
|
|
127
|
+
constructor(message?: string);
|
|
128
|
+
}
|
|
129
|
+
/** Returned by `ctx.err` when the context's deadline has passed. */
|
|
130
|
+
declare class DeadlineExceededError extends Error {
|
|
131
|
+
constructor();
|
|
132
|
+
}
|
|
133
|
+
type ContextError = CancelledError | DeadlineExceededError;
|
|
134
|
+
interface Context {
|
|
135
|
+
/** AbortSignal that fires when this context is cancelled or its deadline expires. */
|
|
136
|
+
readonly signal: AbortSignal;
|
|
137
|
+
/** The deadline for this context, or `null` if none was set. */
|
|
138
|
+
readonly deadline: Date | null;
|
|
139
|
+
/** The cancellation error, or `null` if the context is still active. */
|
|
140
|
+
readonly err: ContextError | null;
|
|
141
|
+
/** Retrieves a value stored in this context or any of its ancestors. */
|
|
142
|
+
value<T = unknown>(key: symbol | string): T | undefined;
|
|
143
|
+
/** Returns a promise that resolves when the context is cancelled. */
|
|
144
|
+
done(): Promise<void>;
|
|
145
|
+
}
|
|
146
|
+
type CancelFunc = (reason?: string) => void;
|
|
147
|
+
/**
|
|
148
|
+
* Returns the root context. It is never cancelled, has no deadline, and carries no values.
|
|
149
|
+
* All other contexts should derive from this.
|
|
150
|
+
*/
|
|
151
|
+
declare function background(): Context;
|
|
152
|
+
/**
|
|
153
|
+
* Returns a child context and a cancel function. Calling `cancel()` cancels the child
|
|
154
|
+
* and all contexts derived from it. The child also cancels when the parent does.
|
|
155
|
+
*/
|
|
156
|
+
declare function withCancel(parent: Context): [Context, CancelFunc];
|
|
157
|
+
/**
|
|
158
|
+
* Returns a child context that automatically cancels at the given `deadline`.
|
|
159
|
+
* If the parent has an earlier deadline, that deadline is inherited.
|
|
160
|
+
* The returned cancel function can cancel early and clears the timer.
|
|
161
|
+
*/
|
|
162
|
+
declare function withDeadline(parent: Context, deadline: Date): [Context, CancelFunc];
|
|
163
|
+
/**
|
|
164
|
+
* Returns a child context that automatically cancels after `ms` milliseconds.
|
|
165
|
+
* Equivalent to `withDeadline(parent, new Date(Date.now() + ms))`.
|
|
166
|
+
*/
|
|
167
|
+
declare function withTimeout(parent: Context, ms: number): [Context, CancelFunc];
|
|
168
|
+
/**
|
|
169
|
+
* Returns a child context carrying a key-value pair.
|
|
170
|
+
* Values are retrieved with `ctx.value(key)` and looked up through the ancestor chain.
|
|
171
|
+
*/
|
|
172
|
+
declare function withValue<T = unknown>(parent: Context, key: symbol | string, value: T): Context;
|
|
173
|
+
|
|
79
174
|
/**
|
|
80
175
|
* Run a function in a worker thread. Returns a handle with the result promise and a cancel function.
|
|
81
176
|
*
|
|
@@ -121,6 +216,7 @@ declare function spawn<T extends StructuredCloneValue, TChannels extends Record<
|
|
|
121
216
|
priority?: 'low' | 'normal' | 'high';
|
|
122
217
|
concurrent?: boolean;
|
|
123
218
|
channels?: TChannels;
|
|
219
|
+
ctx?: Context;
|
|
124
220
|
}): SpawnResult<T>;
|
|
125
221
|
|
|
126
222
|
type SpawnChannels$1 = Record<string, Channel<ChannelValue>>;
|
|
@@ -157,6 +253,8 @@ type SpawnChannels$1 = Record<string, Channel<ChannelValue>>;
|
|
|
157
253
|
declare class WaitGroup<T extends StructuredCloneValue = StructuredCloneValue> {
|
|
158
254
|
private tasks;
|
|
159
255
|
private controller;
|
|
256
|
+
private ctx?;
|
|
257
|
+
constructor(ctx?: Context);
|
|
160
258
|
/**
|
|
161
259
|
* An `AbortSignal` shared across all tasks in this group.
|
|
162
260
|
* Pass it into spawned functions so they can stop early when `cancel()` is called.
|
|
@@ -223,11 +321,23 @@ declare class ErrGroup<T extends StructuredCloneValue = StructuredCloneValue> {
|
|
|
223
321
|
private controller;
|
|
224
322
|
private firstError;
|
|
225
323
|
private hasError;
|
|
324
|
+
private ctx?;
|
|
325
|
+
private limit;
|
|
326
|
+
private inFlight;
|
|
327
|
+
private waiting;
|
|
328
|
+
constructor(ctx?: Context);
|
|
226
329
|
get signal(): AbortSignal;
|
|
330
|
+
/**
|
|
331
|
+
* Set the maximum number of tasks that can run concurrently.
|
|
332
|
+
* Like Go's `errgroup.SetLimit()`. Must be called before any `spawn()`.
|
|
333
|
+
* A value of 0 (default) means unlimited.
|
|
334
|
+
*/
|
|
335
|
+
setLimit(n: number): void;
|
|
227
336
|
spawn<TChannels extends SpawnChannels = Record<never, never>>(fn: (() => T | Promise<T>) | ((channels: TChannels) => T | Promise<T>), opts?: {
|
|
228
337
|
concurrent?: boolean;
|
|
229
338
|
channels?: TChannels;
|
|
230
339
|
}): void;
|
|
340
|
+
private doSpawn;
|
|
231
341
|
wait(): Promise<T[]>;
|
|
232
342
|
cancel(): void;
|
|
233
343
|
}
|
|
@@ -269,6 +379,98 @@ declare class Mutex {
|
|
|
269
379
|
withLock<T>(fn: () => T | Promise<T>): Promise<T>;
|
|
270
380
|
get isLocked(): boolean;
|
|
271
381
|
}
|
|
382
|
+
/**
|
|
383
|
+
* Async read-write mutex. Multiple readers can hold the lock simultaneously,
|
|
384
|
+
* but writers get exclusive access. Modeled after Go's `sync.RWMutex`.
|
|
385
|
+
*
|
|
386
|
+
* Use this instead of `Mutex` when reads vastly outnumber writes — concurrent
|
|
387
|
+
* readers improve throughput without sacrificing write safety.
|
|
388
|
+
*
|
|
389
|
+
* Like `Mutex`, this operates on the main thread only. For cross-thread
|
|
390
|
+
* coordination, use channels instead.
|
|
391
|
+
*
|
|
392
|
+
* @example
|
|
393
|
+
* const rw = new RWMutex()
|
|
394
|
+
*
|
|
395
|
+
* // Multiple readers can run concurrently
|
|
396
|
+
* const data = await rw.withRLock(async () => {
|
|
397
|
+
* return await db.get('config')
|
|
398
|
+
* })
|
|
399
|
+
*
|
|
400
|
+
* // Writers get exclusive access
|
|
401
|
+
* await rw.withLock(async () => {
|
|
402
|
+
* await db.set('config', newValue)
|
|
403
|
+
* })
|
|
404
|
+
*/
|
|
405
|
+
declare class RWMutex {
|
|
406
|
+
private readers;
|
|
407
|
+
private writing;
|
|
408
|
+
private readQueue;
|
|
409
|
+
private writeQueue;
|
|
410
|
+
rLock(): Promise<void>;
|
|
411
|
+
rUnlock(): void;
|
|
412
|
+
lock(): Promise<void>;
|
|
413
|
+
unlock(): void;
|
|
414
|
+
withRLock<T>(fn: () => T | Promise<T>): Promise<T>;
|
|
415
|
+
withLock<T>(fn: () => T | Promise<T>): Promise<T>;
|
|
416
|
+
get isLocked(): boolean;
|
|
417
|
+
private wakeReaders;
|
|
418
|
+
private wakeWriter;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Condition variable for async coordination. Modeled after Go's `sync.Cond`.
|
|
423
|
+
*
|
|
424
|
+
* A `Cond` is associated with a `Mutex`. Goroutines (async tasks) can:
|
|
425
|
+
* - `wait()` — atomically release the lock and suspend until signaled, then re-acquire the lock
|
|
426
|
+
* - `signal()` — wake one waiting task
|
|
427
|
+
* - `broadcast()` — wake all waiting tasks
|
|
428
|
+
*
|
|
429
|
+
* Always check the condition in a loop — spurious wakeups are possible if
|
|
430
|
+
* multiple waiters compete for the lock after `broadcast()`.
|
|
431
|
+
*
|
|
432
|
+
* @example
|
|
433
|
+
* const mu = new Mutex()
|
|
434
|
+
* const cond = new Cond(mu)
|
|
435
|
+
* let ready = false
|
|
436
|
+
*
|
|
437
|
+
* // Waiter
|
|
438
|
+
* await mu.lock()
|
|
439
|
+
* while (!ready) {
|
|
440
|
+
* await cond.wait()
|
|
441
|
+
* }
|
|
442
|
+
* console.log('ready!')
|
|
443
|
+
* mu.unlock()
|
|
444
|
+
*
|
|
445
|
+
* // Signaler (from another async context)
|
|
446
|
+
* await mu.lock()
|
|
447
|
+
* ready = true
|
|
448
|
+
* cond.signal()
|
|
449
|
+
* mu.unlock()
|
|
450
|
+
*
|
|
451
|
+
* @example
|
|
452
|
+
* // Broadcast to wake all waiters
|
|
453
|
+
* await mu.lock()
|
|
454
|
+
* ready = true
|
|
455
|
+
* cond.broadcast()
|
|
456
|
+
* mu.unlock()
|
|
457
|
+
*/
|
|
458
|
+
declare class Cond {
|
|
459
|
+
private mu;
|
|
460
|
+
private waiters;
|
|
461
|
+
constructor(mu: Mutex);
|
|
462
|
+
/**
|
|
463
|
+
* Atomically releases the mutex, suspends the caller until `signal()` or `broadcast()`
|
|
464
|
+
* is called, then re-acquires the mutex before returning.
|
|
465
|
+
*
|
|
466
|
+
* Must be called while holding the mutex.
|
|
467
|
+
*/
|
|
468
|
+
wait(): Promise<void>;
|
|
469
|
+
/** Wake one waiting task (if any). */
|
|
470
|
+
signal(): void;
|
|
471
|
+
/** Wake all waiting tasks. */
|
|
472
|
+
broadcast(): void;
|
|
473
|
+
}
|
|
272
474
|
|
|
273
475
|
/**
|
|
274
476
|
* Run a function exactly once, even if called concurrently.
|
|
@@ -302,7 +504,9 @@ declare class Once<T = void> {
|
|
|
302
504
|
reset(): void;
|
|
303
505
|
}
|
|
304
506
|
|
|
305
|
-
type
|
|
507
|
+
type RecvCase<T = StructuredCloneValue> = [Promise<T>, (value: T) => void];
|
|
508
|
+
type SendCase = [Promise<void>, () => void];
|
|
509
|
+
type SelectCase<T = StructuredCloneValue> = RecvCase<T> | SendCase;
|
|
306
510
|
/**
|
|
307
511
|
* Options for `select()`.
|
|
308
512
|
*
|
|
@@ -318,10 +522,13 @@ interface SelectOptions {
|
|
|
318
522
|
* Each case is a `[promise, handler]` tuple. The handler for the first settled
|
|
319
523
|
* promise is called with its value. All other handlers are ignored.
|
|
320
524
|
*
|
|
525
|
+
* **Recv cases:** `[ch.recv(), (value) => ...]` — handler receives the value.
|
|
526
|
+
* **Send cases:** `[ch.send(value), () => ...]` — handler is called when the send completes.
|
|
527
|
+
*
|
|
321
528
|
* If `opts.default` is provided, `select` becomes non-blocking: if no promise
|
|
322
529
|
* is already resolved, the default runs immediately (Go's `select { default: ... }`).
|
|
323
530
|
*
|
|
324
|
-
* Commonly used with `ch.recv()`, `after()`, and `spawn().result`.
|
|
531
|
+
* Commonly used with `ch.recv()`, `ch.send()`, `after()`, and `spawn().result`.
|
|
325
532
|
*
|
|
326
533
|
* @example
|
|
327
534
|
* // Block until a channel message arrives or timeout after 5s
|
|
@@ -338,6 +545,13 @@ interface SelectOptions {
|
|
|
338
545
|
* )
|
|
339
546
|
*
|
|
340
547
|
* @example
|
|
548
|
+
* // Select with send case — try to send or timeout
|
|
549
|
+
* await select([
|
|
550
|
+
* [ch.send(value), () => console.log('sent!')],
|
|
551
|
+
* [after(1000), () => console.log('send timed out')],
|
|
552
|
+
* ])
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
341
555
|
* // Race two worker results against a deadline
|
|
342
556
|
* const { result: fast } = spawn(() => quickSearch(query))
|
|
343
557
|
* const { result: deep } = spawn(() => deepSearch(query))
|
|
@@ -417,6 +631,54 @@ declare class Ticker {
|
|
|
417
631
|
*/
|
|
418
632
|
declare function ticker(ms: number): Ticker;
|
|
419
633
|
|
|
634
|
+
/**
|
|
635
|
+
* A one-shot timer that can be stopped and reset. Like Go's `time.Timer`.
|
|
636
|
+
*
|
|
637
|
+
* Unlike `after()` which is fire-and-forget, `Timer` gives you control:
|
|
638
|
+
* - `stop()` cancels a pending timer
|
|
639
|
+
* - `reset(ms)` reschedules without allocating a new object
|
|
640
|
+
*
|
|
641
|
+
* The `channel` property is a promise that resolves when the timer fires.
|
|
642
|
+
* After `stop()`, the promise never resolves. After `reset()`, a new `channel`
|
|
643
|
+
* promise is created.
|
|
644
|
+
*
|
|
645
|
+
* @example
|
|
646
|
+
* // Basic timeout with ability to cancel
|
|
647
|
+
* const t = new Timer(5000)
|
|
648
|
+
* await select([
|
|
649
|
+
* [ch.recv(), (v) => { t.stop(); handle(v) }],
|
|
650
|
+
* [t.channel, () => console.log('timed out')],
|
|
651
|
+
* ])
|
|
652
|
+
*
|
|
653
|
+
* @example
|
|
654
|
+
* // Reset a timer (e.g., debounce pattern)
|
|
655
|
+
* const t = new Timer(300)
|
|
656
|
+
* onInput(() => t.reset(300))
|
|
657
|
+
* await t.channel // fires 300ms after last input
|
|
658
|
+
*/
|
|
659
|
+
declare class Timer {
|
|
660
|
+
private timer;
|
|
661
|
+
private _stopped;
|
|
662
|
+
/** Promise that resolves when the timer fires. Replaced on `reset()`. */
|
|
663
|
+
channel: Promise<void>;
|
|
664
|
+
constructor(ms: number);
|
|
665
|
+
private schedule;
|
|
666
|
+
/**
|
|
667
|
+
* Stop the timer. Returns `true` if the timer was pending (stopped before firing),
|
|
668
|
+
* `false` if it had already fired or was already stopped.
|
|
669
|
+
*
|
|
670
|
+
* After stopping, the current `channel` promise will never resolve.
|
|
671
|
+
*/
|
|
672
|
+
stop(): boolean;
|
|
673
|
+
/**
|
|
674
|
+
* Reset the timer to fire after `ms` milliseconds.
|
|
675
|
+
* If the timer was pending, it is stopped first. Creates a new `channel` promise.
|
|
676
|
+
*/
|
|
677
|
+
reset(ms: number): void;
|
|
678
|
+
/** Whether the timer has fired or been stopped. */
|
|
679
|
+
get stopped(): boolean;
|
|
680
|
+
}
|
|
681
|
+
|
|
420
682
|
/**
|
|
421
683
|
* Define a reusable task that runs in a worker thread.
|
|
422
684
|
*
|
|
@@ -506,4 +768,4 @@ type Capability = 'full-threads' | 'single-thread';
|
|
|
506
768
|
declare function detectRuntime(): Runtime;
|
|
507
769
|
declare function detectCapability(): Capability;
|
|
508
770
|
|
|
509
|
-
export { type Capability, type Channel, ErrGroup, Mutex, Once, type PoolStats, type PuruConfig, type Runtime, type SelectOptions, type SpawnResult, Ticker, WaitGroup, after, chan, configure, detectCapability, detectRuntime, resize, select, shutdown, spawn, stats, task, ticker };
|
|
771
|
+
export { type CancelFunc, CancelledError, type Capability, type Channel, Cond, type Context, type ContextError, DeadlineExceededError, ErrGroup, Mutex, Once, type PoolStats, type PuruConfig, RWMutex, type RecvOnly, type Runtime, type SelectOptions, type SendOnly, type SpawnResult, Ticker, Timer, WaitGroup, after, background, chan, configure, detectCapability, detectRuntime, resize, select, shutdown, spawn, stats, task, ticker, withCancel, withDeadline, withTimeout, withValue };
|