@pipedog/asyncio 0.0.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +25 -0
- package/NOTICE +9 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +6 -0
- package/dist/loop.d.ts +26 -0
- package/dist/loop.js +179 -0
- package/dist/primitives.d.ts +138 -0
- package/dist/primitives.js +746 -0
- package/dist/task.d.ts +114 -0
- package/dist/task.js +386 -0
- package/package.json +29 -3
- package/README.md +0 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
BSD 2-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024–2026, Daily
|
|
4
|
+
Copyright (c) 2026, pipedog contributors
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
17
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
18
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
19
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
20
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
21
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
22
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
23
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
24
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
25
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
pipedog is an unofficial TypeScript port of Pipecat (https://github.com/pipecat-ai/pipecat),
|
|
2
|
+
a framework for voice and multimodal conversational AI created by Daily.
|
|
3
|
+
|
|
4
|
+
It is not affiliated with or endorsed by Daily. Pipecat is distributed under
|
|
5
|
+
the BSD 2-Clause License, Copyright (c) 2024–2026, Daily; that copyright notice
|
|
6
|
+
and license are retained in LICENSE, and every ported source file carries a
|
|
7
|
+
header naming the upstream file it was derived from.
|
|
8
|
+
|
|
9
|
+
The upstream commit each file was ported from is recorded in porting/manifest.json.
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/dist/loop.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
/** @internal The task the code is running in (see task.ts). */
|
|
3
|
+
export declare const taskStorage: AsyncLocalStorage<unknown>;
|
|
4
|
+
/** A callback scheduled with `callSoon()` / `callLater()` (asyncio.Handle). */
|
|
5
|
+
export declare class Handle {
|
|
6
|
+
#private;
|
|
7
|
+
constructor(callback: () => void);
|
|
8
|
+
/** Cancel the callback. Does nothing if it already ran. */
|
|
9
|
+
cancel(): void;
|
|
10
|
+
cancelled(): boolean;
|
|
11
|
+
/** @internal */
|
|
12
|
+
_setTimer(timer: ReturnType<typeof setTimeout>): void;
|
|
13
|
+
/** @internal */
|
|
14
|
+
_clearTimer(): void;
|
|
15
|
+
/** @internal */
|
|
16
|
+
_run(): void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Schedule `callback` to run on a later turn of the loop, after the code
|
|
20
|
+
* running now (and every callback scheduled before it) has run up to its
|
|
21
|
+
* next suspension (loop.call_soon). Runs with no current task.
|
|
22
|
+
*/
|
|
23
|
+
export declare function callSoon(callback: () => void): Handle;
|
|
24
|
+
/** Schedule `callback` after `delay` seconds (loop.call_later). */
|
|
25
|
+
export declare function callLater(delay: number, callback: () => void): Handle;
|
|
26
|
+
//# sourceMappingURL=loop.d.ts.map
|
package/dist/loop.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// Copyright (c) 2026, pipedog contributors
|
|
2
|
+
// SPDX-License-Identifier: BSD-2-Clause
|
|
3
|
+
//
|
|
4
|
+
// The ready queue: asyncio's event loop scheduling, for JavaScript.
|
|
5
|
+
//
|
|
6
|
+
// In asyncio a task runs until it genuinely suspends (awaits a future that is
|
|
7
|
+
// not done). Completing a future does not resume its waiters: their wakeups
|
|
8
|
+
// are queued with `loop.call_soon()` and run later, one after another, in FIFO
|
|
9
|
+
// order. So `await queue.get()` on a non-empty queue, `await queue.put()` on
|
|
10
|
+
// an unbounded one or awaiting a coroutine that never suspends all complete
|
|
11
|
+
// without letting any other task run.
|
|
12
|
+
//
|
|
13
|
+
// JavaScript resumes an awaiting function one microtask after its promise
|
|
14
|
+
// settles, so naive promise-based primitives interleave tasks at every
|
|
15
|
+
// `await`. Here, every wakeup (a future's done callbacks, a task's first step,
|
|
16
|
+
// `sleep(0)`, timers) is a `callSoon()` callback instead, and callbacks run
|
|
17
|
+
// one per macrotask: the microtask queue (the resumed task's run up to its next
|
|
18
|
+
// real suspension, plus anything it set off) drains completely before the
|
|
19
|
+
// next callback runs. Pending callbacks keep the process alive, like a running
|
|
20
|
+
// loop.
|
|
21
|
+
//
|
|
22
|
+
// The macrotask source is picked per runtime: a MessageChannel on Node
|
|
23
|
+
// (setImmediate there goes through a full libuv loop iteration per callback),
|
|
24
|
+
// setImmediate on Bun and Deno, a body read on Cloudflare Workers (its
|
|
25
|
+
// setImmediate and timers wait about a millisecond; MessageChannel messages
|
|
26
|
+
// are delivered without draining microtasks in between) and MessageChannel or
|
|
27
|
+
// setTimeout elsewhere.
|
|
28
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
29
|
+
/** @internal The task the code is running in (see task.ts). */
|
|
30
|
+
export const taskStorage = new AsyncLocalStorage();
|
|
31
|
+
/** A callback scheduled with `callSoon()` / `callLater()` (asyncio.Handle). */
|
|
32
|
+
export class Handle {
|
|
33
|
+
#callback;
|
|
34
|
+
#timer;
|
|
35
|
+
constructor(callback) {
|
|
36
|
+
this.#callback = callback;
|
|
37
|
+
}
|
|
38
|
+
/** Cancel the callback. Does nothing if it already ran. */
|
|
39
|
+
cancel() {
|
|
40
|
+
this.#callback = null;
|
|
41
|
+
if (this.#timer !== undefined) {
|
|
42
|
+
clearTimeout(this.#timer);
|
|
43
|
+
this.#timer = undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
cancelled() {
|
|
47
|
+
return this.#callback === null;
|
|
48
|
+
}
|
|
49
|
+
/** @internal */
|
|
50
|
+
_setTimer(timer) {
|
|
51
|
+
this.#timer = timer;
|
|
52
|
+
}
|
|
53
|
+
/** @internal */
|
|
54
|
+
_clearTimer() {
|
|
55
|
+
this.#timer = undefined;
|
|
56
|
+
}
|
|
57
|
+
/** @internal */
|
|
58
|
+
_run() {
|
|
59
|
+
const callback = this.#callback;
|
|
60
|
+
if (callback === null)
|
|
61
|
+
return;
|
|
62
|
+
this.#callback = null;
|
|
63
|
+
callback();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// FIFO of ready handles: an array consumed from `head`, compacted lazily.
|
|
67
|
+
let ready = [];
|
|
68
|
+
let head = 0;
|
|
69
|
+
let trigger;
|
|
70
|
+
let armed = false;
|
|
71
|
+
/**
|
|
72
|
+
* Schedule `callback` to run on a later turn of the loop, after the code
|
|
73
|
+
* running now (and every callback scheduled before it) has run up to its
|
|
74
|
+
* next suspension (loop.call_soon). Runs with no current task.
|
|
75
|
+
*/
|
|
76
|
+
export function callSoon(callback) {
|
|
77
|
+
const handle = new Handle(callback);
|
|
78
|
+
enqueue(handle);
|
|
79
|
+
return handle;
|
|
80
|
+
}
|
|
81
|
+
/** Schedule `callback` after `delay` seconds (loop.call_later). */
|
|
82
|
+
export function callLater(delay, callback) {
|
|
83
|
+
const handle = new Handle(callback);
|
|
84
|
+
// As in asyncio, a due timer only moves its callback to the ready queue.
|
|
85
|
+
handle._setTimer(setTimeout(() => {
|
|
86
|
+
handle._clearTimer();
|
|
87
|
+
if (!handle.cancelled())
|
|
88
|
+
enqueue(handle);
|
|
89
|
+
}, Math.max(0, delay * 1000)));
|
|
90
|
+
return handle;
|
|
91
|
+
}
|
|
92
|
+
function enqueue(handle) {
|
|
93
|
+
ready.push(handle);
|
|
94
|
+
if (!armed) {
|
|
95
|
+
armed = true;
|
|
96
|
+
trigger ??= makeTrigger(runOne);
|
|
97
|
+
trigger.arm();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** One loop turn: run the oldest ready callback, then let microtasks drain. */
|
|
101
|
+
function runOne() {
|
|
102
|
+
armed = false;
|
|
103
|
+
const handle = ready[head];
|
|
104
|
+
if (handle === undefined) {
|
|
105
|
+
trigger?.idle();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
ready[head] = undefined;
|
|
109
|
+
head++;
|
|
110
|
+
if (head === ready.length) {
|
|
111
|
+
ready = [];
|
|
112
|
+
head = 0;
|
|
113
|
+
}
|
|
114
|
+
else if (head >= 1024 && head * 2 >= ready.length) {
|
|
115
|
+
ready = ready.slice(head);
|
|
116
|
+
head = 0;
|
|
117
|
+
}
|
|
118
|
+
if (head < ready.length) {
|
|
119
|
+
armed = true;
|
|
120
|
+
trigger.arm();
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
trigger.idle();
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
// Callbacks run outside any task, as in asyncio (current_task() is None).
|
|
127
|
+
taskStorage.run(undefined, () => handle._run());
|
|
128
|
+
}
|
|
129
|
+
catch (e) {
|
|
130
|
+
// asyncio logs callback errors and keeps the loop going; surface it the
|
|
131
|
+
// way JavaScript reports an uncaught error, without stopping the queue.
|
|
132
|
+
queueMicrotask(() => {
|
|
133
|
+
throw e;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function makeTrigger(run) {
|
|
138
|
+
const g = globalThis;
|
|
139
|
+
const noop = () => { };
|
|
140
|
+
if (g.navigator?.userAgent === "Cloudflare-Workers" && typeof g.Response === "function") {
|
|
141
|
+
// A body read settles from the runtime's event loop, after microtasks.
|
|
142
|
+
const Response = g.Response;
|
|
143
|
+
return { arm: () => void new Response("").arrayBuffer().then(run), idle: noop };
|
|
144
|
+
}
|
|
145
|
+
const isNode = typeof g.process?.versions?.node === "string" && !g.Bun && !g.Deno;
|
|
146
|
+
if (isNode && typeof g.MessageChannel === "function") {
|
|
147
|
+
const { port1, port2 } = new g.MessageChannel();
|
|
148
|
+
port1.onmessage = run;
|
|
149
|
+
// Only pending callbacks keep the process alive.
|
|
150
|
+
port1.unref?.();
|
|
151
|
+
let referenced = false;
|
|
152
|
+
return {
|
|
153
|
+
arm: () => {
|
|
154
|
+
if (!referenced) {
|
|
155
|
+
referenced = true;
|
|
156
|
+
port1.ref?.();
|
|
157
|
+
}
|
|
158
|
+
port2.postMessage(null);
|
|
159
|
+
},
|
|
160
|
+
idle: () => {
|
|
161
|
+
if (referenced) {
|
|
162
|
+
referenced = false;
|
|
163
|
+
port1.unref?.();
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
if (typeof g.setImmediate === "function") {
|
|
169
|
+
const setImmediate = g.setImmediate;
|
|
170
|
+
return { arm: () => void setImmediate(run), idle: noop };
|
|
171
|
+
}
|
|
172
|
+
if (typeof g.MessageChannel === "function") {
|
|
173
|
+
const { port1, port2 } = new g.MessageChannel();
|
|
174
|
+
port1.onmessage = run;
|
|
175
|
+
return { arm: () => port2.postMessage(null), idle: noop };
|
|
176
|
+
}
|
|
177
|
+
return { arm: () => void setTimeout(run, 0), idle: noop };
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=loop.js.map
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { Future, type TaskFn } from "./task.ts";
|
|
2
|
+
/** Monotonic time in seconds (loop.time()). */
|
|
3
|
+
export declare function monotonic(): number;
|
|
4
|
+
/** Monotonic time in nanoseconds (time.monotonic_ns()). */
|
|
5
|
+
export declare function monotonicNs(): number;
|
|
6
|
+
/**
|
|
7
|
+
* Wall-clock time in seconds (time.time()).
|
|
8
|
+
*
|
|
9
|
+
* Sub-millisecond, like Python's: `Date.now()` alone would give successive
|
|
10
|
+
* readings the same value, so intervals between frames pushed back to back
|
|
11
|
+
* would measure as zero. Falls back to `Date.now()` where the runtime has no
|
|
12
|
+
* time origin.
|
|
13
|
+
*/
|
|
14
|
+
export declare function time(): number;
|
|
15
|
+
/**
|
|
16
|
+
* asyncio.sleep(seconds). `sleep(0)` yields once: the task goes to the back
|
|
17
|
+
* of the ready queue, so everything already scheduled runs first.
|
|
18
|
+
*/
|
|
19
|
+
export declare function sleep(seconds: number): Promise<void>;
|
|
20
|
+
/** asyncio.Event */
|
|
21
|
+
export declare class Event {
|
|
22
|
+
#private;
|
|
23
|
+
isSet(): boolean;
|
|
24
|
+
set(): void;
|
|
25
|
+
clear(): void;
|
|
26
|
+
wait(): Promise<true>;
|
|
27
|
+
}
|
|
28
|
+
export declare class QueueEmpty extends Error {
|
|
29
|
+
name: string;
|
|
30
|
+
}
|
|
31
|
+
export declare class QueueFull extends Error {
|
|
32
|
+
name: string;
|
|
33
|
+
}
|
|
34
|
+
export declare class QueueShutDown extends Error {
|
|
35
|
+
name: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* asyncio.Queue. Subclasses override `_init/_put/_get/_size` to change
|
|
39
|
+
* ordering, as Python's PriorityQueue and LifoQueue do.
|
|
40
|
+
*/
|
|
41
|
+
export declare class Queue<T> {
|
|
42
|
+
#private;
|
|
43
|
+
readonly maxsize: number;
|
|
44
|
+
constructor(maxsize?: number);
|
|
45
|
+
protected _items: T[];
|
|
46
|
+
protected _head: number;
|
|
47
|
+
protected _init(): void;
|
|
48
|
+
protected _put(item: T): void;
|
|
49
|
+
protected _get(): T;
|
|
50
|
+
protected _size(): number;
|
|
51
|
+
/** The queued items in storage order (for subclasses that inspect them). */
|
|
52
|
+
protected _queued(): readonly T[];
|
|
53
|
+
qsize(): number;
|
|
54
|
+
empty(): boolean;
|
|
55
|
+
full(): boolean;
|
|
56
|
+
put(item: T): Promise<void>;
|
|
57
|
+
putNowait(item: T): void;
|
|
58
|
+
get(): Promise<T>;
|
|
59
|
+
getNowait(): T;
|
|
60
|
+
taskDone(): void;
|
|
61
|
+
join(): Promise<void>;
|
|
62
|
+
shutdown(immediate?: boolean): void;
|
|
63
|
+
}
|
|
64
|
+
/** asyncio.LifoQueue */
|
|
65
|
+
export declare class LifoQueue<T> extends Queue<T> {
|
|
66
|
+
protected _get(): T;
|
|
67
|
+
}
|
|
68
|
+
/** Default ordering for PriorityQueue: compares numbers, strings and tuples (arrays) like Python. */
|
|
69
|
+
export declare function pythonCompare(a: unknown, b: unknown): number;
|
|
70
|
+
/** heapq.heappush */
|
|
71
|
+
export declare function heapPush<T>(heap: T[], item: T, compare?: (a: T, b: T) => number): void;
|
|
72
|
+
/** heapq.heappop */
|
|
73
|
+
export declare function heapPop<T>(heap: T[], compare?: (a: T, b: T) => number): T;
|
|
74
|
+
/** asyncio.PriorityQueue backed by a binary heap. Smallest item first. */
|
|
75
|
+
export declare class PriorityQueue<T> extends Queue<T> {
|
|
76
|
+
protected readonly _compare: (a: T, b: T) => number;
|
|
77
|
+
constructor(maxsize?: number, compare?: (a: T, b: T) => number);
|
|
78
|
+
protected _put(item: T): void;
|
|
79
|
+
protected _get(): T;
|
|
80
|
+
}
|
|
81
|
+
/** asyncio.Lock */
|
|
82
|
+
export declare class Lock {
|
|
83
|
+
#private;
|
|
84
|
+
locked(): boolean;
|
|
85
|
+
acquire(): Promise<true>;
|
|
86
|
+
release(): void;
|
|
87
|
+
/** `async with lock:` → `await lock.run(async () => {...})` */
|
|
88
|
+
run<R>(fn: () => Promise<R> | R): Promise<R>;
|
|
89
|
+
}
|
|
90
|
+
/** asyncio.Semaphore */
|
|
91
|
+
export declare class Semaphore {
|
|
92
|
+
#private;
|
|
93
|
+
constructor(value?: number);
|
|
94
|
+
/** Due to state, or FIFO rules (must allow others to run first). */
|
|
95
|
+
locked(): boolean;
|
|
96
|
+
acquire(): Promise<true>;
|
|
97
|
+
release(): void;
|
|
98
|
+
run<R>(fn: () => Promise<R> | R): Promise<R>;
|
|
99
|
+
}
|
|
100
|
+
/** Something to await: a function (a "coroutine"), a Task or Future, or a foreign promise. */
|
|
101
|
+
export type Awaitable<T> = TaskFn<T> | Future<T> | PromiseLike<T>;
|
|
102
|
+
/**
|
|
103
|
+
* asyncio.wait_for(aw, timeout). A function (a "coroutine") or a foreign
|
|
104
|
+
* promise is awaited in the current task, a Task is awaited (and cancelled on
|
|
105
|
+
* timeout). `timeout` null waits forever.
|
|
106
|
+
*/
|
|
107
|
+
export declare function waitFor<T>(aw: Awaitable<T>, timeout: number | null | undefined): Promise<T>;
|
|
108
|
+
/**
|
|
109
|
+
* asyncio.timeout(seconds) as a scope: runs `fn` and cancels it on timeout,
|
|
110
|
+
* raising TimeoutError.
|
|
111
|
+
*/
|
|
112
|
+
export declare function timeout<T>(seconds: number | null, fn: TaskFn<T>): Promise<T>;
|
|
113
|
+
/**
|
|
114
|
+
* asyncio.gather(*aws). Functions run as child tasks; if the caller is
|
|
115
|
+
* cancelled, all children are cancelled.
|
|
116
|
+
*/
|
|
117
|
+
export declare function gather<T extends readonly unknown[]>(...aws: {
|
|
118
|
+
[K in keyof T]: Awaitable<T[K]>;
|
|
119
|
+
}): Promise<T>;
|
|
120
|
+
/** asyncio.gather(*aws, return_exceptions=True) */
|
|
121
|
+
export declare function gatherSettled(...aws: Awaitable<unknown>[]): Promise<unknown[]>;
|
|
122
|
+
export declare const FIRST_COMPLETED = "FIRST_COMPLETED";
|
|
123
|
+
export declare const FIRST_EXCEPTION = "FIRST_EXCEPTION";
|
|
124
|
+
export declare const ALL_COMPLETED = "ALL_COMPLETED";
|
|
125
|
+
export type ReturnWhen = typeof FIRST_COMPLETED | typeof FIRST_EXCEPTION | typeof ALL_COMPLETED;
|
|
126
|
+
/**
|
|
127
|
+
* asyncio.wait(tasks, timeout, return_when). Does not cancel anything on
|
|
128
|
+
* timeout. Returns [done, pending].
|
|
129
|
+
*/
|
|
130
|
+
export declare function wait<F extends Future<any>>(tasks: Iterable<F>, options?: {
|
|
131
|
+
timeout?: number | null;
|
|
132
|
+
returnWhen?: ReturnWhen;
|
|
133
|
+
}): Promise<[Set<F>, Set<F>]>;
|
|
134
|
+
/** asyncio.shield(aw): the caller can be cancelled without cancelling `aw`. */
|
|
135
|
+
export declare function shield<T>(aw: Awaitable<T>): Promise<T>;
|
|
136
|
+
/** Whether `value` is a Future or Task (asyncio.isfuture). */
|
|
137
|
+
export declare function isFuture(value: unknown): value is Future<unknown>;
|
|
138
|
+
//# sourceMappingURL=primitives.d.ts.map
|