posipaki 0.6.4 → 0.7.1

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/dist/index.d.ts CHANGED
@@ -1,2 +1,7 @@
1
- import { a as spawn, i as ProcessFn, n as Process, o as ExitMessage, r as ProcessCtx, s as runDispatch, t as Message } from "./process-BlUe6Luh.js";
2
- export { type ExitMessage, type Message, Process, type ProcessCtx, type ProcessFn, runDispatch, spawn };
1
+ import { a as runDispatchAsync, c as ExitMessage, d as ProcessCtx, f as ProcessFn, i as AsyncProcess, l as Message, n as spawn, o as spawnAsync, p as SupervisorState, r as runDispatch, s as AsyncProcessFn, t as Process, u as PipeState } from "./process-DF-plv-v.js";
2
+
3
+ //#region src/adapters.d.ts
4
+ declare function asyncify<A, S, IM extends Message, OM extends Message>(fn: ProcessFn<A, S, IM, OM>): AsyncProcessFn<A, S, IM, OM>;
5
+ //#endregion
6
+ export { AsyncProcess, type AsyncProcessFn, type ExitMessage, type Message, type PipeState, Process, type ProcessCtx, type ProcessFn, type SupervisorState, asyncify, runDispatch, runDispatchAsync, spawn, spawnAsync };
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/adapters.ts"],"mappings":";;;iBAEgB,QAAA,kBAA0B,OAAA,aAAoB,OAAA,EAC5D,EAAA,EAAI,SAAA,CAAU,CAAA,EAAG,CAAA,EAAG,EAAA,EAAI,EAAA,IACvB,cAAA,CAAe,CAAA,EAAG,CAAA,EAAG,EAAA,EAAI,EAAA"}
package/dist/index.js CHANGED
@@ -1,86 +1,156 @@
1
- import { i as watchExit, n as makeWaiter, r as runDispatch, t as defer } from "./util-5PSJsovA.js";
2
- //#region src/process.ts
1
+ import { i as runDispatch, n as defer, r as makeWaiter, t as debugLog } from "./util-Cw64MseZ.js";
2
+ //#region src/adapters.ts
3
+ function asyncify(fn) {
4
+ return async function* (ctx, args) {
5
+ yield* fn(ctx, args);
6
+ };
7
+ }
8
+ //#endregion
9
+ //#region src/process.async.ts
3
10
  /**
4
- * Spawn a new process from a process function. Returns a curried
5
- * function: call it with initial args to start execution.
11
+ * Async equivalent of `runDispatch`. Loops, yielding `null` and feeding
12
+ * each incoming message to an `async` reducer. Exits when `readyFn()`
13
+ * returns true.
6
14
  */
7
- function spawn(fn, pname, toParent) {
8
- return (args) => {
9
- const process = new Process(fn, pname, toParent);
10
- process.start(args);
11
- return process;
12
- };
15
+ async function* runDispatchAsync(name, fn, readyFn = () => false, debugLevel = false) {
16
+ let msg;
17
+ while (!readyFn()) {
18
+ msg = yield null;
19
+ debugLog(debugLevel, "msg", name, " <- ", msg);
20
+ await fn(msg);
21
+ }
13
22
  }
14
23
  const noop = () => null;
15
24
  /**
16
- * A running process an actor with message-passing, child processes,
17
- * and observable state. Driven by a generator function that yields
18
- * state snapshots and receives messages via `yield` expressions.
25
+ * A process driven by an async generator. Functionally identical to
26
+ * {@link Process} but supports `await` inside reducers.
27
+ *
28
+ * Messages are processed **one at a time** — if a tick is already
29
+ * in-flight, new messages are buffered and processed when the current
30
+ * tick completes.
19
31
  */
20
- var Process = class Process {
32
+ var AsyncProcess = class AsyncProcess {
21
33
  constructor(fn, pname, toParent) {
34
+ this.current = null;
35
+ this.buffer = [];
36
+ this.nextTick = null;
37
+ this.children = [];
38
+ this.subscribers = [];
22
39
  this._isPaused = false;
40
+ this._tickInProgress = false;
41
+ this._exitReject = null;
23
42
  this.pgenerator = fn;
24
43
  this.pname = pname;
25
44
  this.toParent = toParent || noop;
26
45
  this.id = Symbol(pname);
27
- this.current = null;
28
46
  this.state = null;
29
- this.buffer = [];
30
- this.nextTick = null;
31
- this.children = [];
32
- this.subscribers = [];
33
47
  this.exitWaiter = makeWaiter();
48
+ this._ready = makeWaiter();
49
+ this._resolveReady = this._ready.resolve;
50
+ }
51
+ /** Promise that resolves once the initial state is available. */
52
+ ready() {
53
+ return this._ready.promise;
34
54
  }
35
- /** Kick off the generator with initial arguments. */
55
+ /**
56
+ * Kick off the async generator. The first `yield` sets the initial
57
+ * state; for async generators this happens in a microtask.
58
+ */
36
59
  start(arg0) {
37
60
  const ctx = {
38
61
  pname: this.pname,
39
62
  fork: this.fork.bind(this),
63
+ forkSync: this.forkSync.bind(this),
40
64
  send: this.send.bind(this),
41
65
  toParent: this.toParent
42
66
  };
43
- const task = watchExit(this)(ctx, arg0);
44
- this.current = task;
45
- let ret = task.next();
46
- this.state = ret.value || null;
47
- this._eatResult(task.next());
48
- }
49
- /** Fork a child process. Children forward messages to this process
50
- * and send `EXIT` when they terminate. */
67
+ this.current = this._watchExit(ctx, arg0);
68
+ this.current.next().then((ret) => {
69
+ this.state = ret.value ?? null;
70
+ this._resolveReady();
71
+ if (ret.done) {
72
+ this.exitWaiter.resolve();
73
+ return;
74
+ }
75
+ this._eatResult(this.current.next({ type: "__ADVANCE__" }));
76
+ });
77
+ return this;
78
+ }
79
+ /** Wrap the user's generator so EXIT/STOP logic fires on completion. */
80
+ async *_watchExit(ctx, arg0) {
81
+ try {
82
+ yield* this.pgenerator(ctx, arg0);
83
+ } finally {
84
+ this.toAllChildren({ type: "STOP" });
85
+ this.toParent({
86
+ type: "EXIT",
87
+ pid: this.id
88
+ });
89
+ }
90
+ }
51
91
  fork(fn, pname) {
52
92
  return (args) => {
53
- const child = new Process(fn, pname, this.fromChild.bind(this));
93
+ const child = new AsyncProcess(fn, pname, this.fromChild.bind(this));
54
94
  this.children.push(child);
55
95
  child.start(args);
56
96
  return child;
57
97
  };
58
98
  }
59
- _tick() {
60
- if (!this.current) return;
61
- let msg;
62
- let ret = null;
63
- while (msg = this.buffer.shift()) {
64
- ret = this.current.next(msg);
65
- if (ret.done) break;
99
+ forkSync(fn, pname) {
100
+ return this.fork(asyncify(fn), pname);
101
+ }
102
+ async _tick() {
103
+ if (!this.current || this._tickInProgress) return;
104
+ this._tickInProgress = true;
105
+ try {
106
+ let msg;
107
+ let ret = null;
108
+ while ((msg = this.buffer.shift()) !== void 0) {
109
+ ret = await this._safeNext(msg);
110
+ if (!ret || ret.done) break;
111
+ }
112
+ this.notify();
113
+ this._eatResult(ret);
114
+ } catch (e) {
115
+ this._exitReject?.(e);
116
+ this._exitReject = null;
117
+ } finally {
118
+ this._tickInProgress = false;
119
+ }
120
+ }
121
+ /** Call `.next()` and redirect unhandled rejections. */
122
+ async _safeNext(msg) {
123
+ try {
124
+ return await this.current.next(msg);
125
+ } catch (e) {
126
+ this._exitReject?.(e);
127
+ this._exitReject = null;
128
+ return {
129
+ done: true,
130
+ value: void 0
131
+ };
66
132
  }
67
- this.notify();
68
- this._eatResult(ret);
69
133
  }
70
134
  _eatResult(ret) {
71
- if (ret && ret.done) this.exitWaiter.resolve();
135
+ if (!ret) return;
136
+ Promise.resolve(ret).then((r) => {
137
+ if (r.done) this.exitWaiter.resolve();
138
+ });
72
139
  }
73
- /** Send a message to every child process (used for `STOP` propagation). */
140
+ /** Broadcast a message to all children. */
74
141
  toAllChildren(msg) {
75
142
  this.children.forEach((p) => p.send(msg));
76
143
  }
77
- /** Send a message to this process. Messages are buffered and
78
- * processed asynchronously via a microtask. */
144
+ /** Enqueue a message. Processing is async (microtask). */
79
145
  send(msg) {
80
146
  this.buffer.push(msg);
81
147
  this._scheduleTick();
82
148
  }
83
- /** Synchronously flush the message buffer. Useful in tests. */
149
+ /**
150
+ * Synchronously flush the buffer. For sync processes use {@link Process.tick};
151
+ * for async processes this is **not guaranteed** to process everything
152
+ * immediately if reducers contain `await`. Prefer `send()` + `await proc.wait()`.
153
+ */
84
154
  tick() {
85
155
  this.nextTick?.flush();
86
156
  this.nextTick = null;
@@ -96,11 +166,9 @@ var Process = class Process {
96
166
  notify() {
97
167
  this.subscribers.forEach((f) => f());
98
168
  }
99
- /** Whether any subscribers are watching for state changes. */
100
169
  get isListenedTo() {
101
170
  return this.subscribers.length > 0;
102
171
  }
103
- /** Subscribe to state changes. Returns an unsubscribe function. */
104
172
  subscribe(f) {
105
173
  this.subscribers.push(f);
106
174
  return () => {
@@ -109,29 +177,64 @@ var Process = class Process {
109
177
  this.subscribers.splice(idx, 1);
110
178
  };
111
179
  }
112
- /** Pause message processing. Incoming messages are buffered
113
- * but not processed until {@link resume} is called. */
114
180
  pause() {
115
181
  this.nextTick?.cancel();
116
182
  this.nextTick = null;
117
183
  this._isPaused = true;
118
184
  }
119
- /** Resume processing after a {@link pause}. */
120
185
  resume() {
121
186
  this._isPaused = false;
122
187
  this._scheduleTick();
123
188
  }
124
- /** Return a promise that resolves when the generator completes. */
189
+ /**
190
+ * Returns a promise that resolves when the generator completes, or
191
+ * rejects if an unhandled error occurs during message processing.
192
+ */
125
193
  wait() {
126
- return this.exitWaiter.promise;
194
+ return new Promise((resolve, reject) => {
195
+ this._exitReject = reject;
196
+ this.exitWaiter.promise.then(() => {
197
+ this._exitReject = null;
198
+ resolve();
199
+ }, (e) => {
200
+ this._exitReject = null;
201
+ reject(e);
202
+ });
203
+ });
127
204
  }
128
- /** Handle a message forwarded from a child process. `EXIT` removes
129
- * the child; all other messages are forwarded to `send`. */
130
205
  fromChild(msg) {
131
206
  if (msg.type === "EXIT") this.children = this.children.filter((p) => p.id !== msg.pid);
132
207
  this.send(msg);
133
208
  }
134
209
  };
210
+ /**
211
+ * Spawn a new async process. Accepts both sync and async process
212
+ * functions — sync ones are automatically wrapped with {@link asyncify}.
213
+ */
214
+ function spawnAsync(fn, pname, toParent) {
215
+ return (args) => {
216
+ const proc = new AsyncProcess(fn, pname, toParent);
217
+ proc.start(args);
218
+ return proc;
219
+ };
220
+ }
221
+ //#endregion
222
+ //#region src/process.ts
223
+ function spawn(fn, pname, tp) {
224
+ return (a) => new Process(fn, pname, tp).start(a);
225
+ }
226
+ var Process = class extends AsyncProcess {
227
+ constructor(fn, pname, tp) {
228
+ super(asyncify(fn), pname, tp);
229
+ }
230
+ start(a) {
231
+ super.start(a);
232
+ return this;
233
+ }
234
+ tick() {
235
+ return super._tick();
236
+ }
237
+ };
135
238
  //#endregion
136
239
  //#region src/index.ts
137
240
  /**
@@ -139,28 +242,9 @@ var Process = class Process {
139
242
  * generator functions. Processes communicate via message-passing,
140
243
  * can fork children, and expose their state reactively.
141
244
  *
142
- * ## Quick start
143
- *
144
- * ```ts
145
- * import { spawn, runDispatch } from 'posipaki'
146
- *
147
- * function* hello({ pname }) {
148
- * const state = { count: 0 }
149
- * yield state
150
- * yield* runDispatch(pname, (msg) => {
151
- * if (msg.type === 'POKE') state.count++
152
- * }, () => false)
153
- * }
154
- *
155
- * const proc = spawn(hello, 'hello')(null)
156
- * proc.send({ type: 'POKE' })
157
- * proc.tick()
158
- * console.log(proc.state.count) // 1
159
- * ```
160
- *
161
245
  * @module
162
246
  */
163
247
  //#endregion
164
- export { Process, runDispatch, spawn };
248
+ export { AsyncProcess, Process, asyncify, runDispatch, runDispatchAsync, spawn, spawnAsync };
165
249
 
166
250
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/process.ts","../src/index.ts"],"sourcesContent":["import {\n ExitMessage,\n watchExit,\n Waiter,\n defer,\n DeferredCall,\n makeWaiter,\n} from \"./util\";\n\n/**\n * Base message type. All messages must include a `type` field\n * for discrimination in reducers.\n */\nexport interface Message {\n type: string;\n}\n\ntype ProcessGenerator<ProcessState, InMessage> = Generator<\n ProcessState | null,\n void,\n InMessage\n>;\ntype Fork = <\n ChildArgs,\n ChildState,\n InMessage extends Message,\n OutMessage extends Message,\n>(\n fn: ProcessFn<ChildArgs, ChildState, InMessage, OutMessage>,\n pname: string,\n) => (args: ChildArgs) => Process<ChildArgs, ChildState, InMessage, OutMessage>;\n\n/**\n * A process generator function. Receives a {@link ProcessCtx} and\n * initial args, yields state (or `null`) at each step.\n */\nexport type ProcessFn<Args, State, InMessage, OutMessage> = (\n ctx: ProcessCtx<InMessage, OutMessage>,\n args: Args,\n) => ProcessGenerator<State, InMessage>;\ntype ProcessMessageCb<M> = (msg: M) => void;\n\n/**\n * Context injected into every running process. Provides the process\n * name, ability to fork children, and I/O channels.\n */\nexport type ProcessCtx<IM, OM> = {\n pname: string;\n fork: Fork;\n send: (msg: IM) => void;\n toParent: ProcessMessageCb<OM>;\n};\n\ntype NotifyFn = () => void;\ntype _UnsubscibeFn = () => void;\n\n/**\n * Spawn a new process from a process function. Returns a curried\n * function: call it with initial args to start execution.\n */\nexport function spawn<\n Args,\n State,\n InMessage extends Message = Message,\n OutMessage extends Message = ExitMessage,\n>(\n fn: ProcessFn<Args, State, InMessage, OutMessage>,\n pname: string,\n toParent?: ProcessMessageCb<OutMessage>,\n) {\n return (args: Args): Process<Args, State, InMessage, OutMessage> => {\n const process = new Process<Args, State, InMessage, OutMessage>(\n fn,\n pname,\n toParent,\n );\n process.start(args);\n return process;\n };\n}\nconst noop = () => null;\n\n/**\n * A running process — an actor with message-passing, child processes,\n * and observable state. Driven by a generator function that yields\n * state snapshots and receives messages via `yield` expressions.\n */\nclass Process<\n Args,\n State,\n InMessage extends Message,\n OutMessage extends Message,\n> {\n pgenerator: ProcessFn<Args, State, InMessage, OutMessage>;\n pname: string;\n toParent: ProcessMessageCb<OutMessage>;\n id: symbol;\n state: State | null;\n\n private current: ProcessGenerator<State, InMessage> | null;\n private buffer: Array<InMessage>;\n private nextTick: DeferredCall | null;\n private children: Array<Process<unknown, unknown, Message, Message>>;\n private subscribers: Array<NotifyFn>;\n private exitWaiter: Waiter;\n private _isPaused: boolean = false;\n\n constructor(\n fn: ProcessFn<Args, State, InMessage, OutMessage>,\n pname: string,\n toParent: ProcessMessageCb<OutMessage> | undefined,\n ) {\n this.pgenerator = fn;\n this.pname = pname;\n this.toParent = toParent || noop;\n this.id = Symbol(pname);\n\n this.current = null;\n this.state = null;\n this.buffer = [];\n this.nextTick = null;\n\n this.children = [];\n this.subscribers = [];\n this.exitWaiter = makeWaiter();\n }\n\n /** Kick off the generator with initial arguments. */\n start(arg0: Args) {\n const ctx: ProcessCtx<InMessage, OutMessage> = {\n pname: this.pname,\n fork: this.fork.bind(this),\n send: this.send.bind(this),\n toParent: this.toParent,\n };\n const task = watchExit<Args, State, InMessage, OutMessage>(this)(ctx, arg0);\n this.current = task;\n let ret = task.next();\n this.state = ret.value || null;\n this._eatResult(task.next());\n }\n\n /** Fork a child process. Children forward messages to this process\n * and send `EXIT` when they terminate. */\n fork<ChildArgs, ChildState, ChildIM extends Message, ChildOM extends Message>(\n fn: ProcessFn<ChildArgs, ChildState, ChildIM, ChildOM>,\n pname: string,\n ) {\n return (\n args: ChildArgs,\n ): Process<ChildArgs, ChildState, ChildIM, ChildOM> => {\n // not enough typescript power in this one\n const fromChild = this.fromChild.bind(this) as unknown;\n const child = new Process<ChildArgs, ChildState, ChildIM, ChildOM>(\n fn,\n pname,\n fromChild as ProcessMessageCb<ChildOM>,\n );\n // we don't keep track of what is happening down there\n this.children.push(\n child as unknown as Process<unknown, unknown, Message, Message>,\n );\n child.start(args);\n return child;\n };\n }\n\n _tick() {\n if (!this.current) {\n return;\n }\n let msg: InMessage | undefined;\n let ret: IteratorResult<State | null, void> | null = null;\n while ((msg = this.buffer.shift())) {\n ret = this.current.next(msg);\n if (ret.done) {\n break;\n }\n }\n this.notify();\n this._eatResult(ret);\n }\n\n _eatResult(ret: IteratorResult<State | null, void> | null) {\n if (ret && ret.done) {\n this.exitWaiter.resolve();\n }\n }\n\n /** Send a message to every child process (used for `STOP` propagation). */\n toAllChildren(msg: Message) {\n this.children.forEach((p) => p.send(msg));\n }\n\n /** Send a message to this process. Messages are buffered and\n * processed asynchronously via a microtask. */\n send(msg: InMessage) {\n this.buffer.push(msg);\n this._scheduleTick();\n }\n\n /** Synchronously flush the message buffer. Useful in tests. */\n tick() {\n this.nextTick?.flush();\n this.nextTick = null;\n }\n\n _scheduleTick() {\n if (this._isPaused) {\n return;\n }\n\n this.nextTick?.cancel();\n this.nextTick = defer(() => {\n this.nextTick = null;\n this._tick();\n });\n }\n\n notify() {\n this.subscribers.forEach((f) => f());\n }\n\n /** Whether any subscribers are watching for state changes. */\n get isListenedTo() {\n return this.subscribers.length > 0;\n }\n\n /** Subscribe to state changes. Returns an unsubscribe function. */\n subscribe(f: NotifyFn) {\n this.subscribers.push(f);\n return () => {\n const idx = this.subscribers.indexOf(f);\n if (idx < 0) {\n return;\n }\n this.subscribers.splice(idx, 1);\n };\n }\n\n /** Pause message processing. Incoming messages are buffered\n * but not processed until {@link resume} is called. */\n pause() {\n this.nextTick?.cancel();\n this.nextTick = null;\n this._isPaused = true;\n }\n\n /** Resume processing after a {@link pause}. */\n resume() {\n this._isPaused = false;\n this._scheduleTick();\n }\n\n /** Return a promise that resolves when the generator completes. */\n wait() {\n return this.exitWaiter.promise;\n }\n\n /** Handle a message forwarded from a child process. `EXIT` removes\n * the child; all other messages are forwarded to `send`. */\n fromChild(msg: InMessage) {\n if (msg.type === \"EXIT\") {\n this.children = this.children.filter(\n (p) => p.id !== (msg as unknown as ExitMessage).pid,\n );\n }\n this.send(msg);\n }\n}\n\nexport { Process };\n","/**\n * Posipaki — Erlang-inspired lightweight actor processes built on\n * generator functions. Processes communicate via message-passing,\n * can fork children, and expose their state reactively.\n *\n * ## Quick start\n *\n * ```ts\n * import { spawn, runDispatch } from 'posipaki'\n *\n * function* hello({ pname }) {\n * const state = { count: 0 }\n * yield state\n * yield* runDispatch(pname, (msg) => {\n * if (msg.type === 'POKE') state.count++\n * }, () => false)\n * }\n *\n * const proc = spawn(hello, 'hello')(null)\n * proc.send({ type: 'POKE' })\n * proc.tick()\n * console.log(proc.state.count) // 1\n * ```\n *\n * @module\n */\n\nimport { Process, spawn } from \"./process\";\nimport type { Message, ProcessCtx, ProcessFn } from \"./process\";\nimport { runDispatch } from \"./util\";\nimport type { ExitMessage } from \"./util\";\n\nexport { Process, spawn, runDispatch };\nexport type { Message, ExitMessage, ProcessCtx, ProcessFn };\n"],"mappings":";;;;;;AA4DA,SAAgB,MAMd,IACA,OACA,UACA;CACA,QAAQ,SAA4D;EAClE,MAAM,UAAU,IAAI,QAClB,IACA,OACA,QACF;EACA,QAAQ,MAAM,IAAI;EAClB,OAAO;CACT;AACF;AACA,MAAM,aAAa;;;;;;AAOnB,IAAM,UAAN,MAAM,QAKJ;CAeA,YACE,IACA,OACA,UACA;mBAN2B;EAO3B,KAAK,aAAa;EAClB,KAAK,QAAQ;EACb,KAAK,WAAW,YAAY;EAC5B,KAAK,KAAK,OAAO,KAAK;EAEtB,KAAK,UAAU;EACf,KAAK,QAAQ;EACb,KAAK,SAAS,CAAC;EACf,KAAK,WAAW;EAEhB,KAAK,WAAW,CAAC;EACjB,KAAK,cAAc,CAAC;EACpB,KAAK,aAAa,WAAW;CAC/B;;CAGA,MAAM,MAAY;EAChB,MAAM,MAAyC;GAC7C,OAAO,KAAK;GACZ,MAAM,KAAK,KAAK,KAAK,IAAI;GACzB,MAAM,KAAK,KAAK,KAAK,IAAI;GACzB,UAAU,KAAK;EACjB;EACA,MAAM,OAAO,UAA8C,IAAI,EAAE,KAAK,IAAI;EAC1E,KAAK,UAAU;EACf,IAAI,MAAM,KAAK,KAAK;EACpB,KAAK,QAAQ,IAAI,SAAS;EAC1B,KAAK,WAAW,KAAK,KAAK,CAAC;CAC7B;;;CAIA,KACE,IACA,OACA;EACA,QACE,SACqD;GAGrD,MAAM,QAAQ,IAAI,QAChB,IACA,OAHgB,KAAK,UAAU,KAAK,IAI5B,CACV;GAEA,KAAK,SAAS,KACZ,KACF;GACA,MAAM,MAAM,IAAI;GAChB,OAAO;EACT;CACF;CAEA,QAAQ;EACN,IAAI,CAAC,KAAK,SACR;EAEF,IAAI;EACJ,IAAI,MAAiD;EACrD,OAAQ,MAAM,KAAK,OAAO,MAAM,GAAI;GAClC,MAAM,KAAK,QAAQ,KAAK,GAAG;GAC3B,IAAI,IAAI,MACN;EAEJ;EACA,KAAK,OAAO;EACZ,KAAK,WAAW,GAAG;CACrB;CAEA,WAAW,KAAgD;EACzD,IAAI,OAAO,IAAI,MACb,KAAK,WAAW,QAAQ;CAE5B;;CAGA,cAAc,KAAc;EAC1B,KAAK,SAAS,SAAS,MAAM,EAAE,KAAK,GAAG,CAAC;CAC1C;;;CAIA,KAAK,KAAgB;EACnB,KAAK,OAAO,KAAK,GAAG;EACpB,KAAK,cAAc;CACrB;;CAGA,OAAO;EACL,KAAK,UAAU,MAAM;EACrB,KAAK,WAAW;CAClB;CAEA,gBAAgB;EACd,IAAI,KAAK,WACP;EAGF,KAAK,UAAU,OAAO;EACtB,KAAK,WAAW,YAAY;GAC1B,KAAK,WAAW;GAChB,KAAK,MAAM;EACb,CAAC;CACH;CAEA,SAAS;EACP,KAAK,YAAY,SAAS,MAAM,EAAE,CAAC;CACrC;;CAGA,IAAI,eAAe;EACjB,OAAO,KAAK,YAAY,SAAS;CACnC;;CAGA,UAAU,GAAa;EACrB,KAAK,YAAY,KAAK,CAAC;EACvB,aAAa;GACX,MAAM,MAAM,KAAK,YAAY,QAAQ,CAAC;GACtC,IAAI,MAAM,GACR;GAEF,KAAK,YAAY,OAAO,KAAK,CAAC;EAChC;CACF;;;CAIA,QAAQ;EACN,KAAK,UAAU,OAAO;EACtB,KAAK,WAAW;EAChB,KAAK,YAAY;CACnB;;CAGA,SAAS;EACP,KAAK,YAAY;EACjB,KAAK,cAAc;CACrB;;CAGA,OAAO;EACL,OAAO,KAAK,WAAW;CACzB;;;CAIA,UAAU,KAAgB;EACxB,IAAI,IAAI,SAAS,QACf,KAAK,WAAW,KAAK,SAAS,QAC3B,MAAM,EAAE,OAAQ,IAA+B,GAClD;EAEF,KAAK,KAAK,GAAG;CACf;AACF"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/adapters.ts","../src/process.async.ts","../src/process.ts","../src/index.ts"],"sourcesContent":["import type { ProcessFn, AsyncProcessFn, Message } from \"./types.js\"\n\nexport function asyncify<A, S, IM extends Message, OM extends Message>(\n fn: ProcessFn<A, S, IM, OM>,\n): AsyncProcessFn<A, S, IM, OM> {\n return async function* (ctx, args) {\n yield* fn(ctx, args) as any\n }\n}\n","import { defer, makeWaiter, debugLog } from \"./util.js\";\nimport type { DeferredCall, Waiter } from \"./util.js\";\nimport type {\n Message,\n ExitMessage,\n ProcessCtx,\n AsyncProcessFn,\n ProcessFn,\n Fork,\n} from \"./types.js\";\nimport { asyncify } from \"./adapters.js\";\n\n// ---- types ------------------------------------------------------------------\n\n/** An async iterator over process state. */\ntype AsyncProcessGenerator<ProcessState, InMessage> = AsyncGenerator<\n ProcessState | null,\n void,\n InMessage\n>;\n\ntype NotifyFn = () => void;\n\n// ---- runDispatchAsync -------------------------------------------------------\n\ntype AsyncReducer<M> = (msg: M) => Promise<void>;\ntype ReadyFn = () => boolean;\n\n/**\n * Async equivalent of `runDispatch`. Loops, yielding `null` and feeding\n * each incoming message to an `async` reducer. Exits when `readyFn()`\n * returns true.\n */\nexport async function* runDispatchAsync<M>(\n name: string,\n fn: AsyncReducer<M>,\n readyFn: ReadyFn = () => false,\n debugLevel = false,\n): AsyncGenerator<null, void, M> {\n let msg: M;\n while (!readyFn()) {\n msg = yield null;\n debugLog(debugLevel, \"msg\", name, \" <- \", msg);\n await fn(msg);\n }\n}\n\n// ---- AsyncProcess -----------------------------------------------------------\n\ntype ProcessMessageCb<M> = (msg: M) => void;\n\nconst noop = () => null;\n\n/**\n * A process driven by an async generator. Functionally identical to\n * {@link Process} but supports `await` inside reducers.\n *\n * Messages are processed **one at a time** — if a tick is already\n * in-flight, new messages are buffered and processed when the current\n * tick completes.\n */\nexport class AsyncProcess<\n Args,\n State,\n InMessage extends Message,\n OutMessage extends Message,\n> {\n pgenerator: AsyncProcessFn<Args, State, InMessage, OutMessage>;\n pname: string;\n toParent: ProcessMessageCb<OutMessage>;\n id: symbol;\n state: State | null;\n\n private current: AsyncProcessGenerator<State, InMessage> | null = null;\n private buffer: Array<InMessage> = [];\n private nextTick: DeferredCall | null = null;\n private children: Array<AsyncProcess<unknown, unknown, Message, Message>> =\n [];\n private subscribers: Array<NotifyFn> = [];\n private exitWaiter: Waiter;\n private _isPaused: boolean = false;\n private _tickInProgress: boolean = false;\n private _exitReject: ((e: unknown) => void) | null = null;\n private _ready!: Waiter;\n private _resolveReady!: () => void;\n\n constructor(\n fn: AsyncProcessFn<Args, State, InMessage, OutMessage>,\n pname: string,\n toParent: ProcessMessageCb<OutMessage> | undefined,\n ) {\n this.pgenerator = fn;\n this.pname = pname;\n this.toParent = toParent || (noop as ProcessMessageCb<OutMessage>);\n this.id = Symbol(pname);\n this.state = null;\n this.exitWaiter = makeWaiter();\n this._ready = makeWaiter();\n this._resolveReady = this._ready.resolve;\n }\n\n /** Promise that resolves once the initial state is available. */\n ready(): Promise<void> {\n return this._ready.promise;\n }\n\n // ---- lifecycle ------------------------------------------------------------\n\n /**\n * Kick off the async generator. The first `yield` sets the initial\n * state; for async generators this happens in a microtask.\n */\n start(arg0: Args) {\n const ctx: ProcessCtx<Args, State, InMessage, OutMessage> = {\n pname: this.pname,\n fork: this.fork.bind(this),\n forkSync: this.forkSync.bind(this),\n send: this.send.bind(this),\n toParent: this.toParent,\n };\n\n this.current = this._watchExit(ctx, arg0);\n void this.current.next().then((ret: IteratorResult<State | null, void>) => {\n this.state = ret.value ?? null;\n this._resolveReady();\n if (ret.done) {\n this.exitWaiter.resolve();\n return;\n }\n // Advance past the initial yield so the _watchExit generator\n // runs its finally block (EXIT/STOP) and the inner generator\n // enters its dispatch loop. The second .next() sends no\n // message — it just consumes the _watchExit wrapper's own\n // yield, not the user's generator.\n this._eatResult(this.current!.next({ type: \"__ADVANCE__\" } as any));\n });\n return this;\n }\n\n /** Wrap the user's generator so EXIT/STOP logic fires on completion. */\n private async *_watchExit(\n ctx: ProcessCtx<Args, State, InMessage, OutMessage>,\n arg0: Args,\n ): AsyncProcessGenerator<State, InMessage> {\n try {\n yield* this.pgenerator(ctx, arg0);\n } finally {\n this.toAllChildren({ type: \"STOP\" } as Message);\n this.toParent({\n type: \"EXIT\",\n pid: this.id,\n } as unknown as OutMessage);\n }\n }\n\n // ---- fork -----------------------------------------------------------------\n\n fork<ChildArgs, ChildState, ChildIM extends Message, ChildOM extends Message>(\n fn: AsyncProcessFn<ChildArgs, ChildState, ChildIM, ChildOM>,\n pname: string,\n ): (\n args: ChildArgs,\n ) => AsyncProcess<ChildArgs, ChildState, ChildIM, ChildOM> {\n return (args: ChildArgs) => {\n const child = new AsyncProcess<ChildArgs, ChildState, ChildIM, ChildOM>(\n fn,\n pname,\n this.fromChild.bind(this) as unknown as ProcessMessageCb<ChildOM>,\n );\n this.children.push(\n child as unknown as AsyncProcess<unknown, unknown, Message, Message>,\n );\n child.start(args);\n return child;\n };\n }\n\n forkSync<\n ChildArgs,\n ChildState,\n ChildIM extends Message,\n ChildOM extends Message,\n >(\n fn: ProcessFn<ChildArgs, ChildState, ChildIM, ChildOM>,\n pname: string,\n ): (\n args: ChildArgs,\n ) => AsyncProcess<ChildArgs, ChildState, ChildIM, ChildOM> {\n return this.fork(asyncify(fn), pname);\n }\n\n // ---- message processing ---------------------------------------------------\n\n protected async _tick(): Promise<void> {\n if (!this.current || this._tickInProgress) return;\n\n this._tickInProgress = true;\n try {\n let msg: InMessage | undefined;\n let ret: IteratorResult<State | null, void> | null = null;\n while ((msg = this.buffer.shift()) !== undefined) {\n ret = await this._safeNext(msg);\n if (!ret || ret.done) break;\n }\n this.notify();\n this._eatResult(ret);\n } catch (e) {\n this._exitReject?.(e);\n this._exitReject = null;\n } finally {\n this._tickInProgress = false;\n }\n }\n\n /** Call `.next()` and redirect unhandled rejections. */\n private async _safeNext(\n msg: InMessage,\n ): Promise<IteratorResult<State | null, void> | null> {\n try {\n return await this.current!.next(msg);\n } catch (e) {\n this._exitReject?.(e);\n this._exitReject = null;\n return { done: true, value: undefined };\n }\n }\n\n private _eatResult(\n ret:\n | IteratorResult<State | null, void>\n | Promise<IteratorResult<State | null, void>>\n | null,\n ): void {\n if (!ret) return;\n Promise.resolve(ret).then((r) => {\n if (r.done) {\n this.exitWaiter.resolve();\n }\n });\n }\n\n /** Broadcast a message to all children. */\n toAllChildren(msg: Message): void {\n this.children.forEach((p) => p.send(msg));\n }\n\n /** Enqueue a message. Processing is async (microtask). */\n send(msg: InMessage): void {\n this.buffer.push(msg);\n this._scheduleTick();\n }\n\n /**\n * Synchronously flush the buffer. For sync processes use {@link Process.tick};\n * for async processes this is **not guaranteed** to process everything\n * immediately if reducers contain `await`. Prefer `send()` + `await proc.wait()`.\n */\n tick(): void {\n this.nextTick?.flush();\n this.nextTick = null;\n }\n\n private _scheduleTick(): void {\n if (this._isPaused) return;\n\n this.nextTick?.cancel();\n this.nextTick = defer(() => {\n this.nextTick = null;\n void this._tick();\n });\n }\n\n // ---- subscribers ----------------------------------------------------------\n\n notify(): void {\n this.subscribers.forEach((f) => f());\n }\n\n get isListenedTo(): boolean {\n return this.subscribers.length > 0;\n }\n\n subscribe(f: NotifyFn): () => void {\n this.subscribers.push(f);\n return () => {\n const idx = this.subscribers.indexOf(f);\n if (idx < 0) return;\n this.subscribers.splice(idx, 1);\n };\n }\n\n // ---- pause / resume -------------------------------------------------------\n\n pause(): void {\n this.nextTick?.cancel();\n this.nextTick = null;\n this._isPaused = true;\n }\n\n resume(): void {\n this._isPaused = false;\n this._scheduleTick();\n }\n\n // ---- waiting --------------------------------------------------------------\n\n /**\n * Returns a promise that resolves when the generator completes, or\n * rejects if an unhandled error occurs during message processing.\n */\n wait(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n this._exitReject = reject;\n\n this.exitWaiter.promise.then(\n () => {\n this._exitReject = null;\n resolve();\n },\n (e) => {\n this._exitReject = null;\n reject(e);\n },\n );\n });\n }\n\n // ---- child messages -------------------------------------------------------\n\n private fromChild(msg: InMessage): void {\n if (msg.type === \"EXIT\") {\n this.children = this.children.filter(\n (p) => p.id !== (msg as unknown as ExitMessage).pid,\n );\n }\n this.send(msg);\n }\n}\n\n// ---- spawnAsync -------------------------------------------------------------\n\n/**\n * Spawn a new async process. Accepts both sync and async process\n * functions — sync ones are automatically wrapped with {@link asyncify}.\n */\nexport function spawnAsync<\n Args,\n State,\n InMessage extends Message = Message,\n OutMessage extends Message = ExitMessage,\n>(\n fn: AsyncProcessFn<Args, State, InMessage, OutMessage>,\n pname: string,\n toParent?: ProcessMessageCb<OutMessage>,\n): (args: Args) => AsyncProcess<Args, State, InMessage, OutMessage> {\n return (args: Args) => {\n const proc = new AsyncProcess<Args, State, InMessage, OutMessage>(\n fn,\n pname,\n toParent,\n );\n proc.start(args);\n return proc;\n };\n}\n","import { AsyncProcess } from \"./process.async.js\";\nimport { asyncify } from \"./adapters.js\";\nimport type { Message, ExitMessage, ProcessFn } from \"./types.js\";\n\nexport type { Message, ProcessFn, ProcessCtx } from \"./types.js\";\nexport { runDispatch } from \"./util.js\";\n\nexport function spawn<\n A,\n S,\n IM extends Message = Message,\n OM extends Message = ExitMessage,\n>(\n fn: ProcessFn<A, S, IM, OM>,\n pname: string,\n tp?: (m: OM) => void,\n): (a: A) => Process<A, S, IM, OM> {\n return (a: A) => new Process(fn, pname, tp).start(a) as Process<A, S, IM, OM>;\n}\n\nclass Process<\n A,\n S,\n IM extends Message,\n OM extends Message,\n> extends AsyncProcess<A, S, IM, OM> {\n constructor(\n fn: ProcessFn<A, S, IM, OM>,\n pname: string,\n tp?: (m: OM) => void,\n ) {\n super(asyncify(fn), pname, tp);\n }\n\n start(a: A) {\n super.start(a);\n return this;\n }\n tick(): Promise<void> {\n return super._tick();\n }\n}\n\nexport { Process };\n","/**\n * Posipaki — Erlang-inspired lightweight actor processes built on\n * generator functions. Processes communicate via message-passing,\n * can fork children, and expose their state reactively.\n *\n * @module\n */\n\nimport { Process, spawn } from \"./process\"\nimport { runDispatch } from \"./util\"\nimport { AsyncProcess, spawnAsync, runDispatchAsync } from \"./process.async\"\nimport { asyncify } from \"./adapters\"\n\nexport { Process, spawn, runDispatch }\nexport { AsyncProcess, spawnAsync, runDispatchAsync, asyncify }\n\nexport type {\n Message, ExitMessage, ProcessFn, ProcessCtx,\n AsyncProcessFn, PipeState, SupervisorState,\n} from \"./types\"\n"],"mappings":";;AAEA,SAAgB,SACd,IAC8B;CAC9B,OAAO,iBAAiB,KAAK,MAAM;EACjC,OAAO,GAAG,KAAK,IAAI;CACrB;AACF;;;;;;;;ACyBA,gBAAuB,iBACrB,MACA,IACA,gBAAyB,OACzB,aAAa,OACkB;CAC/B,IAAI;CACJ,OAAO,CAAC,QAAQ,GAAG;EACjB,MAAM,MAAM;EACZ,SAAS,YAAY,OAAO,MAAM,QAAQ,GAAG;EAC7C,MAAM,GAAG,GAAG;CACd;AACF;AAMA,MAAM,aAAa;;;;;;;;;AAUnB,IAAa,eAAb,MAAa,aAKX;CAoBA,YACE,IACA,OACA,UACA;iBAjBgE;gBAC/B,CAAC;kBACI;kBAEtC,CAAC;qBACoC,CAAC;mBAEX;yBACM;qBACkB;EASnD,KAAK,aAAa;EAClB,KAAK,QAAQ;EACb,KAAK,WAAW,YAAa;EAC7B,KAAK,KAAK,OAAO,KAAK;EACtB,KAAK,QAAQ;EACb,KAAK,aAAa,WAAW;EAC7B,KAAK,SAAS,WAAW;EACzB,KAAK,gBAAgB,KAAK,OAAO;CACnC;;CAGA,QAAuB;EACrB,OAAO,KAAK,OAAO;CACrB;;;;;CAQA,MAAM,MAAY;EAChB,MAAM,MAAsD;GAC1D,OAAO,KAAK;GACZ,MAAM,KAAK,KAAK,KAAK,IAAI;GACzB,UAAU,KAAK,SAAS,KAAK,IAAI;GACjC,MAAM,KAAK,KAAK,KAAK,IAAI;GACzB,UAAU,KAAK;EACjB;EAEA,KAAK,UAAU,KAAK,WAAW,KAAK,IAAI;EACxC,KAAU,QAAQ,KAAK,EAAE,MAAM,QAA4C;GACzE,KAAK,QAAQ,IAAI,SAAS;GAC1B,KAAK,cAAc;GACnB,IAAI,IAAI,MAAM;IACZ,KAAK,WAAW,QAAQ;IACxB;GACF;GAMA,KAAK,WAAW,KAAK,QAAS,KAAK,EAAE,MAAM,cAAc,CAAQ,CAAC;EACpE,CAAC;EACD,OAAO;CACT;;CAGA,OAAe,WACb,KACA,MACyC;EACzC,IAAI;GACF,OAAO,KAAK,WAAW,KAAK,IAAI;EAClC,UAAU;GACR,KAAK,cAAc,EAAE,MAAM,OAAO,CAAY;GAC9C,KAAK,SAAS;IACZ,MAAM;IACN,KAAK,KAAK;GACZ,CAA0B;EAC5B;CACF;CAIA,KACE,IACA,OAGyD;EACzD,QAAQ,SAAoB;GAC1B,MAAM,QAAQ,IAAI,aAChB,IACA,OACA,KAAK,UAAU,KAAK,IAAI,CAC1B;GACA,KAAK,SAAS,KACZ,KACF;GACA,MAAM,MAAM,IAAI;GAChB,OAAO;EACT;CACF;CAEA,SAME,IACA,OAGyD;EACzD,OAAO,KAAK,KAAK,SAAS,EAAE,GAAG,KAAK;CACtC;CAIA,MAAgB,QAAuB;EACrC,IAAI,CAAC,KAAK,WAAW,KAAK,iBAAiB;EAE3C,KAAK,kBAAkB;EACvB,IAAI;GACF,IAAI;GACJ,IAAI,MAAiD;GACrD,QAAQ,MAAM,KAAK,OAAO,MAAM,OAAO,KAAA,GAAW;IAChD,MAAM,MAAM,KAAK,UAAU,GAAG;IAC9B,IAAI,CAAC,OAAO,IAAI,MAAM;GACxB;GACA,KAAK,OAAO;GACZ,KAAK,WAAW,GAAG;EACrB,SAAS,GAAG;GACV,KAAK,cAAc,CAAC;GACpB,KAAK,cAAc;EACrB,UAAU;GACR,KAAK,kBAAkB;EACzB;CACF;;CAGA,MAAc,UACZ,KACoD;EACpD,IAAI;GACF,OAAO,MAAM,KAAK,QAAS,KAAK,GAAG;EACrC,SAAS,GAAG;GACV,KAAK,cAAc,CAAC;GACpB,KAAK,cAAc;GACnB,OAAO;IAAE,MAAM;IAAM,OAAO,KAAA;GAAU;EACxC;CACF;CAEA,WACE,KAIM;EACN,IAAI,CAAC,KAAK;EACV,QAAQ,QAAQ,GAAG,EAAE,MAAM,MAAM;GAC/B,IAAI,EAAE,MACJ,KAAK,WAAW,QAAQ;EAE5B,CAAC;CACH;;CAGA,cAAc,KAAoB;EAChC,KAAK,SAAS,SAAS,MAAM,EAAE,KAAK,GAAG,CAAC;CAC1C;;CAGA,KAAK,KAAsB;EACzB,KAAK,OAAO,KAAK,GAAG;EACpB,KAAK,cAAc;CACrB;;;;;;CAOA,OAAa;EACX,KAAK,UAAU,MAAM;EACrB,KAAK,WAAW;CAClB;CAEA,gBAA8B;EAC5B,IAAI,KAAK,WAAW;EAEpB,KAAK,UAAU,OAAO;EACtB,KAAK,WAAW,YAAY;GAC1B,KAAK,WAAW;GAChB,KAAU,MAAM;EAClB,CAAC;CACH;CAIA,SAAe;EACb,KAAK,YAAY,SAAS,MAAM,EAAE,CAAC;CACrC;CAEA,IAAI,eAAwB;EAC1B,OAAO,KAAK,YAAY,SAAS;CACnC;CAEA,UAAU,GAAyB;EACjC,KAAK,YAAY,KAAK,CAAC;EACvB,aAAa;GACX,MAAM,MAAM,KAAK,YAAY,QAAQ,CAAC;GACtC,IAAI,MAAM,GAAG;GACb,KAAK,YAAY,OAAO,KAAK,CAAC;EAChC;CACF;CAIA,QAAc;EACZ,KAAK,UAAU,OAAO;EACtB,KAAK,WAAW;EAChB,KAAK,YAAY;CACnB;CAEA,SAAe;EACb,KAAK,YAAY;EACjB,KAAK,cAAc;CACrB;;;;;CAQA,OAAsB;EACpB,OAAO,IAAI,SAAe,SAAS,WAAW;GAC5C,KAAK,cAAc;GAEnB,KAAK,WAAW,QAAQ,WAChB;IACJ,KAAK,cAAc;IACnB,QAAQ;GACV,IACC,MAAM;IACL,KAAK,cAAc;IACnB,OAAO,CAAC;GACV,CACF;EACF,CAAC;CACH;CAIA,UAAkB,KAAsB;EACtC,IAAI,IAAI,SAAS,QACf,KAAK,WAAW,KAAK,SAAS,QAC3B,MAAM,EAAE,OAAQ,IAA+B,GAClD;EAEF,KAAK,KAAK,GAAG;CACf;AACF;;;;;AAQA,SAAgB,WAMd,IACA,OACA,UACkE;CAClE,QAAQ,SAAe;EACrB,MAAM,OAAO,IAAI,aACf,IACA,OACA,QACF;EACA,KAAK,MAAM,IAAI;EACf,OAAO;CACT;AACF;;;ACrWA,SAAgB,MAMd,IACA,OACA,IACiC;CACjC,QAAQ,MAAS,IAAI,QAAQ,IAAI,OAAO,EAAE,EAAE,MAAM,CAAC;AACrD;AAEA,IAAM,UAAN,cAKU,aAA2B;CACnC,YACE,IACA,OACA,IACA;EACA,MAAM,SAAS,EAAE,GAAG,OAAO,EAAE;CAC/B;CAEA,MAAM,GAAM;EACV,MAAM,MAAM,CAAC;EACb,OAAO;CACT;CACA,OAAsB;EACpB,OAAO,MAAM,MAAM;CACrB;AACF"}
package/dist/pipe.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as ProcessFn, o as ExitMessage, t as Message } from "./process-BlUe6Luh.js";
1
+ import { c as ExitMessage, f as ProcessFn, l as Message } from "./process-DF-plv-v.js";
2
2
 
3
3
  //#region src/pipe.d.ts
4
4
  /**
@@ -16,7 +16,7 @@ interface PipeState<Params, Result> {
16
16
  * The `.state.result` of each completed child is spread into the params
17
17
  * of the next.
18
18
  */
19
- declare function pipe<Params, Result>(fns: ProcessFn<Params, any, any, any>[]): ProcessFn<Params, PipeState<Params, Result>, Message, Message | ExitMessage>;
19
+ declare function pipe<Params, Result>(fns: ProcessFn<unknown, unknown, Message, Message>[]): ProcessFn<Params, PipeState<Params, Result>, Message, Message | ExitMessage>;
20
20
  //#endregion
21
21
  export { PipeState, pipe };
22
22
  //# sourceMappingURL=pipe.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pipe.d.ts","names":[],"sources":["../src/pipe.ts"],"mappings":";;;AAWA;;;;;AAAA,UAAiB,SAAA;EACf,MAAA,EAAQ,MAAA;EACR,MAAA,EAAQ,MAAM;EACd,OAAA;AAAA;;;AAAO;AACR;;iBAOQ,IAAA,iBACP,GAAA,EAAK,SAAA,CAAU,MAAA,qBACd,SAAA,CACD,MAAA,EACA,SAAA,CAAU,MAAA,EAAQ,MAAA,GAClB,OAAA,EACA,OAAA,GAAU,WAAA"}
1
+ {"version":3,"file":"pipe.d.ts","names":[],"sources":["../src/pipe.ts"],"mappings":";;;AAWA;;;;;AAAA,UAAiB,SAAA;EACf,MAAA,EAAQ,MAAA;EACR,MAAA,EAAQ,MAAM;EACd,OAAA;AAAA;;;AAAO;AACR;;iBAOQ,IAAA,iBACP,GAAA,EAAK,SAAA,mBAA4B,OAAA,EAAS,OAAA,MACzC,SAAA,CACD,MAAA,EACA,SAAA,CAAU,MAAA,EAAQ,MAAA,GAClB,OAAA,EACA,OAAA,GAAU,WAAA"}
package/dist/pipe.js CHANGED
@@ -1,4 +1,4 @@
1
- import { r as runDispatch } from "./util-5PSJsovA.js";
1
+ import { i as runDispatch } from "./util-Cw64MseZ.js";
2
2
  //#region src/pipe.ts
3
3
  /**
4
4
  * Chain process functions so each runs after the previous one exits.
@@ -6,7 +6,7 @@ import { r as runDispatch } from "./util-5PSJsovA.js";
6
6
  * of the next.
7
7
  */
8
8
  function pipe(fns) {
9
- return function* ({ pname, fork }, params) {
9
+ return function* (ctx, params) {
10
10
  const state = {
11
11
  params: { ...params },
12
12
  result: null,
@@ -17,14 +17,14 @@ function pipe(fns) {
17
17
  let task;
18
18
  function spawnNext() {
19
19
  const fn = queue.shift();
20
- task = fork(fn, `${pname} [${fn.name || "<anonymous>"}]`)(state.params);
20
+ task = ctx.forkSync(fn, `${ctx.pname} [${fn.name || "<anonymous>"}]`)(state.params);
21
21
  }
22
22
  function hasNext() {
23
23
  return queue.length > 0;
24
24
  }
25
25
  spawnNext();
26
26
  state.running = hasNext();
27
- yield* runDispatch(pname, (msg) => {
27
+ yield* runDispatch(ctx.pname, (msg) => {
28
28
  if (msg.type === "STOP") {
29
29
  state.params = null;
30
30
  state.running = false;
package/dist/pipe.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"pipe.js","names":[],"sources":["../src/pipe.ts"],"sourcesContent":["import { runDispatch } from \"./util.js\";\nimport type { ExitMessage } from \"./util.js\";\nimport type { ProcessFn, ProcessCtx } from \"./process.js\";\nimport { Process } from \"./process.js\";\nimport type { Message } from \"./process.js\";\n\n/**\n * State yielded by the pipe process. `params` starts as a copy of the\n * initial args and is updated after each child exits by merging the\n * child's `state.result` into it.\n */\nexport interface PipeState<Params, Result> {\n params: Params | null;\n result: Result | null;\n running: boolean;\n}\n\n/**\n * Chain process functions so each runs after the previous one exits.\n * The `.state.result` of each completed child is spread into the params\n * of the next.\n */\nfunction pipe<Params, Result>(\n fns: ProcessFn<Params, any, any, any>[],\n): ProcessFn<\n Params,\n PipeState<Params, Result>,\n Message,\n Message | ExitMessage\n> {\n return function* (\n { pname, fork }: ProcessCtx<Message, Message | ExitMessage>,\n params: Params,\n ) {\n const state: PipeState<Params, Result> = {\n params: { ...params },\n result: null,\n running: false,\n };\n yield state;\n\n const queue = [...fns];\n let task: Process<any, any, any, any>;\n\n function spawnNext(): void {\n const fn = queue.shift()!;\n task = fork(\n fn,\n `${pname} [${fn.name || \"<anonymous>\"}]`,\n )(state.params as Params);\n }\n\n function hasNext(): boolean {\n return queue.length > 0;\n }\n\n spawnNext();\n state.running = hasNext();\n\n yield* runDispatch<Message | ExitMessage>(\n pname,\n (msg) => {\n if (msg.type === \"STOP\") {\n state.params = null;\n state.running = false;\n return;\n }\n\n if (msg.type === \"EXIT\" && (msg as ExitMessage).pid === task.id) {\n if (hasNext()) {\n state.params = {\n ...state.params!,\n ...task.state?.result,\n } as Params;\n spawnNext();\n } else {\n state.running = false;\n state.result = task.state?.result as Result;\n }\n }\n },\n () => !state.running,\n true,\n );\n };\n}\n\nexport { pipe };\n"],"mappings":";;;;;;;AAsBA,SAAS,KACP,KAMA;CACA,OAAO,WACL,EAAE,OAAO,QACT,QACA;EACA,MAAM,QAAmC;GACvC,QAAQ,EAAE,GAAG,OAAO;GACpB,QAAQ;GACR,SAAS;EACX;EACA,MAAM;EAEN,MAAM,QAAQ,CAAC,GAAG,GAAG;EACrB,IAAI;EAEJ,SAAS,YAAkB;GACzB,MAAM,KAAK,MAAM,MAAM;GACvB,OAAO,KACL,IACA,GAAG,MAAM,IAAI,GAAG,QAAQ,cAAc,EACxC,EAAE,MAAM,MAAgB;EAC1B;EAEA,SAAS,UAAmB;GAC1B,OAAO,MAAM,SAAS;EACxB;EAEA,UAAU;EACV,MAAM,UAAU,QAAQ;EAExB,OAAO,YACL,QACC,QAAQ;GACP,IAAI,IAAI,SAAS,QAAQ;IACvB,MAAM,SAAS;IACf,MAAM,UAAU;IAChB;GACF;GAEA,IAAI,IAAI,SAAS,UAAW,IAAoB,QAAQ,KAAK,IAC3D,IAAI,QAAQ,GAAG;IACb,MAAM,SAAS;KACb,GAAG,MAAM;KACT,GAAG,KAAK,OAAO;IACjB;IACA,UAAU;GACZ,OAAO;IACL,MAAM,UAAU;IAChB,MAAM,SAAS,KAAK,OAAO;GAC7B;EAEJ,SACM,CAAC,MAAM,SACb,IACF;CACF;AACF"}
1
+ {"version":3,"file":"pipe.js","names":[],"sources":["../src/pipe.ts"],"sourcesContent":["import { runDispatch } from \"./util.js\";\nimport type { ExitMessage } from \"./util.js\";\nimport type { ProcessFn, ProcessCtx } from \"./process.js\";\nimport type { AsyncProcess } from \"./process.async.js\";\nimport type { Message } from \"./types.js\";\n\n/**\n * State yielded by the pipe process. `params` starts as a copy of the\n * initial args and is updated after each child exits by merging the\n * child's `state.result` into it.\n */\nexport interface PipeState<Params, Result> {\n params: Params | null;\n result: Result | null;\n running: boolean;\n}\n\n/**\n * Chain process functions so each runs after the previous one exits.\n * The `.state.result` of each completed child is spread into the params\n * of the next.\n */\nfunction pipe<Params, Result>(\n fns: ProcessFn<unknown, unknown, Message, Message>[],\n): ProcessFn<\n Params,\n PipeState<Params, Result>,\n Message,\n Message | ExitMessage\n> {\n return function* (\n ctx: ProcessCtx<\n Params,\n PipeState<Params, Result>,\n Message,\n Message | ExitMessage\n >,\n params: Params,\n ) {\n const state: PipeState<Params, Result> = {\n params: { ...params },\n result: null,\n running: false,\n };\n yield state;\n\n const queue = [...fns];\n let task: AsyncProcess<any, any, any, any>;\n\n function spawnNext(): void {\n const fn = queue.shift()!;\n task = ctx.forkSync(\n fn,\n `${ctx.pname} [${fn.name || \"<anonymous>\"}]`,\n )(state.params as Params);\n }\n\n function hasNext(): boolean {\n return queue.length > 0;\n }\n\n spawnNext();\n state.running = hasNext();\n\n yield* runDispatch<Message | ExitMessage>(\n ctx.pname,\n (msg) => {\n if (msg.type === \"STOP\") {\n state.params = null;\n state.running = false;\n return;\n }\n\n if (msg.type === \"EXIT\" && (msg as ExitMessage).pid === task.id) {\n if (hasNext()) {\n state.params = {\n ...state.params!,\n ...task.state?.result,\n } as Params;\n spawnNext();\n } else {\n state.running = false;\n state.result = task.state?.result as Result;\n }\n }\n },\n () => !state.running,\n true,\n );\n };\n}\n\nexport { pipe };\n"],"mappings":";;;;;;;AAsBA,SAAS,KACP,KAMA;CACA,OAAO,WACL,KAMA,QACA;EACA,MAAM,QAAmC;GACvC,QAAQ,EAAE,GAAG,OAAO;GACpB,QAAQ;GACR,SAAS;EACX;EACA,MAAM;EAEN,MAAM,QAAQ,CAAC,GAAG,GAAG;EACrB,IAAI;EAEJ,SAAS,YAAkB;GACzB,MAAM,KAAK,MAAM,MAAM;GACvB,OAAO,IAAI,SACT,IACA,GAAG,IAAI,MAAM,IAAI,GAAG,QAAQ,cAAc,EAC5C,EAAE,MAAM,MAAgB;EAC1B;EAEA,SAAS,UAAmB;GAC1B,OAAO,MAAM,SAAS;EACxB;EAEA,UAAU;EACV,MAAM,UAAU,QAAQ;EAExB,OAAO,YACL,IAAI,QACH,QAAQ;GACP,IAAI,IAAI,SAAS,QAAQ;IACvB,MAAM,SAAS;IACf,MAAM,UAAU;IAChB;GACF;GAEA,IAAI,IAAI,SAAS,UAAW,IAAoB,QAAQ,KAAK,IAC3D,IAAI,QAAQ,GAAG;IACb,MAAM,SAAS;KACb,GAAG,MAAM;KACT,GAAG,KAAK,OAAO;IACjB;IACA,UAAU;GACZ,OAAO;IACL,MAAM,UAAU;IAChB,MAAM,SAAS,KAAK,OAAO;GAC7B;EAEJ,SACM,CAAC,MAAM,SACb,IACF;CACF;AACF"}
@@ -0,0 +1,141 @@
1
+ //#region src/types.d.ts
2
+ /** Base message type. All messages must include a `type` field
3
+ * for discrimination in reducers. */
4
+ interface Message {
5
+ type: string;
6
+ }
7
+ /** Message emitted by a process to its parent when it terminates. */
8
+ type ExitMessage = {
9
+ type: "EXIT";
10
+ pid: symbol;
11
+ };
12
+ type ProcessFn<Args, State, InMessage extends Message, OutMessage extends Message> = (ctx: ProcessCtx<Args, State, InMessage, OutMessage>, args: Args) => Generator<State | null, void, InMessage>;
13
+ type AsyncProcessFn<Args, State, InMessage extends Message, OutMessage extends Message> = (ctx: ProcessCtx<Args, State, InMessage, OutMessage>, args: Args) => AsyncGenerator<State | null, void, InMessage>;
14
+ type ProcessMessageCb$1<M> = (msg: M) => void;
15
+ /** Fork a child process. Takes a ProcessFn and a name, returns a
16
+ * curried function that accepts the child's initial args. */
17
+ /** Context injected into every running process. */
18
+ type ProcessCtx<Args, State, IM extends Message, OM extends Message> = {
19
+ pname: string;
20
+ send: (msg: IM) => void;
21
+ toParent: ProcessMessageCb$1<OM>;
22
+ } & Pick<AsyncProcess<Args, State, IM, OM>, "fork" | "forkSync">;
23
+ /** State yielded by the pipe process. */
24
+ interface PipeState<Params, Result> {
25
+ params: Params | null;
26
+ result: Result | null;
27
+ running: boolean;
28
+ }
29
+ /** State yielded by the supervisor process. */
30
+ interface SupervisorState {
31
+ processes: any[];
32
+ phase: "wait" | "running" | "stopping";
33
+ }
34
+ //#endregion
35
+ //#region src/process.async.d.ts
36
+ type NotifyFn = () => void;
37
+ type AsyncReducer<M> = (msg: M) => Promise<void>;
38
+ type ReadyFn$1 = () => boolean;
39
+ /**
40
+ * Async equivalent of `runDispatch`. Loops, yielding `null` and feeding
41
+ * each incoming message to an `async` reducer. Exits when `readyFn()`
42
+ * returns true.
43
+ */
44
+ declare function runDispatchAsync<M>(name: string, fn: AsyncReducer<M>, readyFn?: ReadyFn$1, debugLevel?: boolean): AsyncGenerator<null, void, M>;
45
+ type ProcessMessageCb<M> = (msg: M) => void;
46
+ /**
47
+ * A process driven by an async generator. Functionally identical to
48
+ * {@link Process} but supports `await` inside reducers.
49
+ *
50
+ * Messages are processed **one at a time** — if a tick is already
51
+ * in-flight, new messages are buffered and processed when the current
52
+ * tick completes.
53
+ */
54
+ declare class AsyncProcess<Args, State, InMessage extends Message, OutMessage extends Message> {
55
+ pgenerator: AsyncProcessFn<Args, State, InMessage, OutMessage>;
56
+ pname: string;
57
+ toParent: ProcessMessageCb<OutMessage>;
58
+ id: symbol;
59
+ state: State | null;
60
+ private current;
61
+ private buffer;
62
+ private nextTick;
63
+ private children;
64
+ private subscribers;
65
+ private exitWaiter;
66
+ private _isPaused;
67
+ private _tickInProgress;
68
+ private _exitReject;
69
+ private _ready;
70
+ private _resolveReady;
71
+ constructor(fn: AsyncProcessFn<Args, State, InMessage, OutMessage>, pname: string, toParent: ProcessMessageCb<OutMessage> | undefined);
72
+ /** Promise that resolves once the initial state is available. */
73
+ ready(): Promise<void>;
74
+ /**
75
+ * Kick off the async generator. The first `yield` sets the initial
76
+ * state; for async generators this happens in a microtask.
77
+ */
78
+ start(arg0: Args): this;
79
+ /** Wrap the user's generator so EXIT/STOP logic fires on completion. */
80
+ private _watchExit;
81
+ fork<ChildArgs, ChildState, ChildIM extends Message, ChildOM extends Message>(fn: AsyncProcessFn<ChildArgs, ChildState, ChildIM, ChildOM>, pname: string): (args: ChildArgs) => AsyncProcess<ChildArgs, ChildState, ChildIM, ChildOM>;
82
+ forkSync<ChildArgs, ChildState, ChildIM extends Message, ChildOM extends Message>(fn: ProcessFn<ChildArgs, ChildState, ChildIM, ChildOM>, pname: string): (args: ChildArgs) => AsyncProcess<ChildArgs, ChildState, ChildIM, ChildOM>;
83
+ protected _tick(): Promise<void>;
84
+ /** Call `.next()` and redirect unhandled rejections. */
85
+ private _safeNext;
86
+ private _eatResult;
87
+ /** Broadcast a message to all children. */
88
+ toAllChildren(msg: Message): void;
89
+ /** Enqueue a message. Processing is async (microtask). */
90
+ send(msg: InMessage): void;
91
+ /**
92
+ * Synchronously flush the buffer. For sync processes use {@link Process.tick};
93
+ * for async processes this is **not guaranteed** to process everything
94
+ * immediately if reducers contain `await`. Prefer `send()` + `await proc.wait()`.
95
+ */
96
+ tick(): void;
97
+ private _scheduleTick;
98
+ notify(): void;
99
+ get isListenedTo(): boolean;
100
+ subscribe(f: NotifyFn): () => void;
101
+ pause(): void;
102
+ resume(): void;
103
+ /**
104
+ * Returns a promise that resolves when the generator completes, or
105
+ * rejects if an unhandled error occurs during message processing.
106
+ */
107
+ wait(): Promise<void>;
108
+ private fromChild;
109
+ }
110
+ /**
111
+ * Spawn a new async process. Accepts both sync and async process
112
+ * functions — sync ones are automatically wrapped with {@link asyncify}.
113
+ */
114
+ declare function spawnAsync<Args, State, InMessage extends Message = Message, OutMessage extends Message = ExitMessage>(fn: AsyncProcessFn<Args, State, InMessage, OutMessage>, pname: string, toParent?: ProcessMessageCb<OutMessage>): (args: Args) => AsyncProcess<Args, State, InMessage, OutMessage>;
115
+ //#endregion
116
+ //#region src/util.d.ts
117
+ type ReducerClosure<M> = (msg: M) => void;
118
+ type ReadyFn = () => boolean;
119
+ /**
120
+ * Generator helper that loops, yielding `null` and feeding incoming
121
+ * messages to `fn` until `readyFn()` returns true. Used inside
122
+ * process generators to build the main message loop.
123
+ */
124
+ declare function runDispatch<M>(name: string, fn: ReducerClosure<M>, readyFn?: ReadyFn, debugLevel?: boolean): Generator<null, void, M>;
125
+ /**
126
+ * Wrap a process generator so that on completion it sends STOP to
127
+ * all children and EXIT to the parent. Useful for custom process
128
+ * wrappers that need lifecycle management without extending
129
+ * AsyncProcess.
130
+ */
131
+ //#endregion
132
+ //#region src/process.d.ts
133
+ declare function spawn<A, S, IM extends Message = Message, OM extends Message = ExitMessage>(fn: ProcessFn<A, S, IM, OM>, pname: string, tp?: (m: OM) => void): (a: A) => Process<A, S, IM, OM>;
134
+ declare class Process<A, S, IM extends Message, OM extends Message> extends AsyncProcess<A, S, IM, OM> {
135
+ constructor(fn: ProcessFn<A, S, IM, OM>, pname: string, tp?: (m: OM) => void);
136
+ start(a: A): this;
137
+ tick(): Promise<void>;
138
+ }
139
+ //#endregion
140
+ export { runDispatchAsync as a, ExitMessage as c, ProcessCtx as d, ProcessFn as f, AsyncProcess as i, Message as l, spawn as n, spawnAsync as o, SupervisorState as p, runDispatch as r, AsyncProcessFn as s, Process as t, PipeState as u };
141
+ //# sourceMappingURL=process-DF-plv-v.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-DF-plv-v.d.ts","names":[],"sources":["../src/types.ts","../src/process.async.ts","../src/util.ts","../src/process.ts"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { i as ProcessFn, n as Process, o as ExitMessage, r as ProcessCtx } from "./process-BlUe6Luh.js";
1
+ import { c as ExitMessage, d as ProcessCtx, f as ProcessFn, t as Process } from "./process-DF-plv-v.js";
2
2
 
3
3
  //#region src/supervisor.d.ts
4
4
  type SupMsg = ExitMessage | RunMsg | StopMsg | ErrorMsg | OkMsg;
@@ -34,7 +34,7 @@ declare function supervise({
34
34
  pname,
35
35
  toParent,
36
36
  fork
37
- }: ProcessCtx<SupMsg, SupMsg>, wrap?: (s: SupervisorState) => SupervisorState, debugLevel?: boolean): Generator<SupervisorState | null, void, SupMsg>;
37
+ }: ProcessCtx<null, SupervisorState, SupMsg, SupMsg>, wrap?: (s: SupervisorState) => SupervisorState, debugLevel?: boolean): Generator<SupervisorState | null, void, SupMsg>;
38
38
  /**
39
39
  * Bind a process function to a supervisor. The returned function,
40
40
  * when called, sends a `RUN` message so the supervisor forks the child.
@@ -1 +1 @@
1
- {"version":3,"file":"supervisor.d.ts","names":[],"sources":["../src/supervisor.ts"],"mappings":";;;KAMK,MAAA,GAAS,WAAA,GAAc,MAAA,GAAS,OAAA,GAAU,QAAA,GAAW,KAAA;AAAA,UAEhD,MAAA;EACR,IAAA;EACA,EAAA,EAAI,SAAS;EACb,IAAA;EACA,KAAA;AAAA;AAAA,UAGQ,OAAA;EACR,IAAI;AAAA;AAAA,UAGI,QAAA;EACR,IAAI;AAAA;AAAA,UAGI,KAAA;EACR,IAAA;EAAA,CACC,GAAW;AAAA;AAAA,UAKG,eAAA;EACf,SAAA,EAAW,OAAO;EAClB,KAAA;AAAA;;;;;;;;AApBK;iBAiCG,SAAA;EACN,KAAA;EAAO,QAAA;EAAU;AAAA,GAAQ,UAAA,CAAW,MAAA,EAAQ,MAAA,GAC9C,IAAA,IAAO,CAAA,EAAG,eAAA,KAAoB,eAAA,EAC9B,UAAA,aACC,SAAA,CAAU,eAAA,eAA8B,MAAA;;;;AAjCrC;iBAoEG,MAAA,qBACP,UAAA,EAAY,OAAA,WAAkB,MAAA,EAAQ,MAAA,GACtC,EAAA,EAAI,SAAA,CAAU,IAAA,kBACd,KAAA,eACK,IAAA,EAAM,IAAA"}
1
+ {"version":3,"file":"supervisor.d.ts","names":[],"sources":["../src/supervisor.ts"],"mappings":";;;KAMK,MAAA,GAAS,WAAA,GAAc,MAAA,GAAS,OAAA,GAAU,QAAA,GAAW,KAAA;AAAA,UAEhD,MAAA;EACR,IAAA;EACA,EAAA,EAAI,SAAS;EACb,IAAA;EACA,KAAA;AAAA;AAAA,UAGQ,OAAA;EACR,IAAI;AAAA;AAAA,UAGI,QAAA;EACR,IAAI;AAAA;AAAA,UAGI,KAAA;EACR,IAAA;EAAA,CACC,GAAW;AAAA;AAAA,UAKG,eAAA;EACf,SAAA,EAAW,OAAO;EAClB,KAAA;AAAA;;;;;;;;AApBK;iBAiCG,SAAA;EACN,KAAA;EAAO,QAAA;EAAU;AAAA,GAAQ,UAAA,OAAiB,eAAA,EAAiB,MAAA,EAAQ,MAAA,GACrE,IAAA,IAAO,CAAA,EAAG,eAAA,KAAoB,eAAA,EAC9B,UAAA,aACC,SAAA,CAAU,eAAA,eAA8B,MAAA;;;;AAjCrC;iBAoEG,MAAA,qBACP,UAAA,EAAY,OAAA,WAAkB,MAAA,EAAQ,MAAA,GACtC,EAAA,EAAI,SAAA,CAAU,IAAA,kBACd,KAAA,eACK,IAAA,EAAM,IAAA"}
@@ -1,4 +1,4 @@
1
- import { r as runDispatch } from "./util-5PSJsovA.js";
1
+ import { i as runDispatch } from "./util-Cw64MseZ.js";
2
2
  //#region src/supervisor.ts
3
3
  /**
4
4
  * A supervisor process. Call `attach` to send it `RUN` messages —
@@ -1 +1 @@
1
- {"version":3,"file":"supervisor.js","names":[],"sources":["../src/supervisor.ts"],"sourcesContent":["import { runDispatch } from \"./util.js\";\nimport type { Process, ProcessFn, ProcessCtx } from \"./process.js\";\nimport type { ExitMessage } from \"./util.js\";\n\n// ---- messages ---------------------------------------------------------------\n\ntype SupMsg = ExitMessage | RunMsg | StopMsg | ErrorMsg | OkMsg;\n\ninterface RunMsg {\n type: \"RUN\";\n fn: ProcessFn<any, any, any, any>;\n args: any[];\n pname: string;\n}\n\ninterface StopMsg {\n type: \"STOP\";\n}\n\ninterface ErrorMsg {\n type: \"ERROR\";\n}\n\ninterface OkMsg {\n type: \"OK\";\n [key: string]: unknown;\n}\n\n// ---- state ------------------------------------------------------------------\n\nexport interface SupervisorState {\n processes: Process<any, any, any, any>[];\n phase: \"wait\" | \"running\" | \"stopping\";\n}\n\n// ---- supervise --------------------------------------------------------------\n\n/**\n * A supervisor process. Call `attach` to send it `RUN` messages —\n * it will fork the given function as a child. The supervisor exits\n * when it enters `'stopping'` phase (triggered by `ERROR` or `STOP`).\n *\n * @param wrap optional transformation applied to the state before\n * each yield (e.g. `reactive` from Vue)\n */\nfunction* supervise(\n { pname, toParent, fork }: ProcessCtx<SupMsg, SupMsg>,\n wrap: (s: SupervisorState) => SupervisorState = (a) => a,\n debugLevel = false,\n): Generator<SupervisorState | null, void, SupMsg> {\n const state = wrap({ processes: [], phase: \"wait\" });\n yield state;\n\n yield* runDispatch<SupMsg>(\n pname,\n (msg) => {\n switch (msg.type) {\n case \"RUN\":\n (fork as (...a: any[]) => any)(msg.fn, msg.pname)(...msg.args);\n state.phase = \"running\";\n break;\n case \"ERROR\":\n case \"STOP\":\n state.phase = \"stopping\";\n break;\n case \"EXIT\":\n state.processes = state.processes.filter((p) => p.id !== msg.pid);\n break;\n case \"OK\":\n toParent(msg);\n break;\n }\n },\n () => state.phase === \"stopping\",\n debugLevel,\n );\n}\n\n// ---- attach -----------------------------------------------------------------\n\n/**\n * Bind a process function to a supervisor. The returned function,\n * when called, sends a `RUN` message so the supervisor forks the child.\n */\nfunction attach<Args extends any[]>(\n supervisor: Process<any, any, SupMsg, SupMsg>,\n fn: ProcessFn<Args, any, any, any>,\n pname: string,\n): (...args: Args) => void {\n return (...args) => {\n supervisor.send({ type: \"RUN\", fn, args, pname } as SupMsg);\n };\n}\n\nexport { attach, supervise };\n"],"mappings":";;;;;;;;;;AA6CA,UAAU,UACR,EAAE,OAAO,UAAU,QACnB,QAAiD,MAAM,GACvD,aAAa,OACoC;CACjD,MAAM,QAAQ,KAAK;EAAE,WAAW,CAAC;EAAG,OAAO;CAAO,CAAC;CACnD,MAAM;CAEN,OAAO,YACL,QACC,QAAQ;EACP,QAAQ,IAAI,MAAZ;GACE,KAAK;IACH,KAA+B,IAAI,IAAI,IAAI,KAAK,EAAE,GAAG,IAAI,IAAI;IAC7D,MAAM,QAAQ;IACd;GACF,KAAK;GACL,KAAK;IACH,MAAM,QAAQ;IACd;GACF,KAAK;IACH,MAAM,YAAY,MAAM,UAAU,QAAQ,MAAM,EAAE,OAAO,IAAI,GAAG;IAChE;GACF,KAAK;IACH,SAAS,GAAG;IACZ;EACJ;CACF,SACM,MAAM,UAAU,YACtB,UACF;AACF;;;;;AAQA,SAAS,OACP,YACA,IACA,OACyB;CACzB,QAAQ,GAAG,SAAS;EAClB,WAAW,KAAK;GAAE,MAAM;GAAO;GAAI;GAAM;EAAM,CAAW;CAC5D;AACF"}
1
+ {"version":3,"file":"supervisor.js","names":[],"sources":["../src/supervisor.ts"],"sourcesContent":["import { runDispatch } from \"./util.js\";\nimport type { Process, ProcessFn, ProcessCtx } from \"./process.js\";\nimport type { ExitMessage } from \"./util.js\";\n\n// ---- messages ---------------------------------------------------------------\n\ntype SupMsg = ExitMessage | RunMsg | StopMsg | ErrorMsg | OkMsg;\n\ninterface RunMsg {\n type: \"RUN\";\n fn: ProcessFn<any, any, any, any>;\n args: any[];\n pname: string;\n}\n\ninterface StopMsg {\n type: \"STOP\";\n}\n\ninterface ErrorMsg {\n type: \"ERROR\";\n}\n\ninterface OkMsg {\n type: \"OK\";\n [key: string]: unknown;\n}\n\n// ---- state ------------------------------------------------------------------\n\nexport interface SupervisorState {\n processes: Process<any, any, any, any>[];\n phase: \"wait\" | \"running\" | \"stopping\";\n}\n\n// ---- supervise --------------------------------------------------------------\n\n/**\n * A supervisor process. Call `attach` to send it `RUN` messages —\n * it will fork the given function as a child. The supervisor exits\n * when it enters `'stopping'` phase (triggered by `ERROR` or `STOP`).\n *\n * @param wrap optional transformation applied to the state before\n * each yield (e.g. `reactive` from Vue)\n */\nfunction* supervise(\n { pname, toParent, fork }: ProcessCtx<null, SupervisorState, SupMsg, SupMsg>,\n wrap: (s: SupervisorState) => SupervisorState = (a) => a,\n debugLevel = false,\n): Generator<SupervisorState | null, void, SupMsg> {\n const state = wrap({ processes: [], phase: \"wait\" });\n yield state;\n\n yield* runDispatch<SupMsg>(\n pname,\n (msg) => {\n switch (msg.type) {\n case \"RUN\":\n (fork as (...a: any[]) => any)(msg.fn, msg.pname)(...msg.args);\n state.phase = \"running\";\n break;\n case \"ERROR\":\n case \"STOP\":\n state.phase = \"stopping\";\n break;\n case \"EXIT\":\n state.processes = state.processes.filter((p) => p.id !== msg.pid);\n break;\n case \"OK\":\n toParent(msg);\n break;\n }\n },\n () => state.phase === \"stopping\",\n debugLevel,\n );\n}\n\n// ---- attach -----------------------------------------------------------------\n\n/**\n * Bind a process function to a supervisor. The returned function,\n * when called, sends a `RUN` message so the supervisor forks the child.\n */\nfunction attach<Args extends any[]>(\n supervisor: Process<any, any, SupMsg, SupMsg>,\n fn: ProcessFn<Args, any, any, any>,\n pname: string,\n): (...args: Args) => void {\n return (...args) => {\n supervisor.send({ type: \"RUN\", fn, args, pname } as SupMsg);\n };\n}\n\nexport { attach, supervise };\n"],"mappings":";;;;;;;;;;AA6CA,UAAU,UACR,EAAE,OAAO,UAAU,QACnB,QAAiD,MAAM,GACvD,aAAa,OACoC;CACjD,MAAM,QAAQ,KAAK;EAAE,WAAW,CAAC;EAAG,OAAO;CAAO,CAAC;CACnD,MAAM;CAEN,OAAO,YACL,QACC,QAAQ;EACP,QAAQ,IAAI,MAAZ;GACE,KAAK;IACH,KAA+B,IAAI,IAAI,IAAI,KAAK,EAAE,GAAG,IAAI,IAAI;IAC7D,MAAM,QAAQ;IACd;GACF,KAAK;GACL,KAAK;IACH,MAAM,QAAQ;IACd;GACF,KAAK;IACH,MAAM,YAAY,MAAM,UAAU,QAAQ,MAAM,EAAE,OAAO,IAAI,GAAG;IAChE;GACF,KAAK;IACH,SAAS,GAAG;IACZ;EACJ;CACF,SACM,MAAM,UAAU,YACtB,UACF;AACF;;;;;AAQA,SAAS,OACP,YACA,IACA,OACyB;CACzB,QAAQ,GAAG,SAAS;EAClB,WAAW,KAAK;GAAE,MAAM;GAAO;GAAI;GAAM;EAAM,CAAW;CAC5D;AACF"}
@@ -15,18 +15,6 @@ function* runDispatch(name, fn, readyFn = () => false, debugLevel = false) {
15
15
  fn(msg);
16
16
  }
17
17
  }
18
- /** Wrap a process generator so that on completion it sends `STOP`
19
- * to all children and `EXIT` to the parent. */
20
- function watchExit(process) {
21
- return function* (ctx, arg0) {
22
- yield* process.pgenerator(ctx, arg0);
23
- process.toAllChildren({ type: "STOP" });
24
- process.toParent({
25
- type: "EXIT",
26
- pid: process.id
27
- });
28
- };
29
- }
30
18
  /** Schedule a function to run on the next microtask. Returns an
31
19
  * object with `cancel()` and `flush()` methods. */
32
20
  function defer(fn) {
@@ -59,6 +47,6 @@ function makeWaiter() {
59
47
  };
60
48
  }
61
49
  //#endregion
62
- export { watchExit as i, makeWaiter as n, runDispatch as r, defer as t };
50
+ export { runDispatch as i, defer as n, makeWaiter as r, debugLog as t };
63
51
 
64
- //# sourceMappingURL=util-5PSJsovA.js.map
52
+ //# sourceMappingURL=util-Cw64MseZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util-Cw64MseZ.js","names":[],"sources":["../src/util.ts"],"sourcesContent":["import type { ExitMessage } from \"./types.js\";\nexport function debugLog(level: boolean, ...args: Array<unknown>) {\n if (level) {\n console.log(...args);\n }\n}\n\ntype ReducerClosure<M> = (msg: M) => void;\ntype ReadyFn = () => boolean;\ntype NotifyFn = () => void;\n\n/**\n * Generator helper that loops, yielding `null` and feeding incoming\n * messages to `fn` until `readyFn()` returns true. Used inside\n * process generators to build the main message loop.\n */\nfunction* runDispatch<M>(\n name: string,\n fn: ReducerClosure<M>,\n readyFn: ReadyFn = () => false,\n debugLevel = false,\n): Generator<null, void, M> {\n let msg: M;\n while (!readyFn()) {\n msg = yield null;\n debugLog(debugLevel, \"msg\", name, \" <- \", msg);\n fn(msg);\n }\n}\n\n/**\n * Wrap a process generator so that on completion it sends STOP to\n * all children and EXIT to the parent. Useful for custom process\n * wrappers that need lifecycle management without extending\n * AsyncProcess.\n */\nfunction watchExit<\n A,\n S,\n IM extends { type: string },\n OM extends { type: string } | ExitMessage,\n>(\n proc: {\n toAllChildren: (m: { type: string }) => void;\n toParent: (m: OM) => void;\n id: symbol;\n },\n gen: (ctx: any, args: A) => Generator<S | null, void, IM>,\n): (ctx: any, args: A) => Generator<S | null, void, IM> {\n return function* (ctx, args) {\n try {\n yield* gen(ctx, args);\n } finally {\n proc.toAllChildren({ type: \"STOP\" });\n proc.toParent({ type: \"EXIT\", pid: proc.id } as OM);\n }\n };\n}\n\ntype DeferCb = () => void;\ntype Defer = (fn: DeferCb) => unknown;\ntype Cancel = (taskId: any) => void;\ntype WindowGlobal = {\n setImmediate?: Defer;\n clearImmediate?: Cancel;\n requestIdleCallback?: Defer;\n cancelIdleCallback?: Cancel;\n setTimeout: Defer;\n clearTimeout: Cancel;\n};\n\n/** Handle to a scheduled (but not yet executed) callback. */\nexport type DeferredCall = {\n cancel: () => void;\n flush: () => void;\n};\n/** Schedule a function to run on the next microtask. Returns an\n * object with `cancel()` and `flush()` methods. */\nfunction defer(fn: DeferCb): DeferredCall {\n const g = globalThis as WindowGlobal;\n function schedule(deferFn: Defer, cancelFn: Cancel) {\n const taskId = deferFn(fn);\n return () => cancelFn(taskId);\n }\n let cancel: () => void;\n if (g.setImmediate && g.clearImmediate) {\n cancel = schedule(g.setImmediate, g.clearImmediate);\n } else if (g.requestIdleCallback && g.cancelIdleCallback) {\n cancel = schedule(g.requestIdleCallback, g.cancelIdleCallback);\n } else {\n cancel = schedule((f) => setTimeout(f, 0), clearTimeout);\n }\n\n function flush() {\n cancel();\n fn();\n }\n\n return { flush, cancel };\n}\n/** A promise paired with its resolve function — used to signal\n * process completion. */\nexport type Waiter = {\n promise: Promise<void>;\n resolve: NotifyFn;\n};\n/** Create a new {@link Waiter}. */\nfunction makeWaiter(): Waiter {\n let resolve: unknown;\n let promise = new Promise<void>((_resolve) => {\n resolve = _resolve;\n });\n return { promise, resolve: resolve as NotifyFn };\n}\n\nexport { runDispatch, defer, makeWaiter, watchExit };\nexport type { ExitMessage };\n"],"mappings":";AACA,SAAgB,SAAS,OAAgB,GAAG,MAAsB;CAChE,IAAI,OACF,QAAQ,IAAI,GAAG,IAAI;AAEvB;;;;;;AAWA,UAAU,YACR,MACA,IACA,gBAAyB,OACzB,aAAa,OACa;CAC1B,IAAI;CACJ,OAAO,CAAC,QAAQ,GAAG;EACjB,MAAM,MAAM;EACZ,SAAS,YAAY,OAAO,MAAM,QAAQ,GAAG;EAC7C,GAAG,GAAG;CACR;AACF;;;AAkDA,SAAS,MAAM,IAA2B;CACxC,MAAM,IAAI;CACV,SAAS,SAAS,SAAgB,UAAkB;EAClD,MAAM,SAAS,QAAQ,EAAE;EACzB,aAAa,SAAS,MAAM;CAC9B;CACA,IAAI;CACJ,IAAI,EAAE,gBAAgB,EAAE,gBACtB,SAAS,SAAS,EAAE,cAAc,EAAE,cAAc;MAC7C,IAAI,EAAE,uBAAuB,EAAE,oBACpC,SAAS,SAAS,EAAE,qBAAqB,EAAE,kBAAkB;MAE7D,SAAS,UAAU,MAAM,WAAW,GAAG,CAAC,GAAG,YAAY;CAGzD,SAAS,QAAQ;EACf,OAAO;EACP,GAAG;CACL;CAEA,OAAO;EAAE;EAAO;CAAO;AACzB;;AAQA,SAAS,aAAqB;CAC5B,IAAI;CAIJ,OAAO;EAAE,SAAA,IAHS,SAAe,aAAa;GAC5C,UAAU;EACZ,CACe;EAAY;CAAoB;AACjD"}
@@ -0,0 +1,46 @@
1
+ import { c as ExitMessage, d as ProcessCtx, t as Process } from "./process-DF-plv-v.js";
2
+
3
+ //#region src/xfetch.d.ts
4
+ /** State of an in-flight or completed fetch. */
5
+ type FetchState<T> = {
6
+ code: "pending" | "loading" | "aborted" | "failed" | "ok";
7
+ data: T | null;
8
+ text: string | null;
9
+ };
10
+ /** Arguments for a fetch process. */
11
+ type FetchArgs<T> = {
12
+ url: URL;
13
+ method?: "GET";
14
+ body?: undefined;
15
+ } | {
16
+ url: URL;
17
+ method?: "POST" | "PUT" | "PATCH";
18
+ body: T;
19
+ };
20
+ /** Messages emitted by a fetch process during its lifecycle. */
21
+ type FetchMessage<T> = {
22
+ type: "OK";
23
+ data?: T | null;
24
+ text?: string | null;
25
+ } | {
26
+ type: "ERROR" | "LOADING" | "ABORTED" | "STOP";
27
+ } | ExitMessage;
28
+ /** A process that performs an HTTP fetch. */
29
+ type FetchProcess<D> = Process<FetchArgs<D>, FetchState<D>, FetchMessage<D>, FetchMessage<D>>;
30
+ /**
31
+ * A fetch wrapper implemented as a process. Supports GET/POST/PUT/PATCH,
32
+ * JSON detection, abort via AbortController, and yields a
33
+ * {@link FetchState} with the current status.
34
+ */
35
+ declare function xfetch<Type>({
36
+ pname,
37
+ toParent,
38
+ send
39
+ }: ProcessCtx<FetchMessage<Type>, FetchMessage<Type>>, {
40
+ method,
41
+ url,
42
+ body
43
+ }: FetchArgs<Type>): Generator<FetchState<Type> | null, void, FetchMessage<Type>>;
44
+ //#endregion
45
+ export { FetchArgs, FetchMessage, FetchProcess, FetchState, xfetch };
46
+ //# sourceMappingURL=xfetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xfetch.d.ts","names":[],"sources":["../src/xfetch.ts"],"mappings":";;;;KAOY,UAAA;EACV,IAAA;EACA,IAAA,EAAM,CAAC;EACP,IAAA;AAAA;;KAIU,SAAA;EACN,GAAA,EAAK,GAAA;EAAK,MAAA;EAAgB,IAAA;AAAA;EAC1B,GAAA,EAAK,GAAA;EAAK,MAAA;EAAmC,IAAA,EAAM,CAAA;AAAA;;KAG7C,YAAA;EACN,IAAA;EAAY,IAAA,GAAO,CAAA;EAAU,IAAA;AAAA;EAC7B,IAAA;AAAA,IACF,WAAW;;KAGH,YAAA,MAAkB,OAAA,CAC5B,SAAA,CAAU,CAAA,GACV,UAAA,CAAW,CAAA,GACX,YAAA,CAAa,CAAA,GACb,YAAA,CAAa,CAAA;;;;;;iBAiBL,MAAA;EACN,KAAA;EAAO,QAAA;EAAU;AAAA,GAAQ,UAAA,CAAW,YAAA,CAAa,IAAA,GAAO,YAAA,CAAa,IAAA;EACrE,MAAA;EAAgB,GAAA;EAAK;AAAA,GAAQ,SAAA,CAAU,IAAA,IACxC,SAAA,CAAU,UAAA,CAAW,IAAA,gBAAoB,YAAA,CAAa,IAAA"}
package/dist/xfetch.js ADDED
@@ -0,0 +1,69 @@
1
+ import { i as runDispatch } from "./util-Cw64MseZ.js";
2
+ //#region src/xfetch.ts
3
+ function isJsonHelper(res) {
4
+ return res.headers.get("content-type") === "application/json";
5
+ }
6
+ /**
7
+ * A fetch wrapper implemented as a process. Supports GET/POST/PUT/PATCH,
8
+ * JSON detection, abort via AbortController, and yields a
9
+ * {@link FetchState} with the current status.
10
+ */
11
+ function* xfetch({ pname, toParent, send }, { method = "GET", url, body }) {
12
+ const state = {
13
+ code: "pending",
14
+ data: null,
15
+ text: null
16
+ };
17
+ yield state;
18
+ const controller = new AbortController();
19
+ const signal = controller.signal;
20
+ const toSelf = send;
21
+ (async function doRequest() {
22
+ try {
23
+ toSelf({ type: "LOADING" });
24
+ const serializedBody = method === "GET" ? void 0 : JSON.stringify(body);
25
+ const headers = new Headers({});
26
+ if (serializedBody) headers.set("content-type", "application/json");
27
+ const res = await fetch(url.href, {
28
+ method,
29
+ signal,
30
+ body: serializedBody,
31
+ headers
32
+ });
33
+ if (isJsonHelper(res)) toSelf({
34
+ type: "OK",
35
+ data: await res.json()
36
+ });
37
+ else toSelf({
38
+ type: "OK",
39
+ text: await res.text()
40
+ });
41
+ } catch (e) {
42
+ if (e instanceof DOMException && e.name === "AbortError") toSelf({ type: "ABORTED" });
43
+ else toSelf({ type: "ERROR" });
44
+ }
45
+ })();
46
+ const isDone = () => !(state.code === "pending" || state.code === "loading");
47
+ yield* runDispatch(pname, (msg) => {
48
+ if (msg.type === "STOP") controller.abort();
49
+ if (msg.type === "ABORTED") {
50
+ toParent(msg);
51
+ state.code = "aborted";
52
+ }
53
+ if (msg.type === "ERROR") {
54
+ toParent(msg);
55
+ state.code = "failed";
56
+ }
57
+ if (msg.type === "LOADING") state.code = "loading";
58
+ if (msg.type === "OK") {
59
+ toParent(msg);
60
+ state.code = "ok";
61
+ state.data = msg.data || null;
62
+ state.text = msg.text || null;
63
+ }
64
+ }, isDone, true);
65
+ }
66
+ //#endregion
67
+ export { xfetch };
68
+
69
+ //# sourceMappingURL=xfetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xfetch.js","names":[],"sources":["../src/xfetch.ts"],"sourcesContent":["import { runDispatch } from \"./util.js\";\nimport type { ExitMessage, ProcessCtx } from \"./types.js\";\nimport type { Process } from \"./process.js\";\n\n// ---- types ------------------------------------------------------------------\n\n/** State of an in-flight or completed fetch. */\nexport type FetchState<T> = {\n code: \"pending\" | \"loading\" | \"aborted\" | \"failed\" | \"ok\";\n data: T | null;\n text: string | null;\n};\n\n/** Arguments for a fetch process. */\nexport type FetchArgs<T> =\n | { url: URL; method?: \"GET\"; body?: undefined }\n | { url: URL; method?: \"POST\" | \"PUT\" | \"PATCH\"; body: T };\n\n/** Messages emitted by a fetch process during its lifecycle. */\nexport type FetchMessage<T> =\n | { type: \"OK\"; data?: T | null; text?: string | null }\n | { type: \"ERROR\" | \"LOADING\" | \"ABORTED\" | \"STOP\" }\n | ExitMessage;\n\n/** A process that performs an HTTP fetch. */\nexport type FetchProcess<D> = Process<\n FetchArgs<D>,\n FetchState<D>,\n FetchMessage<D>,\n FetchMessage<D>\n>;\n\n// ---- helpers ----------------------------------------------------------------\n\nfunction isJsonHelper(res: Response): boolean {\n const ct = res.headers.get(\"content-type\");\n return ct === \"application/json\";\n}\n\n// ---- xfetch -----------------------------------------------------------------\n\n/**\n * A fetch wrapper implemented as a process. Supports GET/POST/PUT/PATCH,\n * JSON detection, abort via AbortController, and yields a\n * {@link FetchState} with the current status.\n */\nfunction* xfetch<Type>(\n { pname, toParent, send }: ProcessCtx<FetchMessage<Type>, FetchMessage<Type>>,\n { method = \"GET\", url, body }: FetchArgs<Type>,\n): Generator<FetchState<Type> | null, void, FetchMessage<Type>> {\n const state: FetchState<Type> = { code: \"pending\", data: null, text: null };\n yield state;\n\n const controller = new AbortController();\n const signal = controller.signal;\n const toSelf = send;\n\n (async function doRequest() {\n try {\n toSelf({ type: \"LOADING\" });\n const serializedBody =\n method === \"GET\" ? undefined : JSON.stringify(body);\n const headers = new Headers({});\n if (serializedBody) {\n headers.set(\"content-type\", \"application/json\");\n }\n const res: Response = await fetch(url.href, {\n method,\n signal,\n body: serializedBody,\n headers,\n });\n if (isJsonHelper(res)) {\n const data = await res.json();\n toSelf({ type: \"OK\", data });\n } else {\n const text = await res.text();\n toSelf({ type: \"OK\", text });\n }\n } catch (e) {\n const isAborted = e instanceof DOMException && e.name === \"AbortError\";\n if (isAborted) {\n toSelf({ type: \"ABORTED\" });\n } else {\n toSelf({ type: \"ERROR\" });\n }\n }\n })();\n\n const isDone = (): boolean =>\n !(state.code === \"pending\" || state.code === \"loading\");\n\n yield* runDispatch<FetchMessage<Type>>(\n pname,\n (msg: FetchMessage<Type>) => {\n if (msg.type === \"STOP\") {\n controller.abort();\n }\n if (msg.type === \"ABORTED\") {\n toParent(msg);\n state.code = \"aborted\";\n }\n if (msg.type === \"ERROR\") {\n toParent(msg);\n state.code = \"failed\";\n }\n if (msg.type === \"LOADING\") {\n state.code = \"loading\";\n }\n if (msg.type === \"OK\") {\n toParent(msg);\n state.code = \"ok\";\n state.data = msg.data || null;\n state.text = msg.text || null;\n }\n },\n isDone,\n true,\n );\n}\n\nexport { xfetch };\n"],"mappings":";;AAkCA,SAAS,aAAa,KAAwB;CAE5C,OADW,IAAI,QAAQ,IAAI,cACnB,MAAM;AAChB;;;;;;AASA,UAAU,OACR,EAAE,OAAO,UAAU,QACnB,EAAE,SAAS,OAAO,KAAK,QACuC;CAC9D,MAAM,QAA0B;EAAE,MAAM;EAAW,MAAM;EAAM,MAAM;CAAK;CAC1E,MAAM;CAEN,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,SAAS,WAAW;CAC1B,MAAM,SAAS;CAEf,CAAC,eAAe,YAAY;EAC1B,IAAI;GACF,OAAO,EAAE,MAAM,UAAU,CAAC;GAC1B,MAAM,iBACJ,WAAW,QAAQ,KAAA,IAAY,KAAK,UAAU,IAAI;GACpD,MAAM,UAAU,IAAI,QAAQ,CAAC,CAAC;GAC9B,IAAI,gBACF,QAAQ,IAAI,gBAAgB,kBAAkB;GAEhD,MAAM,MAAgB,MAAM,MAAM,IAAI,MAAM;IAC1C;IACA;IACA,MAAM;IACN;GACF,CAAC;GACD,IAAI,aAAa,GAAG,GAElB,OAAO;IAAE,MAAM;IAAM,MAAA,MADF,IAAI,KAAK;GACF,CAAC;QAG3B,OAAO;IAAE,MAAM;IAAM,MAAA,MADF,IAAI,KAAK;GACF,CAAC;EAE/B,SAAS,GAAG;GAEV,IADkB,aAAa,gBAAgB,EAAE,SAAS,cAExD,OAAO,EAAE,MAAM,UAAU,CAAC;QAE1B,OAAO,EAAE,MAAM,QAAQ,CAAC;EAE5B;CACF,GAAG;CAEH,MAAM,eACJ,EAAE,MAAM,SAAS,aAAa,MAAM,SAAS;CAE/C,OAAO,YACL,QACC,QAA4B;EAC3B,IAAI,IAAI,SAAS,QACf,WAAW,MAAM;EAEnB,IAAI,IAAI,SAAS,WAAW;GAC1B,SAAS,GAAG;GACZ,MAAM,OAAO;EACf;EACA,IAAI,IAAI,SAAS,SAAS;GACxB,SAAS,GAAG;GACZ,MAAM,OAAO;EACf;EACA,IAAI,IAAI,SAAS,WACf,MAAM,OAAO;EAEf,IAAI,IAAI,SAAS,MAAM;GACrB,SAAS,GAAG;GACZ,MAAM,OAAO;GACb,MAAM,OAAO,IAAI,QAAQ;GACzB,MAAM,OAAO,IAAI,QAAQ;EAC3B;CACF,GACA,QACA,IACF;AACF"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "posipaki",
4
- "version": "0.6.4",
4
+ "version": "0.7.1",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "files": [
@@ -19,5 +19,15 @@
19
19
  "tsdown": "^0.22.1",
20
20
  "typescript": "^6.0.3",
21
21
  "vitest": "^4.1.7"
22
+ },
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "default": "./dist/index.js"
27
+ },
28
+ "./xfetch": {
29
+ "types": "./dist/xfetch.d.ts",
30
+ "default": "./dist/xfetch.js"
31
+ }
22
32
  }
23
33
  }
@@ -1,100 +0,0 @@
1
- //#region src/util.d.ts
2
- type ReducerClosure<M> = (msg: M) => void;
3
- type ReadyFn = () => boolean;
4
- /**
5
- * Generator helper that loops, yielding `null` and feeding incoming
6
- * messages to `fn` until `readyFn()` returns true. Used inside
7
- * process generators to build the main message loop.
8
- */
9
- declare function runDispatch<M>(name: string, fn: ReducerClosure<M>, readyFn?: ReadyFn, debugLevel?: boolean): Generator<null, void, M>;
10
- /** Message emitted by a process to its parent when it terminates. */
11
- type ExitMessage = {
12
- type: 'EXIT';
13
- pid: symbol;
14
- };
15
- //#endregion
16
- //#region src/process.d.ts
17
- /**
18
- * Base message type. All messages must include a `type` field
19
- * for discrimination in reducers.
20
- */
21
- interface Message {
22
- type: string;
23
- }
24
- type ProcessGenerator<ProcessState, InMessage> = Generator<ProcessState | null, void, InMessage>;
25
- type Fork = <ChildArgs, ChildState, InMessage extends Message, OutMessage extends Message>(fn: ProcessFn<ChildArgs, ChildState, InMessage, OutMessage>, pname: string) => (args: ChildArgs) => Process<ChildArgs, ChildState, InMessage, OutMessage>;
26
- /**
27
- * A process generator function. Receives a {@link ProcessCtx} and
28
- * initial args, yields state (or `null`) at each step.
29
- */
30
- type ProcessFn<Args, State, InMessage, OutMessage> = (ctx: ProcessCtx<InMessage, OutMessage>, args: Args) => ProcessGenerator<State, InMessage>;
31
- type ProcessMessageCb<M> = (msg: M) => void;
32
- /**
33
- * Context injected into every running process. Provides the process
34
- * name, ability to fork children, and I/O channels.
35
- */
36
- type ProcessCtx<IM, OM> = {
37
- pname: string;
38
- fork: Fork;
39
- send: (msg: IM) => void;
40
- toParent: ProcessMessageCb<OM>;
41
- };
42
- type NotifyFn = () => void;
43
- /**
44
- * Spawn a new process from a process function. Returns a curried
45
- * function: call it with initial args to start execution.
46
- */
47
- declare function spawn<Args, State, InMessage extends Message = Message, OutMessage extends Message = ExitMessage>(fn: ProcessFn<Args, State, InMessage, OutMessage>, pname: string, toParent?: ProcessMessageCb<OutMessage>): (args: Args) => Process<Args, State, InMessage, OutMessage>;
48
- /**
49
- * A running process — an actor with message-passing, child processes,
50
- * and observable state. Driven by a generator function that yields
51
- * state snapshots and receives messages via `yield` expressions.
52
- */
53
- declare class Process<Args, State, InMessage extends Message, OutMessage extends Message> {
54
- pgenerator: ProcessFn<Args, State, InMessage, OutMessage>;
55
- pname: string;
56
- toParent: ProcessMessageCb<OutMessage>;
57
- id: symbol;
58
- state: State | null;
59
- private current;
60
- private buffer;
61
- private nextTick;
62
- private children;
63
- private subscribers;
64
- private exitWaiter;
65
- private _isPaused;
66
- constructor(fn: ProcessFn<Args, State, InMessage, OutMessage>, pname: string, toParent: ProcessMessageCb<OutMessage> | undefined);
67
- /** Kick off the generator with initial arguments. */
68
- start(arg0: Args): void;
69
- /** Fork a child process. Children forward messages to this process
70
- * and send `EXIT` when they terminate. */
71
- fork<ChildArgs, ChildState, ChildIM extends Message, ChildOM extends Message>(fn: ProcessFn<ChildArgs, ChildState, ChildIM, ChildOM>, pname: string): (args: ChildArgs) => Process<ChildArgs, ChildState, ChildIM, ChildOM>;
72
- _tick(): void;
73
- _eatResult(ret: IteratorResult<State | null, void> | null): void;
74
- /** Send a message to every child process (used for `STOP` propagation). */
75
- toAllChildren(msg: Message): void;
76
- /** Send a message to this process. Messages are buffered and
77
- * processed asynchronously via a microtask. */
78
- send(msg: InMessage): void;
79
- /** Synchronously flush the message buffer. Useful in tests. */
80
- tick(): void;
81
- _scheduleTick(): void;
82
- notify(): void;
83
- /** Whether any subscribers are watching for state changes. */
84
- get isListenedTo(): boolean;
85
- /** Subscribe to state changes. Returns an unsubscribe function. */
86
- subscribe(f: NotifyFn): () => void;
87
- /** Pause message processing. Incoming messages are buffered
88
- * but not processed until {@link resume} is called. */
89
- pause(): void;
90
- /** Resume processing after a {@link pause}. */
91
- resume(): void;
92
- /** Return a promise that resolves when the generator completes. */
93
- wait(): Promise<void>;
94
- /** Handle a message forwarded from a child process. `EXIT` removes
95
- * the child; all other messages are forwarded to `send`. */
96
- fromChild(msg: InMessage): void;
97
- }
98
- //#endregion
99
- export { spawn as a, ProcessFn as i, Process as n, ExitMessage as o, ProcessCtx as r, runDispatch as s, Message as t };
100
- //# sourceMappingURL=process-BlUe6Luh.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"process-BlUe6Luh.d.ts","names":[],"sources":["../src/util.ts","../src/process.ts"],"mappings":";KASK,cAAA,OAAqB,GAAA,EAAK,CAAC;AAAA,KAC3B,OAAA;;;;;AAD2B;iBAStB,WAAA,IAAe,IAAA,UAAc,EAAA,EAAK,cAAA,CAAe,CAAA,GAAI,OAAA,GAAS,OAAA,EAAsB,UAAA,aAAsB,SAAA,aAAsB,CAAA;;KAU9H,WAAA;EACV,IAAA;EACA,GAAG;AAAA;;;AA7BmD;;;;AAAA,UCYvC,OAAA;EACf,IAAI;AAAA;AAAA,KAGD,gBAAA,4BAA4C,SAAA,CAC/C,YAAA,eAEA,SAAA;AAAA,KAEG,IAAA,6CAGe,OAAA,qBACC,OAAA,EAEnB,EAAA,EAAI,SAAA,CAAU,SAAA,EAAW,UAAA,EAAY,SAAA,EAAW,UAAA,GAChD,KAAA,cACI,IAAA,EAAM,SAAA,KAAc,OAAA,CAAQ,SAAA,EAAW,UAAA,EAAY,SAAA,EAAW,UAAA;ADrBpC;;;;AAAA,KC2BpB,SAAA,wCACV,GAAA,EAAK,UAAA,CAAW,SAAA,EAAW,UAAA,GAC3B,IAAA,EAAM,IAAA,KACH,gBAAA,CAAiB,KAAA,EAAO,SAAA;AAAA,KACxB,gBAAA,OAAuB,GAAA,EAAK,CAAC;;;;;KAMtB,UAAA;EACV,KAAA;EACA,IAAA,EAAM,IAAA;EACN,IAAA,GAAO,GAAA,EAAK,EAAA;EACZ,QAAA,EAAU,gBAAA,CAAiB,EAAA;AAAA;AAAA,KAGxB,QAAA;;;;;iBAOW,KAAA,gCAGI,OAAA,GAAU,OAAA,qBACT,OAAA,GAAU,WAAA,EAE7B,EAAA,EAAI,SAAA,CAAU,IAAA,EAAM,KAAA,EAAO,SAAA,EAAW,UAAA,GACtC,KAAA,UACA,QAAA,GAAW,gBAAA,CAAiB,UAAA,KAEpB,IAAA,EAAM,IAAA,KAAO,OAAA,CAAQ,IAAA,EAAM,KAAA,EAAO,SAAA,EAAW,UAAA;;;;;ADpDoF;cCqErI,OAAA,gCAGc,OAAA,qBACC,OAAA;EAEnB,UAAA,EAAY,SAAA,CAAU,IAAA,EAAM,KAAA,EAAO,SAAA,EAAW,UAAA;EAC9C,KAAA;EACA,QAAA,EAAU,gBAAA,CAAiB,UAAA;EAC3B,EAAA;EACA,KAAA,EAAO,KAAA;EAAA,QAEC,OAAA;EAAA,QACA,MAAA;EAAA,QACA,QAAA;EAAA,QACA,QAAA;EAAA,QACA,WAAA;EAAA,QACA,UAAA;EAAA,QACA,SAAA;cAGN,EAAA,EAAI,SAAA,CAAU,IAAA,EAAM,KAAA,EAAO,SAAA,EAAW,UAAA,GACtC,KAAA,UACA,QAAA,EAAU,gBAAA,CAAiB,UAAA;EA7FV;EA+GnB,KAAA,CAAM,IAAA,EAAM,IAAA;EA9GZ;;EA8HA,IAAA,wCAA4C,OAAA,kBAAyB,OAAA,EACnE,EAAA,EAAI,SAAA,CAAU,SAAA,EAAW,UAAA,EAAY,OAAA,EAAS,OAAA,GAC9C,KAAA,YAGE,IAAA,EAAM,SAAA,KACL,OAAA,CAAQ,SAAA,EAAW,UAAA,EAAY,OAAA,EAAS,OAAA;EAiB7C,KAAA;EAgBA,UAAA,CAAW,GAAA,EAAK,cAAA,CAAe,KAAA;EAtKX;EA6KpB,aAAA,CAAc,GAAA,EAAK,OAAA;EA7K4B;;EAmL/C,IAAA,CAAK,GAAA,EAAK,SAAA;EAhLD;EAsLT,IAAA;EAKA,aAAA;EAYA,MAAA;EArMO;EAAA,IA0MH,YAAA;EAtMe;EA2MnB,SAAA,CAAU,CAAA,EAAG,QAAA;EAzMY;;EAsNzB,KAAA;EAtNI;EA6NJ,MAAA;EA3NgC;EAiOhC,IAAA,IAAI,OAAA;EAjOmD;;EAuOvD,SAAA,CAAU,GAAA,EAAK,SAAA;AAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"util-5PSJsovA.js","names":[],"sources":["../src/util.ts"],"sourcesContent":["import type { Process } from './process.js';\nimport type { ProcessCtx, Message } from './process.js';\n\nfunction debugLog(level: boolean, ...args: Array<unknown>) {\n if (level) {\n console.log(...args);\n }\n}\n\ntype ReducerClosure<M> = (msg: M) => void;\ntype ReadyFn = () => boolean;\ntype NotifyFn = () => void;\n\n/**\n * Generator helper that loops, yielding `null` and feeding incoming\n * messages to `fn` until `readyFn()` returns true. Used inside\n * process generators to build the main message loop.\n */\nfunction* runDispatch<M>(name: string, fn : ReducerClosure<M>, readyFn: ReadyFn = ()=> false, debugLevel = false) : Generator<null, void, M> {\n let msg: M;\n while(!readyFn()) {\n msg = yield null;\n debugLog(debugLevel, 'msg', name, ' <- ', msg);\n fn(msg);\n }\n}\n\n/** Message emitted by a process to its parent when it terminates. */\nexport type ExitMessage = {\n type: 'EXIT';\n pid: symbol;\n};\n\n/** Wrap a process generator so that on completion it sends `STOP`\n * to all children and `EXIT` to the parent. */\nfunction watchExit<Args, State, InMessage extends Message, OutMessage extends (Message | ExitMessage)>(process: Process<Args, State, InMessage, OutMessage>) {\n return function* (ctx: ProcessCtx<InMessage, OutMessage>, arg0: Args) {\n yield* process.pgenerator(ctx, arg0);\n process.toAllChildren({ type: 'STOP' });\n process.toParent({ type: 'EXIT', pid: process.id} as OutMessage);\n }\n}\n\ntype DeferCb = () => void;\ntype Defer = (fn: DeferCb) => unknown;\ntype Cancel = (taskId: any)=> void;\ntype WindowGlobal = {\n setImmediate?: Defer,\n clearImmediate?: Cancel,\n requestIdleCallback?: Defer,\n cancelIdleCallback?: Cancel,\n setTimeout: Defer,\n clearTimeout: Cancel,\n};\n\n/** Handle to a scheduled (but not yet executed) callback. */\nexport type DeferredCall = {\n cancel: ()=> void,\n flush: () => void,\n};\n/** Schedule a function to run on the next microtask. Returns an\n * object with `cancel()` and `flush()` methods. */\nfunction defer(fn: DeferCb) : DeferredCall {\n const g = globalThis as WindowGlobal;\n function schedule(deferFn: Defer, cancelFn: Cancel) {\n const taskId = deferFn(fn);\n return ()=> cancelFn(taskId);\n }\n let cancel : ()=> void;\n if (g.setImmediate && g.clearImmediate) {\n cancel = schedule(g.setImmediate, g.clearImmediate);\n } else if (g.requestIdleCallback && g.cancelIdleCallback) {\n cancel = schedule(g.requestIdleCallback, g.cancelIdleCallback);\n } else {\n cancel = schedule((f) => setTimeout(f, 0), clearTimeout);\n }\n\n function flush() {\n cancel();\n fn();\n }\n\n return { flush, cancel };\n}\n/** A promise paired with its resolve function — used to signal\n * process completion. */\nexport type Waiter = {\n promise: Promise<void>;\n resolve: NotifyFn;\n};\n/** Create a new {@link Waiter}. */\nfunction makeWaiter() : Waiter {\n let resolve: unknown;\n let promise = new Promise<void>(_resolve => {\n resolve = _resolve;\n });\n return {promise, resolve: resolve as NotifyFn};\n}\n\nexport { runDispatch, watchExit, defer, makeWaiter };\n"],"mappings":";AAGA,SAAS,SAAS,OAAgB,GAAG,MAAsB;CACzD,IAAI,OACF,QAAQ,IAAI,GAAG,IAAI;AAEvB;;;;;;AAWA,UAAU,YAAe,MAAc,IAAwB,gBAAwB,OAAO,aAAa,OAAkC;CAC3I,IAAI;CACJ,OAAM,CAAC,QAAQ,GAAG;EAChB,MAAM,MAAM;EACZ,SAAS,YAAY,OAAO,MAAM,QAAQ,GAAG;EAC7C,GAAG,GAAG;CACR;AACF;;;AAUA,SAAS,UAA8F,SAAsD;CAC3J,OAAO,WAAW,KAAwC,MAAY;EACpE,OAAO,QAAQ,WAAW,KAAK,IAAI;EACnC,QAAQ,cAAc,EAAE,MAAM,OAAO,CAAC;EACtC,QAAQ,SAAS;GAAE,MAAM;GAAQ,KAAK,QAAQ;EAAE,CAAe;CACjE;AACF;;;AAqBA,SAAS,MAAM,IAA4B;CACzC,MAAM,IAAI;CACV,SAAS,SAAS,SAAgB,UAAkB;EAClD,MAAM,SAAS,QAAQ,EAAE;EACzB,aAAY,SAAS,MAAM;CAC7B;CACA,IAAI;CACJ,IAAI,EAAE,gBAAgB,EAAE,gBACtB,SAAS,SAAS,EAAE,cAAc,EAAE,cAAc;MAC7C,IAAI,EAAE,uBAAuB,EAAE,oBACpC,SAAS,SAAS,EAAE,qBAAqB,EAAE,kBAAkB;MAE7D,SAAS,UAAU,MAAM,WAAW,GAAG,CAAC,GAAG,YAAY;CAGzD,SAAS,QAAQ;EACf,OAAO;EACP,GAAG;CACL;CAEA,OAAO;EAAE;EAAO;CAAO;AACzB;;AAQA,SAAS,aAAsB;CAC7B,IAAI;CAIJ,OAAO;EAAC,SAAA,IAHU,SAAc,aAAY;GAC1C,UAAU;EACZ,CACc;EAAY;CAAmB;AAC/C"}