posipaki 0.10.3 → 0.11.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/README.md +139 -10
- package/dist/index.d.ts +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +53 -37
- package/dist/index.js.map +1 -1
- package/dist/pipe.d.ts +1 -1
- package/dist/pipe.js +3 -2
- package/dist/pipe.js.map +1 -1
- package/dist/{process-C-ERBQY5.d.ts → process-BiX7Ena4.d.ts} +39 -22
- package/dist/process-BiX7Ena4.d.ts.map +1 -0
- package/dist/supervisor.d.ts +2 -2
- package/dist/supervisor.d.ts.map +1 -1
- package/dist/supervisor.js +6 -2
- package/dist/supervisor.js.map +1 -1
- package/dist/util-Cw64MseZ.js.map +1 -1
- package/dist/xfetch.d.ts +3 -3
- package/dist/xfetch.d.ts.map +1 -1
- package/dist/xfetch.js +4 -3
- package/dist/xfetch.js.map +1 -1
- package/package.json +1 -1
- package/dist/process-C-ERBQY5.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,19 +1,148 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Posipaki
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The missing primitive: actor processes for JavaScript, built on generator functions
|
|
4
|
+
and the `[msg, sender]` tuple.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
## Why
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
JavaScript has three ways to model async work:
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
- **Promises** — model a single eventual value. Great for request/response, useless
|
|
11
|
+
for something with more than one outcome.
|
|
12
|
+
- **Observables / Streams** — model a sequence of values over time. Great for
|
|
13
|
+
events, but every subscriber sees the same events and there's no back-and-forth.
|
|
14
|
+
- **Stores / Reactive state** — model a value that changes over time. Great for UI
|
|
15
|
+
state, but they're passive — you read them, they don't talk back.
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
What's missing is a primitive for something that **receives messages, updates its
|
|
18
|
+
own state, and sends messages back** — a thing with its own lifecycle, can be started
|
|
19
|
+
and aborted before it resolves.
|
|
12
20
|
|
|
13
|
-
|
|
21
|
+
A **process**.
|
|
14
22
|
|
|
15
|
-
|
|
23
|
+
```ts
|
|
24
|
+
const counter = defineActor({
|
|
25
|
+
initialState: { count: 0 },
|
|
26
|
+
handlers: {
|
|
27
|
+
POKE(msg, sender) {
|
|
28
|
+
this.state.count++;
|
|
29
|
+
if (this.state.count >= 10) {
|
|
30
|
+
this.emit({ type: "FULL" });
|
|
31
|
+
this.exit("limit reached");
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
RESET(msg, sender) {
|
|
35
|
+
this.state.count = 0;
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
});
|
|
16
39
|
|
|
17
|
-
|
|
40
|
+
const proc = counter.spawn(null);
|
|
41
|
+
await proc.ready();
|
|
42
|
+
proc.send({ type: "POKE" });
|
|
18
43
|
|
|
19
|
-
|
|
44
|
+
A process can do everything a promise, a stream, or a store can do —
|
|
45
|
+
but it can also fork children, pause/resume, and exit on its own terms.
|
|
46
|
+
And every handler receives the sender's identity in the `[msg, sender]` tuple,
|
|
47
|
+
so you always know *who* sent what.
|
|
48
|
+
Of course processes are composable into trees.
|
|
49
|
+
|
|
50
|
+
## Quick start
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { spawnAsync, defineActor } from "posipaki";
|
|
54
|
+
|
|
55
|
+
const counter = defineActor({
|
|
56
|
+
initialState: { count: 0 },
|
|
57
|
+
handlers: {
|
|
58
|
+
POKE(msg, sender) {
|
|
59
|
+
this.state.count++;
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const proc = counter.spawn(null);
|
|
65
|
+
await proc.ready(); // state is available
|
|
66
|
+
proc.send({ type: "POKE" }); // delivers [msg, sender] to the generator
|
|
67
|
+
// proc.state.count === 1
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
For full control, drop to generators:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { spawnAsync, runDispatchAsync } from "posipaki";
|
|
74
|
+
|
|
75
|
+
async function* counter(ctx, { max }) {
|
|
76
|
+
const state = { count: 0 };
|
|
77
|
+
yield state;
|
|
78
|
+
|
|
79
|
+
yield* runDispatchAsync(ctx.pname, async ([msg, sender]) => {
|
|
80
|
+
if (msg.type === "POKE" && state.count < max) state.count++;
|
|
81
|
+
});
|
|
82
|
+
// ... or wait for the signal to replace it with another generator
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const proc = spawnAsync(counter, "counter")({ max: 3 });
|
|
86
|
+
await proc.ready();
|
|
87
|
+
proc.send({ type: "POKE" });
|
|
88
|
+
await proc.wait();
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Features
|
|
92
|
+
|
|
93
|
+
- **Processes** — sync or async, same API
|
|
94
|
+
- **Child processes** — `ctx.fork(fn, name)(args)` spawns supervised children
|
|
95
|
+
- **Supervisor** — run and monitor named workers
|
|
96
|
+
- **Reactive state** — `proc.subscribe(cb)` notifies on every state change
|
|
97
|
+
- **Pause/resume** — buffer messages while idle
|
|
98
|
+
- **Pipe** — chain processes so each runs after the previous exits
|
|
99
|
+
- **xfetch** — HTTP requests as processes, with abort support
|
|
100
|
+
- **defineActor** — declarative config for structured actors
|
|
101
|
+
|
|
102
|
+
## Sender provenance
|
|
103
|
+
|
|
104
|
+
Every message the generator receives is a `[msg, sender]` tuple:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
const [msg, sender] = yield state;
|
|
108
|
+
// msg: your discriminated message
|
|
109
|
+
// sender: { fromName: string, fromId: symbol }
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Messages you send via `ctx.sendSelf()` or `ctx.toParent()` are stamped automatically.
|
|
113
|
+
|
|
114
|
+
## Install
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
npm install posipaki
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## API
|
|
121
|
+
|
|
122
|
+
### Processes
|
|
123
|
+
|
|
124
|
+
- `spawnAsync(fn, name)(args)` — create an async process
|
|
125
|
+
- `proc.ready()` — wait for initial state
|
|
126
|
+
- `proc.state` — current reactive state
|
|
127
|
+
- `proc.send(msg, sender)` — inject a message from a named sender
|
|
128
|
+
- `proc.subscribe(cb)` — react to state changes
|
|
129
|
+
- `proc.pause()` / `proc.resume()` — buffer or process messages
|
|
130
|
+
- `proc.wait()` — resolve when the generator completes
|
|
131
|
+
|
|
132
|
+
### Generator context
|
|
133
|
+
|
|
134
|
+
- `ctx.pname` — process name
|
|
135
|
+
- `ctx.id` — unique symbol
|
|
136
|
+
- `ctx.sendSelf(msg)` — enqueue a message to yourself
|
|
137
|
+
- `ctx.toParent(msg)` — send a message to the parent process
|
|
138
|
+
- `ctx.fork(fn, name)(args)` — spawn a child process
|
|
139
|
+
|
|
140
|
+
### Sender types
|
|
141
|
+
|
|
142
|
+
- `SenderInfo` — `{ fromName: string, fromId: symbol }`
|
|
143
|
+
- `WithSender<M>` — `[M, SenderInfo]`
|
|
144
|
+
- `WithoutSender<T>` — extracts `M` from `WithSender<M>`
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as runDispatchAsync, c as ExitMessage, d as ProcessCtx, f as ProcessFn, i as AsyncProcess, l as Message, m as
|
|
1
|
+
import { _ as WithSender, a as runDispatchAsync, c as ExitMessage, d as ProcessCtx, f as ProcessFn, g as SupervisorState, h as StopMessage, i as AsyncProcess, l as Message, m as SenderOrigin, n as spawn, o as spawnAsync, p as SenderInfo, r as runDispatch, s as AsyncProcessFn, t as Process, u as PipeState, v as WithoutSender } from "./process-BiX7Ena4.js";
|
|
2
2
|
|
|
3
3
|
//#region src/adapters.d.ts
|
|
4
4
|
declare function asyncify<A, S, IM extends Message, OM extends Message>(fn: ProcessFn<A, S, IM, OM>): AsyncProcessFn<A, S, IM, OM>;
|
|
@@ -10,13 +10,17 @@ type ActorMessages<M extends Message> = {
|
|
|
10
10
|
interface MethodOptions {
|
|
11
11
|
[key: string]: Function;
|
|
12
12
|
}
|
|
13
|
-
type HandlerFn<InMsg extends Message> = (msg: InMsg) => void | Promise<void>;
|
|
13
|
+
type HandlerFn<InMsg extends Message> = (msg: InMsg, sender: SenderInfo) => void | Promise<void>;
|
|
14
14
|
type HandlerOptions<InMsg extends Message> = Omit<{ [K in InMsg["type"]]: HandlerFn<Extract<InMsg, {
|
|
15
15
|
type: K;
|
|
16
16
|
}>> }, "STOP">;
|
|
17
17
|
interface ActorDefinition<Args, ExposedState, InMsg extends Message, OutMsg extends Message, Handlers extends HandlerOptions<InMsg>> {
|
|
18
18
|
fn: AsyncProcessFn<Args, ExposedState, InMsg, OutMsg>;
|
|
19
19
|
config: ActorConfig<Args, any, ExposedState, InMsg, OutMsg, {}, Handlers>;
|
|
20
|
+
/** Spawn this actor as a standalone process. */
|
|
21
|
+
spawn(args: Args): AsyncProcess<Args, ExposedState, InMsg, OutMsg>;
|
|
22
|
+
/** Spawn this actor as a child of the calling process. */
|
|
23
|
+
spawnAsChild(ctx: ProcessCtx<any, any, any, any>, name: string, args: Args): AsyncProcess<Args, ExposedState, InMsg, OutMsg>;
|
|
20
24
|
}
|
|
21
25
|
interface ActorConfig<Args, InternalState, ExposedState, InMsg extends Message, OutMsg extends Message, Methods extends MethodOptions, Handlers extends HandlerOptions<InMsg>> {
|
|
22
26
|
initialState: InternalState | ((this: void, args: Args, ctx: ActorContext<Args, InternalState, InMsg, OutMsg, Methods, Handlers>["ctx"]) => InternalState);
|
|
@@ -28,7 +32,7 @@ interface ActorConfig<Args, InternalState, ExposedState, InMsg extends Message,
|
|
|
28
32
|
onEnd?: (this: ActorContext<Args, InternalState, InMsg, OutMsg, Methods, Handlers>, reason?: unknown) => void | Promise<void>;
|
|
29
33
|
handlers: Handlers & ThisType<ActorContext<Args, InternalState, InMsg, OutMsg, Methods, Handlers>>;
|
|
30
34
|
methods?: Methods & ThisType<ActorContext<Args, InternalState, InMsg, OutMsg, Methods, Handlers>>;
|
|
31
|
-
onUnhandled?: (this: ActorContext<Args, InternalState, InMsg, OutMsg, Methods, Handlers>, msg: InMsg) => void | Promise<void>;
|
|
35
|
+
onUnhandled?: (this: ActorContext<Args, InternalState, InMsg, OutMsg, Methods, Handlers>, msg: InMsg, sender: SenderInfo) => void | Promise<void>;
|
|
32
36
|
onChildExit?: (this: ActorContext<Args, InternalState, InMsg, OutMsg, Methods, Handlers>, name: string, reason: ExitMessage) => void | Promise<void>;
|
|
33
37
|
}
|
|
34
38
|
type ActorContext<Args, InternalState, InMsg extends Message, OutMsg extends Message, Methods extends MethodOptions, Handlers extends HandlerOptions<InMsg>> = Methods & {
|
|
@@ -47,5 +51,5 @@ type ActorContext<Args, InternalState, InMsg extends Message, OutMsg extends Mes
|
|
|
47
51
|
declare function defineMessages<OutMsg extends Message = Message>(): ActorMessages<OutMsg>;
|
|
48
52
|
declare function defineActor<Args, InternalState, ExposedState, InMsg extends Message, OutMsg extends Message, Methods extends MethodOptions, Handlers extends HandlerOptions<InMsg>>(config: ActorConfig<Args, InternalState, ExposedState, InMsg, OutMsg, Methods, Handlers>): ActorDefinition<Args, ExposedState, InMsg, OutMsg, Handlers>;
|
|
49
53
|
//#endregion
|
|
50
|
-
export { type ActorConfig, type ActorContext, type ActorDefinition, AsyncProcess, type AsyncProcessFn, type ExitMessage, type HandlerFn, type HandlerOptions, type Message, type MethodOptions, type PipeState, Process, type ProcessCtx, type ProcessFn, type StopMessage, type SupervisorState, asyncify, defineActor, defineMessages, runDispatch, runDispatchAsync, spawn, spawnAsync };
|
|
54
|
+
export { type ActorConfig, type ActorContext, type ActorDefinition, AsyncProcess, type AsyncProcessFn, type ExitMessage, type HandlerFn, type HandlerOptions, type Message, type MethodOptions, type PipeState, Process, type ProcessCtx, type ProcessFn, type SenderInfo, type SenderOrigin, type StopMessage, type SupervisorState, type WithSender, type WithoutSender, asyncify, defineActor, defineMessages, runDispatch, runDispatchAsync, spawn, spawnAsync };
|
|
51
55
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/adapters.ts","../src/actor-types.ts","../src/define-actor.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;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/adapters.ts","../src/actor-types.ts","../src/define-actor.ts"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -57,13 +57,21 @@ var AsyncProcess = class AsyncProcess {
|
|
|
57
57
|
* state; for async generators this happens in a microtask.
|
|
58
58
|
*/
|
|
59
59
|
start(arg0) {
|
|
60
|
+
const selfCtx = {
|
|
61
|
+
fromName: this.pname,
|
|
62
|
+
fromId: this.id
|
|
63
|
+
};
|
|
60
64
|
const ctx = {
|
|
61
65
|
pname: this.pname,
|
|
62
66
|
id: this.id,
|
|
63
67
|
fork: this.fork.bind(this),
|
|
64
68
|
forkSync: this.forkSync.bind(this),
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
sendSelf: (msg) => {
|
|
70
|
+
this.send([msg, selfCtx]);
|
|
71
|
+
},
|
|
72
|
+
toParent: (msg) => {
|
|
73
|
+
this.toParent([msg, selfCtx]);
|
|
74
|
+
}
|
|
67
75
|
};
|
|
68
76
|
this.current = this._watchExit(ctx, arg0);
|
|
69
77
|
this.current.next().then((ret) => {
|
|
@@ -73,7 +81,11 @@ var AsyncProcess = class AsyncProcess {
|
|
|
73
81
|
this.exitWaiter.resolve();
|
|
74
82
|
return;
|
|
75
83
|
}
|
|
76
|
-
|
|
84
|
+
const advance = [{ type: "__ADVANCE__" }, {
|
|
85
|
+
fromName: "__internal__",
|
|
86
|
+
fromId: Symbol("__internal__")
|
|
87
|
+
}];
|
|
88
|
+
this._eatResult(this.current.next(advance));
|
|
77
89
|
});
|
|
78
90
|
return this;
|
|
79
91
|
}
|
|
@@ -83,12 +95,7 @@ var AsyncProcess = class AsyncProcess {
|
|
|
83
95
|
yield* this.pgenerator(ctx, arg0);
|
|
84
96
|
} finally {
|
|
85
97
|
this.toAllChildren({ type: "STOP" });
|
|
86
|
-
|
|
87
|
-
type: "EXIT",
|
|
88
|
-
pid: this.id,
|
|
89
|
-
fromName: this.pname,
|
|
90
|
-
fromId: this.id
|
|
91
|
-
});
|
|
98
|
+
ctx.toParent({ type: "EXIT" });
|
|
92
99
|
}
|
|
93
100
|
}
|
|
94
101
|
fork(fn, pname) {
|
|
@@ -106,10 +113,10 @@ var AsyncProcess = class AsyncProcess {
|
|
|
106
113
|
if (!this.current || this._tickInProgress) return;
|
|
107
114
|
this._tickInProgress = true;
|
|
108
115
|
try {
|
|
109
|
-
let
|
|
116
|
+
let msgAndSender;
|
|
110
117
|
let ret = null;
|
|
111
|
-
while ((
|
|
112
|
-
ret = await this._safeNext(
|
|
118
|
+
while ((msgAndSender = this.buffer.shift()) !== void 0) {
|
|
119
|
+
ret = await this._safeNext(msgAndSender);
|
|
113
120
|
if (!ret || ret.done) break;
|
|
114
121
|
}
|
|
115
122
|
this.notify();
|
|
@@ -122,9 +129,9 @@ var AsyncProcess = class AsyncProcess {
|
|
|
122
129
|
}
|
|
123
130
|
}
|
|
124
131
|
/** Call `.next()` and redirect unhandled rejections. */
|
|
125
|
-
async _safeNext(
|
|
132
|
+
async _safeNext(msgAndSender) {
|
|
126
133
|
try {
|
|
127
|
-
return await this.current.next(
|
|
134
|
+
return await this.current.next(msgAndSender);
|
|
128
135
|
} catch (e) {
|
|
129
136
|
this._exitReject?.(e);
|
|
130
137
|
this._exitReject = null;
|
|
@@ -142,11 +149,15 @@ var AsyncProcess = class AsyncProcess {
|
|
|
142
149
|
}
|
|
143
150
|
/** Broadcast a message to all children. */
|
|
144
151
|
toAllChildren(msg) {
|
|
145
|
-
|
|
152
|
+
const stamp = {
|
|
153
|
+
fromName: this.pname,
|
|
154
|
+
fromId: this.id
|
|
155
|
+
};
|
|
156
|
+
this.children.forEach((p) => p.send([msg, stamp]));
|
|
146
157
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
this.buffer.push(
|
|
158
|
+
send(msgOrTuple, from) {
|
|
159
|
+
if (from !== void 0) this.buffer.push([msgOrTuple, from]);
|
|
160
|
+
else this.buffer.push(msgOrTuple);
|
|
150
161
|
this._scheduleTick();
|
|
151
162
|
}
|
|
152
163
|
/**
|
|
@@ -205,9 +216,12 @@ var AsyncProcess = class AsyncProcess {
|
|
|
205
216
|
});
|
|
206
217
|
});
|
|
207
218
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
219
|
+
/** Relays a child's message to this process. The message already carries
|
|
220
|
+
* sender provenance (stamped by the child's `ctx.toParent` wrapper). */
|
|
221
|
+
fromChild(msgAndSender) {
|
|
222
|
+
const [msg, sender] = msgAndSender;
|
|
223
|
+
if (msg.type === "EXIT") this.children = this.children.filter((p) => p.id !== sender.fromId);
|
|
224
|
+
this.send(msgAndSender);
|
|
211
225
|
}
|
|
212
226
|
};
|
|
213
227
|
/**
|
|
@@ -223,9 +237,12 @@ function spawnAsync(fn, pname, toParent) {
|
|
|
223
237
|
}
|
|
224
238
|
//#endregion
|
|
225
239
|
//#region src/process.ts
|
|
240
|
+
/** @deprecated Use {@link spawnAsync} instead. */
|
|
226
241
|
function spawn(fn, pname, tp) {
|
|
227
242
|
return (a) => new Process(fn, pname, tp).start(a);
|
|
228
243
|
}
|
|
244
|
+
/** @deprecated Use {@link AsyncProcess} instead. Sync processes are
|
|
245
|
+
* wrapped via asyncify internally; spawnAsync handles both. */
|
|
229
246
|
var Process = class extends AsyncProcess {
|
|
230
247
|
constructor(fn, pname, tp) {
|
|
231
248
|
super(asyncify(fn), pname, tp);
|
|
@@ -254,11 +271,7 @@ function defineActor(config) {
|
|
|
254
271
|
name: ctx.pname,
|
|
255
272
|
id: ctx.id,
|
|
256
273
|
emit(msg) {
|
|
257
|
-
ctx.toParent(
|
|
258
|
-
...msg,
|
|
259
|
-
fromName: ctx.pname,
|
|
260
|
-
fromId: ctx.id
|
|
261
|
-
});
|
|
274
|
+
ctx.toParent(msg);
|
|
262
275
|
},
|
|
263
276
|
agreeToStop() {
|
|
264
277
|
exitReason = "stopped";
|
|
@@ -279,7 +292,8 @@ function defineActor(config) {
|
|
|
279
292
|
};
|
|
280
293
|
yield exposedState;
|
|
281
294
|
if (config.onStart) await config.onStart.call(self, args);
|
|
282
|
-
yield* runDispatchAsync(ctx.pname, async (
|
|
295
|
+
yield* runDispatchAsync(ctx.pname, async (stamped) => {
|
|
296
|
+
const [msg, sender] = stamped;
|
|
283
297
|
if (msg.type === "STOP") {
|
|
284
298
|
if (config.onStopRequested) {
|
|
285
299
|
await config.onStopRequested.call(self);
|
|
@@ -297,18 +311,14 @@ function defineActor(config) {
|
|
|
297
311
|
}
|
|
298
312
|
}
|
|
299
313
|
if (msg.type === "EXIT") {
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
if (
|
|
303
|
-
delete self.$child[childName];
|
|
304
|
-
if (config.onChildExit) await config.onChildExit.call(self, childName, exitMsg);
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
314
|
+
const childName = sender.fromName;
|
|
315
|
+
if (childName && self.$child[childName]) delete self.$child[childName];
|
|
316
|
+
if (config.onChildExit) await config.onChildExit.call(self, childName, msg);
|
|
307
317
|
}
|
|
308
318
|
if (msg.type !== "STOP") {
|
|
309
319
|
const handler = config.handlers[msg.type];
|
|
310
|
-
if (handler) await handler.call(self, msg);
|
|
311
|
-
else if (config.onUnhandled) await config.onUnhandled.call(self, msg);
|
|
320
|
+
if (handler) await handler.call(self, msg, sender);
|
|
321
|
+
else if (config.onUnhandled) await config.onUnhandled.call(self, msg, sender);
|
|
312
322
|
}
|
|
313
323
|
if (done) return;
|
|
314
324
|
}, () => done);
|
|
@@ -316,7 +326,13 @@ function defineActor(config) {
|
|
|
316
326
|
};
|
|
317
327
|
return {
|
|
318
328
|
fn,
|
|
319
|
-
config
|
|
329
|
+
config,
|
|
330
|
+
spawn(args) {
|
|
331
|
+
return spawnAsync(fn, "actor")(args);
|
|
332
|
+
},
|
|
333
|
+
spawnAsChild(ctx, name, args) {
|
|
334
|
+
return ctx.fork(fn, name)(args);
|
|
335
|
+
}
|
|
320
336
|
};
|
|
321
337
|
}
|
|
322
338
|
//#endregion
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/adapters.ts","../src/process.async.ts","../src/process.ts","../src/define-actor.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 id: this.id,\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 fromName: this.pname,\n fromId: 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","// ── defineActor — high-level actor wrapper ───────────────────────────────────\n//\n// Compiles a declarative config into an AsyncProcessFn. Built on top of\n// the existing runDispatchAsync / spawnAsync primitives.\n//\n// See docs/proposals/define-actor-proposal.md for the full design.\n\nimport { runDispatchAsync } from \"./process.async.js\";\nimport type { AsyncProcessFn, Message, ExitMessage } from \"./types.js\";\nimport type { AsyncProcess } from \"./process.async.js\";\nimport type {\n ActorDefinition,\n ActorConfig,\n ActorContext,\n MethodOptions,\n ActorMessages,\n HandlerOptions,\n HandlerFn,\n} from \"./actor-types.js\";\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Implementation\n// ═══════════════════════════════════════════════════════════════════════════════\n\nexport function defineMessages<\n OutMsg extends Message = Message,\n>(): ActorMessages<OutMsg> {\n return undefined as unknown as ActorMessages<OutMsg>;\n}\n\nexport function defineActor<\n Args,\n InternalState,\n ExposedState,\n InMsg extends Message,\n OutMsg extends Message,\n Methods extends MethodOptions,\n Handlers extends HandlerOptions<InMsg>,\n>(\n config: ActorConfig<\n Args,\n InternalState,\n ExposedState,\n InMsg,\n OutMsg,\n Methods,\n Handlers\n >,\n): ActorDefinition<Args, ExposedState, InMsg, OutMsg, Handlers> {\n const fn: AsyncProcessFn<Args, ExposedState, InMsg, OutMsg> =\n async function* (ctx, args) {\n let done = false;\n let exitReason: unknown;\n let stopRequested = false;\n\n // Resolve internal state — literal or function of args.\n const rawState: InternalState =\n typeof config.initialState === \"function\"\n ? (\n config.initialState as (\n args: Args,\n ictx: typeof ctx,\n ) => InternalState\n )(args, ctx)\n : config.initialState;\n\n // Apply expose if provided, otherwise identity.\n const exposedState: ExposedState = config.expose\n ? config.expose(rawState)\n : (rawState as unknown as ExposedState);\n\n // Build the actor context.\n const self: ActorContext<\n Args,\n InternalState,\n InMsg,\n OutMsg,\n Methods,\n Handlers\n > = {\n ...((config.methods || {}) as Methods),\n state: rawState,\n name: ctx.pname,\n id: ctx.id,\n emit(msg) {\n ctx.toParent({\n ...msg,\n fromName: ctx.pname,\n fromId: ctx.id,\n } as OutMsg);\n },\n agreeToStop() {\n exitReason = \"stopped\";\n done = true;\n },\n exit(reason) {\n exitReason = reason;\n done = true;\n },\n $child: {},\n fork(childFn, name, childArgs) {\n // Unwrap ActorDefinition, pass to ctx.fork.\n const resolved = typeof childFn === \"function\" ? childFn : childFn.fn;\n const child = ctx.fork(resolved, name)(childArgs!);\n self.$child[name] = child as unknown as AsyncProcess<\n unknown,\n unknown,\n Message,\n Message\n >;\n return child;\n },\n ctx,\n };\n\n // Yield the exposed state — external consumers see this.\n yield exposedState;\n\n // Call onStart with args.\n if (config.onStart) {\n await config.onStart.call(self, args);\n }\n\n // Dispatch loop.\n yield* runDispatchAsync(\n ctx.pname,\n async (msg: InMsg) => {\n // ── Built-in STOP handling ──────────────────────────────────\n if (msg.type === \"STOP\") {\n if (config.onStopRequested) {\n await config.onStopRequested.call(self);\n if (!done) {\n stopRequested = true;\n }\n } else {\n // Default: agree immediately.\n exitReason = \"stopped\";\n done = true;\n }\n return;\n }\n\n // ── Re-offer deferred STOP ──────────────────────────────────\n if (stopRequested && !done) {\n if (config.onStopRequested) {\n await config.onStopRequested.call(self);\n if (!done) {\n stopRequested = true;\n }\n }\n }\n\n // ── Built-in EXIT handling ──────────────────────────────────\n if (msg.type === \"EXIT\") {\n const exitMsg = msg as unknown as ExitMessage;\n const childName = exitMsg.fromName;\n\n if (childName && self.$child[childName]) {\n // Recognized child — consume EXIT here.\n delete self.$child[childName];\n if (config.onChildExit) {\n await config.onChildExit.call(self, childName, exitMsg);\n }\n return;\n }\n // Unrecognized EXIT — fall through to handlers/onUnhandled.\n }\n\n // ── Named handlers ──────────────────────────────────────────\n if (msg.type !== \"STOP\") {\n const handler = config.handlers[\n msg.type as keyof Handlers\n ] as HandlerFn<InMsg>;\n if (handler) {\n await handler.call(self, msg);\n } else if (config.onUnhandled) {\n await config.onUnhandled.call(self, msg);\n }\n // No onUnhandled: silently drop.\n }\n\n if (done) return;\n },\n () => done,\n );\n\n // Call onEnd.\n if (config.onEnd) {\n await config.onEnd.call(self, exitReason ?? \"done\");\n }\n };\n\n return {\n fn,\n config: config as unknown as ActorConfig<\n Args,\n InternalState,\n ExposedState,\n InMsg,\n OutMsg,\n {},\n Handlers\n >,\n };\n}\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,\n ExitMessage,\n StopMessage,\n ProcessFn,\n ProcessCtx,\n AsyncProcessFn,\n PipeState,\n SupervisorState,\n} from \"./types\";\n\nexport { defineActor, defineMessages } from \"./define-actor.js\";\nexport type {\n ActorDefinition,\n ActorConfig,\n ActorContext,\n MethodOptions,\n HandlerOptions,\n HandlerFn,\n} from \"./actor-types.js\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/adapters.ts","../src/process.async.ts","../src/process.ts","../src/define-actor.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 WithSender,\n SenderInfo,\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. Receives `WithSender<InMessage>`. */\ntype AsyncProcessGenerator<ProcessState, InMessage extends Message> =\n AsyncGenerator<ProcessState | null, void, WithSender<InMessage>>;\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 /** Called for every message sent via `ctx.toParent`. Receives `WithSender<OutMessage>`. */\n toParent: ProcessMessageCb<WithSender<OutMessage>>;\n id: symbol;\n state: State | null;\n\n private current: AsyncProcessGenerator<State, InMessage> | null = null;\n /** Every buffered message carries sender provenance. */\n private buffer: Array<WithSender<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<WithSender<OutMessage>> | undefined,\n ) {\n this.pgenerator = fn;\n this.pname = pname;\n this.toParent = toParent || (noop as ProcessMessageCb<WithSender<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 selfCtx: SenderInfo = { fromName: this.pname, fromId: this.id };\n\n const ctx: ProcessCtx<Args, State, InMessage, OutMessage> = {\n pname: this.pname,\n id: this.id,\n fork: this.fork.bind(this),\n forkSync: this.forkSync.bind(this),\n sendSelf: (msg) => {\n this.send([msg, selfCtx]);\n },\n toParent: (msg) => {\n this.toParent([msg, selfCtx] as WithSender<OutMessage>);\n },\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.\n const advance: WithSender<InMessage> = [\n { type: \"__ADVANCE__\" } as InMessage,\n { fromName: \"__internal__\", fromId: Symbol(\"__internal__\") } as SenderInfo,\n ];\n this._eatResult(this.current!.next(advance));\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\" });\n // ctx.toParent stamps sender info into a WithSender tuple\n ctx.toParent({ type: \"EXIT\" } 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<\n WithSender<ChildOM>\n >,\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 msgAndSender: WithSender<InMessage> | undefined;\n let ret: IteratorResult<State | null, void> | null = null;\n while ((msgAndSender = this.buffer.shift()) !== undefined) {\n ret = await this._safeNext(msgAndSender);\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 msgAndSender: WithSender<InMessage>,\n ): Promise<IteratorResult<State | null, void> | null> {\n try {\n return await this.current!.next(msgAndSender);\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 const stamp: SenderInfo = { fromName: this.pname, fromId: this.id };\n this.children.forEach((p) => p.send([msg, stamp] as WithSender<Message>));\n }\n\n /** Stamp and enqueue a message from a named sender (external API). */\n send(msg: InMessage, from: SenderInfo): void;\n /** Enqueue a pre-stamped message (internal: fromChild, toAllChildren). */\n send(msgAndSender: WithSender<InMessage>): void;\n send(msgOrTuple: InMessage | WithSender<InMessage>, from?: SenderInfo): void {\n if (from !== undefined) {\n this.buffer.push([msgOrTuple as InMessage, from]);\n } else {\n this.buffer.push(msgOrTuple as WithSender<InMessage>);\n }\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 /** Relays a child's message to this process. The message already carries\n * sender provenance (stamped by the child's `ctx.toParent` wrapper). */\n private fromChild(msgAndSender: WithSender<InMessage>): void {\n const [msg, sender] = msgAndSender;\n if (msg.type === \"EXIT\") {\n // sender.fromId === child's id (set by child's ctx.toParent wrapper)\n this.children = this.children.filter((p) => p.id !== sender.fromId);\n }\n this.send(msgAndSender);\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<WithSender<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, WithSender, ExitMessage, ProcessFn } from \"./types.js\";\n\nexport type { Message, ProcessFn, ProcessCtx } from \"./types.js\";\nexport { runDispatch } from \"./util.js\";\n\n/** @deprecated Use {@link spawnAsync} instead. */\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: WithSender<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\n/** @deprecated Use {@link AsyncProcess} instead. Sync processes are\n * wrapped via asyncify internally; spawnAsync handles both. */\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: WithSender<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","// ── defineActor — high-level actor wrapper ───────────────────────────────────\n//\n// Compiles a declarative config into an AsyncProcessFn. Built on top of\n// the existing runDispatchAsync / spawnAsync primitives.\n//\n// See docs/proposals/define-actor-proposal.md for the full design.\n\nimport { runDispatchAsync, spawnAsync } from \"./process.async.js\";\nimport type {\n SenderInfo,\n WithSender,\n AsyncProcessFn,\n Message,\n ExitMessage,\n ProcessCtx,\n} from \"./types.js\";\nimport type { AsyncProcess } from \"./process.async.js\";\nimport type {\n ActorDefinition,\n ActorConfig,\n ActorContext,\n MethodOptions,\n ActorMessages,\n HandlerOptions,\n HandlerFn,\n} from \"./actor-types.js\";\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Implementation\n// ═══════════════════════════════════════════════════════════════════════════════\n\nexport function defineMessages<\n OutMsg extends Message = Message,\n>(): ActorMessages<OutMsg> {\n return undefined as unknown as ActorMessages<OutMsg>;\n}\n\nexport function defineActor<\n Args,\n InternalState,\n ExposedState,\n InMsg extends Message,\n OutMsg extends Message,\n Methods extends MethodOptions,\n Handlers extends HandlerOptions<InMsg>,\n>(\n config: ActorConfig<\n Args,\n InternalState,\n ExposedState,\n InMsg,\n OutMsg,\n Methods,\n Handlers\n >,\n): ActorDefinition<Args, ExposedState, InMsg, OutMsg, Handlers> {\n // Internal generator receives WithSender<InMsg> so sender identity\n // is directly accessible in the dispatch loop with zero casts.\n const fn = async function* (\n ctx: ProcessCtx<Args, ExposedState, InMsg, OutMsg>,\n args: Args,\n ): AsyncGenerator<ExposedState | null, void, WithSender<InMsg>> {\n let done = false;\n let exitReason: unknown;\n let stopRequested = false;\n\n // Resolve internal state — literal or function of args.\n const rawState: InternalState =\n typeof config.initialState === \"function\"\n ? (\n config.initialState as (\n args: Args,\n ictx: typeof ctx,\n ) => InternalState\n )(args, ctx)\n : config.initialState;\n\n // Apply expose if provided, otherwise identity.\n const exposedState: ExposedState = config.expose\n ? config.expose(rawState)\n : (rawState as unknown as ExposedState);\n\n // Build the actor context.\n const self: ActorContext<\n Args,\n InternalState,\n InMsg,\n OutMsg,\n Methods,\n Handlers\n > = {\n ...((config.methods || {}) as Methods),\n state: rawState,\n name: ctx.pname,\n id: ctx.id,\n emit(msg) {\n ctx.toParent(msg);\n },\n agreeToStop() {\n exitReason = \"stopped\";\n done = true;\n },\n exit(reason) {\n exitReason = reason;\n done = true;\n },\n $child: {},\n fork(childFn, name, childArgs) {\n // Unwrap ActorDefinition, pass to ctx.fork.\n const resolved = typeof childFn === \"function\" ? childFn : childFn.fn;\n const child = ctx.fork(resolved, name)(childArgs!);\n self.$child[name] = child as unknown as AsyncProcess<\n unknown,\n unknown,\n Message,\n Message\n >;\n return child;\n },\n ctx,\n };\n\n // Yield the exposed state — external consumers see this.\n yield exposedState;\n\n // Call onStart with args.\n if (config.onStart) {\n await config.onStart.call(self, args);\n }\n\n // Dispatch loop..\n yield* runDispatchAsync<WithSender<InMsg | ExitMessage>>(\n ctx.pname,\n async (stamped) => {\n const [msg, sender] = stamped;\n\n // ── Built-in STOP handling ──────────────────────────────────\n if (msg.type === \"STOP\") {\n if (config.onStopRequested) {\n await config.onStopRequested.call(self);\n if (!done) {\n stopRequested = true;\n }\n } else {\n // Default: agree immediately.\n exitReason = \"stopped\";\n done = true;\n }\n return;\n }\n\n // ── Re-offer deferred STOP ──────────────────────────────────\n if (stopRequested && !done) {\n if (config.onStopRequested) {\n await config.onStopRequested.call(self);\n if (!done) {\n stopRequested = true;\n }\n }\n }\n\n // ── Built-in EXIT handling ──────────────────────────────────\n if (msg.type === \"EXIT\") {\n const childName = sender.fromName;\n\n if (childName && self.$child[childName]) {\n // Recognized child — consume EXIT here.\n delete self.$child[childName];\n }\n if (config.onChildExit) {\n await config.onChildExit.call(self, childName, msg as ExitMessage);\n }\n // Unrecognized EXIT — fall through to handlers/onUnhandled.\n }\n\n // ── Named handlers ──────────────────────────────────────────\n if (msg.type !== \"STOP\") {\n const handler = config.handlers[\n msg.type as keyof Handlers\n ] as HandlerFn<InMsg>;\n if (handler) {\n await handler.call(self, msg as InMsg, sender);\n } else if (config.onUnhandled) {\n await config.onUnhandled.call(self, msg as InMsg, sender);\n }\n // No onUnhandled: silently drop.\n }\n\n if (done) return;\n },\n () => done,\n );\n\n // Call onEnd.\n if (config.onEnd) {\n await config.onEnd.call(self, exitReason ?? \"done\");\n }\n };\n\n return {\n fn: fn as AsyncProcessFn<Args, ExposedState, InMsg, OutMsg>,\n config: config as unknown as ActorConfig<\n Args,\n InternalState,\n ExposedState,\n InMsg,\n OutMsg,\n {},\n Handlers\n >,\n spawn(args: Args) {\n return spawnAsync(\n fn as AsyncProcessFn<Args, ExposedState, InMsg, OutMsg>,\n \"actor\",\n )(args);\n },\n spawnAsChild(\n ctx: ProcessCtx<any, any, any, any>,\n name: string,\n args: Args,\n ) {\n return ctx.fork(\n fn as AsyncProcessFn<Args, ExposedState, InMsg, OutMsg>,\n name,\n )(args);\n },\n };\n}\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,\n WithSender,\n WithoutSender,\n ExitMessage,\n StopMessage,\n ProcessFn,\n ProcessCtx,\n AsyncProcessFn,\n PipeState,\n SupervisorState,\n SenderOrigin,\n SenderInfo,\n} from \"./types\";\n\nexport { defineActor, defineMessages } from \"./define-actor.js\";\nexport type {\n ActorDefinition,\n ActorConfig,\n ActorContext,\n MethodOptions,\n HandlerOptions,\n HandlerFn,\n} from \"./actor-types.js\";\n"],"mappings":"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"}
|
package/dist/pipe.d.ts
CHANGED
package/dist/pipe.js
CHANGED
|
@@ -24,13 +24,14 @@ function pipe(fns) {
|
|
|
24
24
|
}
|
|
25
25
|
spawnNext();
|
|
26
26
|
state.running = hasNext();
|
|
27
|
-
yield* runDispatch(ctx.pname, (
|
|
27
|
+
yield* runDispatch(ctx.pname, (maybe) => {
|
|
28
|
+
const [msg, _sender] = maybe;
|
|
28
29
|
if (msg.type === "STOP") {
|
|
29
30
|
state.params = null;
|
|
30
31
|
state.running = false;
|
|
31
32
|
return;
|
|
32
33
|
}
|
|
33
|
-
if (msg.type === "EXIT" &&
|
|
34
|
+
if (msg.type === "EXIT" && _sender.fromId === task.id) if (hasNext()) {
|
|
34
35
|
state.params = {
|
|
35
36
|
...state.params,
|
|
36
37
|
...task.state?.result
|
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 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
|
|
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, WithSender, SenderInfo } 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<WithSender<Message | ExitMessage>>(\n ctx.pname,\n (maybe) => {\n const [msg, _sender] = maybe;\n\n if (msg.type === \"STOP\") {\n state.params = null;\n state.running = false;\n return;\n }\n\n if (msg.type === \"EXIT\" && _sender.fromId === 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,UAAU;GACT,MAAM,CAAC,KAAK,WAAW;GAEvB,IAAI,IAAI,SAAS,QAAQ;IACvB,MAAM,SAAS;IACf,MAAM,UAAU;IAChB;GACF;GAEA,IAAI,IAAI,SAAS,UAAU,QAAQ,WAAW,KAAK,IACjD,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"}
|
|
@@ -4,24 +4,30 @@
|
|
|
4
4
|
interface Message {
|
|
5
5
|
type: string;
|
|
6
6
|
}
|
|
7
|
-
/**
|
|
8
|
-
|
|
9
|
-
* `fromId` and `pid` carry the same symbol value. `fromName` is the
|
|
10
|
-
* process name (same as `ctx.pname`). These fields mirror what
|
|
11
|
-
* `this.emit()` / `ctx.toParent()` stamp on normal messages, so
|
|
12
|
-
* callers can identify the sender of any child message — application
|
|
13
|
-
* or EXIT — by reading `msg.fromName` or `msg.fromId`. */
|
|
14
|
-
type ExitMessage = {
|
|
15
|
-
type: "EXIT"; /** @deprecated Use {@link fromId} instead. */
|
|
16
|
-
pid: symbol;
|
|
7
|
+
/** Sender identity. `fromName` is the process name, `fromId` its symbol. */
|
|
8
|
+
interface SenderInfo {
|
|
17
9
|
fromName: string;
|
|
18
10
|
fromId: symbol;
|
|
11
|
+
}
|
|
12
|
+
/** A message paired with its sender. This is the currency inside the
|
|
13
|
+
* framework — every message in the buffer and every value the generator
|
|
14
|
+
* receives is `WithSender<M>`. */
|
|
15
|
+
type WithSender<M extends Message> = [M, SenderInfo];
|
|
16
|
+
/** Extract the message type from a stamped tuple. */
|
|
17
|
+
type WithoutSender<T extends WithSender<any>> = T[0];
|
|
18
|
+
/** Message emitted by a process to its parent when it terminates.
|
|
19
|
+
* The sender's identity is carried in the {@link SenderInfo} tuple. */
|
|
20
|
+
type ExitMessage = {
|
|
21
|
+
type: "EXIT";
|
|
19
22
|
};
|
|
20
23
|
type StopMessage = {
|
|
21
24
|
type: "STOP";
|
|
22
25
|
};
|
|
23
|
-
type ProcessFn<Args, State, InMessage extends Message, OutMessage extends Message> = (ctx: ProcessCtx<Args, State, InMessage, OutMessage>, args: Args) => Generator<State | null, void, InMessage
|
|
24
|
-
type AsyncProcessFn<Args, State, InMessage extends Message, OutMessage extends Message> = (ctx: ProcessCtx<Args, State, InMessage, OutMessage>, args: Args) => AsyncGenerator<State | null, void, InMessage
|
|
26
|
+
type ProcessFn<Args, State, InMessage extends Message, OutMessage extends Message> = (ctx: ProcessCtx<Args, State, InMessage, OutMessage>, args: Args) => Generator<State | null, void, WithSender<InMessage>>;
|
|
27
|
+
type AsyncProcessFn<Args, State, InMessage extends Message, OutMessage extends Message> = (ctx: ProcessCtx<Args, State, InMessage, OutMessage>, args: Args) => AsyncGenerator<State | null, void, WithSender<InMessage>>;
|
|
28
|
+
/** Origin of a message — either a process context or the literal "root"
|
|
29
|
+
* (used by the system harness to inject messages from outside). */
|
|
30
|
+
type SenderOrigin = ProcessCtx<unknown, unknown, Message, Message> | "root";
|
|
25
31
|
type ProcessMessageCb$1<M> = (msg: M) => void;
|
|
26
32
|
/** Fork a child process. Takes a ProcessFn and a name, returns a
|
|
27
33
|
* curried function that accepts the child's initial args. */
|
|
@@ -29,7 +35,7 @@ type ProcessMessageCb$1<M> = (msg: M) => void;
|
|
|
29
35
|
type ProcessCtx<Args, State, IM extends Message, OM extends Message> = {
|
|
30
36
|
pname: string;
|
|
31
37
|
id: symbol;
|
|
32
|
-
|
|
38
|
+
sendSelf: (msg: IM) => void;
|
|
33
39
|
toParent: ProcessMessageCb$1<OM>;
|
|
34
40
|
} & Pick<AsyncProcess<Args, State, IM, OM>, "fork" | "forkSync">;
|
|
35
41
|
/** State yielded by the pipe process. */
|
|
@@ -66,10 +72,12 @@ type ProcessMessageCb<M> = (msg: M) => void;
|
|
|
66
72
|
declare class AsyncProcess<Args, State, InMessage extends Message, OutMessage extends Message> {
|
|
67
73
|
pgenerator: AsyncProcessFn<Args, State, InMessage, OutMessage>;
|
|
68
74
|
pname: string;
|
|
69
|
-
toParent
|
|
75
|
+
/** Called for every message sent via `ctx.toParent`. Receives `WithSender<OutMessage>`. */
|
|
76
|
+
toParent: ProcessMessageCb<WithSender<OutMessage>>;
|
|
70
77
|
id: symbol;
|
|
71
78
|
state: State | null;
|
|
72
79
|
private current;
|
|
80
|
+
/** Every buffered message carries sender provenance. */
|
|
73
81
|
private buffer;
|
|
74
82
|
private nextTick;
|
|
75
83
|
private children;
|
|
@@ -80,7 +88,7 @@ declare class AsyncProcess<Args, State, InMessage extends Message, OutMessage ex
|
|
|
80
88
|
private _exitReject;
|
|
81
89
|
private _ready;
|
|
82
90
|
private _resolveReady;
|
|
83
|
-
constructor(fn: AsyncProcessFn<Args, State, InMessage, OutMessage>, pname: string, toParent: ProcessMessageCb<OutMessage
|
|
91
|
+
constructor(fn: AsyncProcessFn<Args, State, InMessage, OutMessage>, pname: string, toParent: ProcessMessageCb<WithSender<OutMessage>> | undefined);
|
|
84
92
|
/** Promise that resolves once the initial state is available. */
|
|
85
93
|
ready(): Promise<void>;
|
|
86
94
|
/**
|
|
@@ -98,8 +106,10 @@ declare class AsyncProcess<Args, State, InMessage extends Message, OutMessage ex
|
|
|
98
106
|
private _eatResult;
|
|
99
107
|
/** Broadcast a message to all children. */
|
|
100
108
|
toAllChildren(msg: Message): void;
|
|
101
|
-
/**
|
|
102
|
-
send(msg: InMessage): void;
|
|
109
|
+
/** Stamp and enqueue a message from a named sender (external API). */
|
|
110
|
+
send(msg: InMessage, from: SenderInfo): void;
|
|
111
|
+
/** Enqueue a pre-stamped message (internal: fromChild, toAllChildren). */
|
|
112
|
+
send(msgAndSender: WithSender<InMessage>): void;
|
|
103
113
|
/**
|
|
104
114
|
* Synchronously flush the buffer. For sync processes use {@link Process.tick};
|
|
105
115
|
* for async processes this is **not guaranteed** to process everything
|
|
@@ -117,13 +127,15 @@ declare class AsyncProcess<Args, State, InMessage extends Message, OutMessage ex
|
|
|
117
127
|
* rejects if an unhandled error occurs during message processing.
|
|
118
128
|
*/
|
|
119
129
|
wait(): Promise<void>;
|
|
130
|
+
/** Relays a child's message to this process. The message already carries
|
|
131
|
+
* sender provenance (stamped by the child's `ctx.toParent` wrapper). */
|
|
120
132
|
private fromChild;
|
|
121
133
|
}
|
|
122
134
|
/**
|
|
123
135
|
* Spawn a new async process. Accepts both sync and async process
|
|
124
136
|
* functions — sync ones are automatically wrapped with {@link asyncify}.
|
|
125
137
|
*/
|
|
126
|
-
declare function spawnAsync<Args, State, InMessage extends Message = Message, OutMessage extends Message = ExitMessage>(fn: AsyncProcessFn<Args, State, InMessage, OutMessage>, pname: string, toParent?: ProcessMessageCb<OutMessage
|
|
138
|
+
declare function spawnAsync<Args, State, InMessage extends Message = Message, OutMessage extends Message = ExitMessage>(fn: AsyncProcessFn<Args, State, InMessage, OutMessage>, pname: string, toParent?: ProcessMessageCb<WithSender<OutMessage>>): (args: Args) => AsyncProcess<Args, State, InMessage, OutMessage>;
|
|
127
139
|
//#endregion
|
|
128
140
|
//#region src/util.d.ts
|
|
129
141
|
type ReducerClosure<M> = (msg: M) => void;
|
|
@@ -135,6 +147,8 @@ type ReadyFn = () => boolean;
|
|
|
135
147
|
*/
|
|
136
148
|
declare function runDispatch<M>(name: string, fn: ReducerClosure<M>, readyFn?: ReadyFn, debugLevel?: boolean): Generator<null, void, M>;
|
|
137
149
|
/**
|
|
150
|
+
* @deprecated Use AsyncProcess._watchExit instead.
|
|
151
|
+
*
|
|
138
152
|
* Wrap a process generator so that on completion it sends STOP to
|
|
139
153
|
* all children and EXIT to the parent. Useful for custom process
|
|
140
154
|
* wrappers that need lifecycle management without extending
|
|
@@ -142,12 +156,15 @@ declare function runDispatch<M>(name: string, fn: ReducerClosure<M>, readyFn?: R
|
|
|
142
156
|
*/
|
|
143
157
|
//#endregion
|
|
144
158
|
//#region src/process.d.ts
|
|
145
|
-
|
|
159
|
+
/** @deprecated Use {@link spawnAsync} instead. */
|
|
160
|
+
declare function spawn<A, S, IM extends Message = Message, OM extends Message = ExitMessage>(fn: ProcessFn<A, S, IM, OM>, pname: string, tp?: (m: WithSender<OM>) => void): (a: A) => Process<A, S, IM, OM>;
|
|
161
|
+
/** @deprecated Use {@link AsyncProcess} instead. Sync processes are
|
|
162
|
+
* wrapped via asyncify internally; spawnAsync handles both. */
|
|
146
163
|
declare class Process<A, S, IM extends Message, OM extends Message> extends AsyncProcess<A, S, IM, OM> {
|
|
147
|
-
constructor(fn: ProcessFn<A, S, IM, OM>, pname: string, tp?: (m: OM) => void);
|
|
164
|
+
constructor(fn: ProcessFn<A, S, IM, OM>, pname: string, tp?: (m: WithSender<OM>) => void);
|
|
148
165
|
start(a: A): this;
|
|
149
166
|
tick(): Promise<void>;
|
|
150
167
|
}
|
|
151
168
|
//#endregion
|
|
152
|
-
export { runDispatchAsync as a, ExitMessage as c, ProcessCtx as d, ProcessFn as f, AsyncProcess as i, Message as l,
|
|
153
|
-
//# sourceMappingURL=process-
|
|
169
|
+
export { WithSender as _, runDispatchAsync as a, ExitMessage as c, ProcessCtx as d, ProcessFn as f, SupervisorState as g, StopMessage as h, AsyncProcess as i, Message as l, SenderOrigin as m, spawn as n, spawnAsync as o, SenderInfo as p, runDispatch as r, AsyncProcessFn as s, Process as t, PipeState as u, WithoutSender as v };
|
|
170
|
+
//# sourceMappingURL=process-BiX7Ena4.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-BiX7Ena4.d.ts","names":[],"sources":["../src/types.ts","../src/process.async.ts","../src/util.ts","../src/process.ts"],"mappings":"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"}
|
package/dist/supervisor.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as ExitMessage, d as ProcessCtx, f as ProcessFn, t as Process } from "./process-
|
|
1
|
+
import { _ as WithSender, c as ExitMessage, d as ProcessCtx, f as ProcessFn, t as Process } from "./process-BiX7Ena4.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<null, SupervisorState, 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, WithSender<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.
|
package/dist/supervisor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supervisor.d.ts","names":[],"sources":["../src/supervisor.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"supervisor.d.ts","names":[],"sources":["../src/supervisor.ts"],"mappings":";;;KAOK,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;AAAA;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,UAAA,CAAW,MAAA;;;AAjChD;AAAA;iBAqEG,MAAA,qBACP,UAAA,EAAY,OAAA,WAAkB,MAAA,EAAQ,MAAA,GACtC,EAAA,EAAI,SAAA,CAAU,IAAA,kBACd,KAAA,eACK,IAAA,EAAM,IAAA"}
|
package/dist/supervisor.js
CHANGED
|
@@ -14,7 +14,8 @@ function* supervise({ pname, toParent, fork }, wrap = (a) => a, debugLevel = fal
|
|
|
14
14
|
phase: "wait"
|
|
15
15
|
});
|
|
16
16
|
yield state;
|
|
17
|
-
yield* runDispatch(pname, (
|
|
17
|
+
yield* runDispatch(pname, (maybe) => {
|
|
18
|
+
const [msg, _sender] = maybe;
|
|
18
19
|
switch (msg.type) {
|
|
19
20
|
case "RUN":
|
|
20
21
|
fork(msg.fn, msg.pname)(...msg.args);
|
|
@@ -25,7 +26,7 @@ function* supervise({ pname, toParent, fork }, wrap = (a) => a, debugLevel = fal
|
|
|
25
26
|
state.phase = "stopping";
|
|
26
27
|
break;
|
|
27
28
|
case "EXIT":
|
|
28
|
-
state.processes = state.processes.filter((p) => p.id !==
|
|
29
|
+
state.processes = state.processes.filter((p) => p.id !== _sender.fromId);
|
|
29
30
|
break;
|
|
30
31
|
case "OK":
|
|
31
32
|
toParent(msg);
|
|
@@ -44,6 +45,9 @@ function attach(supervisor, fn, pname) {
|
|
|
44
45
|
fn,
|
|
45
46
|
args,
|
|
46
47
|
pname
|
|
48
|
+
}, {
|
|
49
|
+
fromName: "external",
|
|
50
|
+
fromId: Symbol("external")
|
|
47
51
|
});
|
|
48
52
|
};
|
|
49
53
|
}
|
package/dist/supervisor.js.map
CHANGED
|
@@ -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<null, SupervisorState, SupMsg, SupMsg>,\n wrap: (s: SupervisorState) => SupervisorState = (a) => a,\n debugLevel = false,\n): Generator<SupervisorState | null, void, SupMsg
|
|
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 { WithSender } from \"./types.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, WithSender<SupMsg>> {\n const state = wrap({ processes: [], phase: \"wait\" });\n yield state;\n\n yield* runDispatch<WithSender<SupMsg>>(\n pname,\n (maybe) => {\n const [msg, _sender] = maybe;\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 !== _sender.fromId);\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, { fromName: \"external\", fromId: Symbol(\"external\") });\n };\n}\n\nexport { attach, supervise };\n"],"mappings":";;;;;;;;;;AA8CA,UAAU,UACR,EAAE,OAAO,UAAU,QACnB,QAAiD,MAAM,GACvD,aAAa,OACgD;CAC7D,MAAM,QAAQ,KAAK;EAAE,WAAW,CAAC;EAAG,OAAO;CAAO,CAAC;CACnD,MAAM;CAEN,OAAO,YACL,QACC,UAAU;EACT,MAAM,CAAC,KAAK,WAAW;EACvB,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,QAAQ,MAAM;IACvE;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,GAAa;GAAE,UAAU;GAAY,QAAQ,OAAO,UAAU;EAAE,CAAC;CAClH;AACF"}
|
|
@@ -1 +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 pname: string;\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({\n type: \"EXIT\",\n
|
|
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* @deprecated Use AsyncProcess._watchExit instead.\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 pname: string;\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({\n type: \"EXIT\",\n } 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;;;AAuDA,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"}
|
package/dist/xfetch.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as ExitMessage, d as ProcessCtx, t as Process } from "./process-
|
|
1
|
+
import { _ as WithSender, c as ExitMessage, d as ProcessCtx, t as Process } from "./process-BiX7Ena4.js";
|
|
2
2
|
|
|
3
3
|
//#region src/xfetch.d.ts
|
|
4
4
|
/** State of an in-flight or completed fetch. */
|
|
@@ -41,13 +41,13 @@ type FetchProcess<D> = Process<FetchArgs<D>, FetchState<D>, FetchMessage<D>, Fet
|
|
|
41
41
|
declare function xfetch<Type>({
|
|
42
42
|
pname,
|
|
43
43
|
toParent,
|
|
44
|
-
|
|
44
|
+
sendSelf
|
|
45
45
|
}: ProcessCtx<FetchArgs<Type>, FetchState<Type>, FetchMessage<Type>, FetchMessage<Type>>, {
|
|
46
46
|
method,
|
|
47
47
|
url,
|
|
48
48
|
body,
|
|
49
49
|
headers: callerHeaders
|
|
50
|
-
}: FetchArgs<Type>): Generator<FetchState<Type> | null, void, FetchMessage<Type
|
|
50
|
+
}: FetchArgs<Type>): Generator<FetchState<Type> | null, void, WithSender<FetchMessage<Type>>>;
|
|
51
51
|
//#endregion
|
|
52
52
|
export { FetchArgs, FetchMessage, FetchProcess, FetchState, xfetch };
|
|
53
53
|
//# sourceMappingURL=xfetch.d.ts.map
|
package/dist/xfetch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xfetch.d.ts","names":[],"sources":["../src/xfetch.ts"],"mappings":";;;;KAOY,UAAA;EACV,IAAA;EACA,IAAA,EAAM,CAAA;EACN,IAAA;EACA,MAAA;EACA,eAAA,EAAiB,MAAM;AAAA;;KAIb,SAAA;EAEN,GAAA,EAAK,GAAA;EACL,MAAA;EACA,IAAA;EACA,OAAA,GAAU,MAAA;AAAA;EAGV,GAAA,EAAK,GAAA;EACL,MAAA;EACA,IAAA,EAAM,CAAA;EACN,OAAA,GAAU,MAAA;AAAA;;KAIJ,YAAA;EAEN,IAAA;EACA,IAAA,GAAO,CAAA;EACP,IAAA;EACA,MAAA;EACA,eAAA,EAAiB,MAAA;AAAA;EAEjB,IAAA;AAAA,IACF,WAAA;;KAGQ,YAAA,MAAkB,OAAA,CAC5B,SAAA,CAAU,CAAA,GACV,UAAA,CAAW,CAAA,GACX,YAAA,CAAa,CAAA,GACb,YAAA,CAAa,CAAA;;;;;;iBA0BL,MAAA;EAEN,KAAA;EACA,QAAA;EACA;AAAA,GACC,UAAA,CACD,SAAA,CAAU,IAAA,GACV,UAAA,CAAW,IAAA,GACX,YAAA,CAAa,IAAA,GACb,YAAA,CAAa,IAAA;EAEb,MAAA;EAAgB,GAAA;EAAK,IAAA;EAAM,OAAA,EAAS;AAAA,GAAiB,SAAA,CAAU,IAAA,IAChE,SAAA,CAAU,UAAA,CAAW,IAAA,gBAAoB,YAAA,CAAa,IAAA"}
|
|
1
|
+
{"version":3,"file":"xfetch.d.ts","names":[],"sources":["../src/xfetch.ts"],"mappings":";;;;KAOY,UAAA;EACV,IAAA;EACA,IAAA,EAAM,CAAA;EACN,IAAA;EACA,MAAA;EACA,eAAA,EAAiB,MAAM;AAAA;;KAIb,SAAA;EAEN,GAAA,EAAK,GAAA;EACL,MAAA;EACA,IAAA;EACA,OAAA,GAAU,MAAA;AAAA;EAGV,GAAA,EAAK,GAAA;EACL,MAAA;EACA,IAAA,EAAM,CAAA;EACN,OAAA,GAAU,MAAA;AAAA;;KAIJ,YAAA;EAEN,IAAA;EACA,IAAA,GAAO,CAAA;EACP,IAAA;EACA,MAAA;EACA,eAAA,EAAiB,MAAA;AAAA;EAEjB,IAAA;AAAA,IACF,WAAA;;KAGQ,YAAA,MAAkB,OAAA,CAC5B,SAAA,CAAU,CAAA,GACV,UAAA,CAAW,CAAA,GACX,YAAA,CAAa,CAAA,GACb,YAAA,CAAa,CAAA;;;;;;iBA0BL,MAAA;EAEN,KAAA;EACA,QAAA;EACA;AAAA,GACC,UAAA,CACD,SAAA,CAAU,IAAA,GACV,UAAA,CAAW,IAAA,GACX,YAAA,CAAa,IAAA,GACb,YAAA,CAAa,IAAA;EAEb,MAAA;EAAgB,GAAA;EAAK,IAAA;EAAM,OAAA,EAAS;AAAA,GAAiB,SAAA,CAAU,IAAA,IAChE,SAAA,CAAU,UAAA,CAAW,IAAA,gBAAoB,UAAA,CAAW,YAAA,CAAa,IAAA"}
|
package/dist/xfetch.js
CHANGED
|
@@ -16,7 +16,7 @@ function headersToRecord(h) {
|
|
|
16
16
|
* JSON detection, abort via AbortController, and yields a
|
|
17
17
|
* {@link FetchState} with the current status.
|
|
18
18
|
*/
|
|
19
|
-
function* xfetch({ pname, toParent,
|
|
19
|
+
function* xfetch({ pname, toParent, sendSelf }, { method = "GET", url, body, headers: callerHeaders }) {
|
|
20
20
|
const state = {
|
|
21
21
|
code: "pending",
|
|
22
22
|
data: null,
|
|
@@ -27,7 +27,7 @@ function* xfetch({ pname, toParent, send }, { method = "GET", url, body, headers
|
|
|
27
27
|
yield state;
|
|
28
28
|
const controller = new AbortController();
|
|
29
29
|
const signal = controller.signal;
|
|
30
|
-
const toSelf =
|
|
30
|
+
const toSelf = sendSelf;
|
|
31
31
|
(async function doRequest() {
|
|
32
32
|
try {
|
|
33
33
|
toSelf({ type: "LOADING" });
|
|
@@ -60,7 +60,8 @@ function* xfetch({ pname, toParent, send }, { method = "GET", url, body, headers
|
|
|
60
60
|
}
|
|
61
61
|
})();
|
|
62
62
|
const isDone = () => !(state.code === "pending" || state.code === "loading");
|
|
63
|
-
yield* runDispatch(pname, (
|
|
63
|
+
yield* runDispatch(pname, (maybe) => {
|
|
64
|
+
const [msg, _sender] = maybe;
|
|
64
65
|
if (msg.type === "STOP") controller.abort();
|
|
65
66
|
if (msg.type === "ABORTED") {
|
|
66
67
|
toParent(msg);
|
package/dist/xfetch.js.map
CHANGED
|
@@ -1 +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 status: number;\n responseHeaders: Record<string, string>;\n};\n\n/** Arguments for a fetch process. */\nexport type FetchArgs<T> =\n | {\n url: URL;\n method?: \"GET\";\n body?: undefined;\n headers?: Record<string, string>;\n }\n | {\n url: URL;\n method?: \"POST\" | \"PUT\" | \"PATCH\";\n body: T;\n headers?: Record<string, string>;\n };\n\n/** Messages emitted by a fetch process during its lifecycle. */\nexport type FetchMessage<T> =\n | {\n type: \"OK\";\n data?: T | null;\n text?: string | null;\n status: number;\n responseHeaders: Record<string, string>;\n }\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/** Convert a Headers object to a plain Record. */\nfunction headersToRecord(h: Headers): Record<string, string> {\n const out: Record<string, string> = {};\n h.forEach((v, k) => {\n out[k] = v;\n });\n return out;\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 {\n pname,\n toParent,\n
|
|
1
|
+
{"version":3,"file":"xfetch.js","names":[],"sources":["../src/xfetch.ts"],"sourcesContent":["import { runDispatch } from \"./util.js\";\nimport type { ExitMessage, WithSender, 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 status: number;\n responseHeaders: Record<string, string>;\n};\n\n/** Arguments for a fetch process. */\nexport type FetchArgs<T> =\n | {\n url: URL;\n method?: \"GET\";\n body?: undefined;\n headers?: Record<string, string>;\n }\n | {\n url: URL;\n method?: \"POST\" | \"PUT\" | \"PATCH\";\n body: T;\n headers?: Record<string, string>;\n };\n\n/** Messages emitted by a fetch process during its lifecycle. */\nexport type FetchMessage<T> =\n | {\n type: \"OK\";\n data?: T | null;\n text?: string | null;\n status: number;\n responseHeaders: Record<string, string>;\n }\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/** Convert a Headers object to a plain Record. */\nfunction headersToRecord(h: Headers): Record<string, string> {\n const out: Record<string, string> = {};\n h.forEach((v, k) => {\n out[k] = v;\n });\n return out;\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 {\n pname,\n toParent,\n sendSelf,\n }: ProcessCtx<\n FetchArgs<Type>,\n FetchState<Type>,\n FetchMessage<Type>,\n FetchMessage<Type>\n >,\n { method = \"GET\", url, body, headers: callerHeaders }: FetchArgs<Type>,\n): Generator<FetchState<Type> | null, void, WithSender<FetchMessage<Type>>> {\n const state: FetchState<Type> = {\n code: \"pending\",\n data: null,\n text: null,\n status: 0,\n responseHeaders: {},\n };\n yield state;\n\n const controller = new AbortController();\n const signal = controller.signal;\n const toSelf = sendSelf;\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(callerHeaders ?? {});\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 const status = res.status;\n const responseHeaders = headersToRecord(res.headers);\n if (isJsonHelper(res)) {\n const data = await res.json();\n toSelf({ type: \"OK\", data, status, responseHeaders });\n } else {\n const text = await res.text();\n toSelf({ type: \"OK\", text, status, responseHeaders });\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<WithSender<FetchMessage<Type>>>(\n pname,\n (maybe: WithSender<FetchMessage<Type>>) => {\n const [msg, _sender] = maybe;\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 state.status = msg.status;\n state.responseHeaders = msg.responseHeaders;\n }\n },\n isDone,\n false,\n );\n}\n\nexport { xfetch };\n"],"mappings":";;AAoDA,SAAS,aAAa,KAAwB;CAE5C,OADW,IAAI,QAAQ,IAAI,cACnB,MAAM;AAChB;;AAGA,SAAS,gBAAgB,GAAoC;CAC3D,MAAM,MAA8B,CAAC;CACrC,EAAE,SAAS,GAAG,MAAM;EAClB,IAAI,KAAK;CACX,CAAC;CACD,OAAO;AACT;;;;;;AASA,UAAU,OACR,EACE,OACA,UACA,YAOF,EAAE,SAAS,OAAO,KAAK,MAAM,SAAS,iBACoC;CAC1E,MAAM,QAA0B;EAC9B,MAAM;EACN,MAAM;EACN,MAAM;EACN,QAAQ;EACR,iBAAiB,CAAC;CACpB;CACA,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,iBAAiB,CAAC,CAAC;GAC/C,IAAI,gBACF,QAAQ,IAAI,gBAAgB,kBAAkB;GAEhD,MAAM,MAAgB,MAAM,MAAM,IAAI,MAAM;IAC1C;IACA;IACA,MAAM;IACN;GACF,CAAC;GACD,MAAM,SAAS,IAAI;GACnB,MAAM,kBAAkB,gBAAgB,IAAI,OAAO;GACnD,IAAI,aAAa,GAAG,GAElB,OAAO;IAAE,MAAM;IAAM,MAAA,MADF,IAAI,KAAK;IACD;IAAQ;GAAgB,CAAC;QAGpD,OAAO;IAAE,MAAM;IAAM,MAAA,MADF,IAAI,KAAK;IACD;IAAQ;GAAgB,CAAC;EAExD,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,UAA0C;EACzC,MAAM,CAAC,KAAK,WAAW;EACvB,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;GACzB,MAAM,SAAS,IAAI;GACnB,MAAM,kBAAkB,IAAI;EAC9B;CACF,GACA,QACA,KACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"process-C-ERBQY5.d.ts","names":[],"sources":["../src/types.ts","../src/process.async.ts","../src/util.ts","../src/process.ts"],"mappings":";AAsBA;;AAAA,UAXiB,OAAA;EACf,IAAI;AAAA;;;;;AAeE;AAER;;KAPY,WAAA;EACV,IAAA,UAOI;EALJ,GAAA;EACA,QAAA;EACA,MAAA;AAAA;AAAA,KAEU,WAAA;EACV,IAAI;AAAA;AAAA,KAKM,SAAA,gCAGQ,OAAA,qBACC,OAAA,KAEnB,GAAA,EAAK,UAAA,CAAW,IAAA,EAAM,KAAA,EAAO,SAAA,EAAW,UAAA,GACxC,IAAA,EAAM,IAAA,KACH,SAAA,CAAU,KAAA,eAAoB,SAAA;AAAA,KAIvB,cAAA,gCAGQ,OAAA,qBACC,OAAA,KAEnB,GAAA,EAAK,UAAA,CAAW,IAAA,EAAM,KAAA,EAAO,SAAA,EAAW,UAAA,GACxC,IAAA,EAAM,IAAA,KACH,cAAA,CAAe,KAAA,eAAoB,SAAA;AAAA,KAInC,kBAAA,OAAuB,GAAA,EAAK,CAAC;;;;KAyBtB,UAAA,yBAAmC,OAAA,aAAoB,OAAA;EACjE,KAAA;EACA,EAAA;EACA,IAAA,GAAO,GAAA,EAAK,EAAA;EACZ,QAAA,EAAU,kBAAA,CAAiB,EAAA;AAAA,IACzB,IAAA,CAAK,YAAA,CAAa,IAAA,EAAM,KAAA,EAAO,EAAA,EAAI,EAAA;;UAKtB,SAAA;EACf,MAAA,EAAQ,MAAA;EACR,MAAA,EAAQ,MAAM;EACd,OAAA;AAAA;;UAMe,eAAA;EACf,SAAA;EACA,KAAK;AAAA;;;KCpFF,QAAA;AAAA,KAIA,YAAA,OAAmB,GAAA,EAAK,CAAA,KAAM,OAAO;AAAA,KACrC,SAAA;;ADdC;AAUN;;;iBCWuB,gBAAA,IACrB,IAAA,UACA,EAAA,EAAI,YAAA,CAAa,CAAA,GACjB,OAAA,GAAS,SAAA,EACT,UAAA,aACC,cAAA,aAA2B,CAAA;AAAA,KAWzB,gBAAA,OAAuB,GAAA,EAAK,CAAC;;;;;ADtB1B;AAER;;;cCgCa,YAAA,gCAGO,OAAA,qBACC,OAAA;EAEnB,UAAA,EAAY,cAAA,CAAe,IAAA,EAAM,KAAA,EAAO,SAAA,EAAW,UAAA;EACnD,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,QAEA,WAAA;EAAA,QACA,UAAA;EAAA,QACA,SAAA;EAAA,QACA,eAAA;EAAA,QACA,WAAA;EAAA,QACA,MAAA;EAAA,QACA,aAAA;cAGN,EAAA,EAAI,cAAA,CAAe,IAAA,EAAM,KAAA,EAAO,SAAA,EAAW,UAAA,GAC3C,KAAA,UACA,QAAA,EAAU,gBAAA,CAAiB,UAAA;EDrD7B;ECkEA,KAAA,IAAS,OAAA;EDhET;;;;EC0EA,KAAA,CAAM,IAAA,EAAM,IAAA;EDvEI;EAAA,QCoGD,UAAA;EAmBf,IAAA,wCAA4C,OAAA,kBAAyB,OAAA,EACnE,EAAA,EAAI,cAAA,CAAe,SAAA,EAAW,UAAA,EAAY,OAAA,EAAS,OAAA,GACnD,KAAA,YAEA,IAAA,EAAM,SAAA,KACH,YAAA,CAAa,SAAA,EAAW,UAAA,EAAY,OAAA,EAAS,OAAA;EAelD,QAAA,wCAGkB,OAAA,kBACA,OAAA,EAEhB,EAAA,EAAI,SAAA,CAAU,SAAA,EAAW,UAAA,EAAY,OAAA,EAAS,OAAA,GAC9C,KAAA,YAEA,IAAA,EAAM,SAAA,KACH,YAAA,CAAa,SAAA,EAAW,UAAA,EAAY,OAAA,EAAS,OAAA;EAAA,UAMlC,KAAA,IAAS,OAAA;ED1JnB;EAAA,QCgLQ,SAAA;EAAA,QAYN,UAAA;ED3LK;EC0Mb,aAAA,CAAc,GAAA,EAAK,OAAA;ED1MuB;EC+M1C,IAAA,CAAK,GAAA,EAAK,SAAA;ED3MA;;;;;ECqNV,IAAA;EAAA,QAKQ,aAAA;EAYR,MAAA;EAAA,IAII,YAAA;EAIJ,SAAA,CAAU,CAAA,EAAG,QAAA;EAWb,KAAA;EAMA,MAAA;EDvPsC;;;;ECkQtC,IAAA,IAAQ,OAAA;EAAA,QAmBA,SAAA;AAAA;;;;;iBAgBM,UAAA,gCAGI,OAAA,GAAU,OAAA,qBACT,OAAA,GAAU,WAAA,EAE7B,EAAA,EAAI,cAAA,CAAe,IAAA,EAAM,KAAA,EAAO,SAAA,EAAW,UAAA,GAC3C,KAAA,UACA,QAAA,GAAW,gBAAA,CAAiB,UAAA,KAC1B,IAAA,EAAM,IAAA,KAAS,YAAA,CAAa,IAAA,EAAM,KAAA,EAAO,SAAA,EAAW,UAAA;;;KC9VnD,cAAA,OAAqB,GAAA,EAAK,CAAC;AAAA,KAC3B,OAAA;AFIC;AAUN;;;;AAVM,iBEII,WAAA,IACR,IAAA,UACA,EAAA,EAAI,cAAA,CAAe,CAAA,GACnB,OAAA,GAAS,OAAA,EACT,UAAA,aACC,SAAA,aAAsB,CAAA;;;;;AFMjB;AAER;;;iBGtBgB,KAAA,kBAGH,OAAA,GAAU,OAAA,aACV,OAAA,GAAU,WAAA,EAErB,EAAA,EAAI,SAAA,CAAU,CAAA,EAAG,CAAA,EAAG,EAAA,EAAI,EAAA,GACxB,KAAA,UACA,EAAA,IAAM,CAAA,EAAG,EAAA,aACP,CAAA,EAAG,CAAA,KAAM,OAAA,CAAQ,CAAA,EAAG,CAAA,EAAG,EAAA,EAAI,EAAA;AAAA,cAIzB,OAAA,kBAGO,OAAA,aACA,OAAA,UACH,YAAA,CAAa,CAAA,EAAG,CAAA,EAAG,EAAA,EAAI,EAAA;cAE7B,EAAA,EAAI,SAAA,CAAU,CAAA,EAAG,CAAA,EAAG,EAAA,EAAI,EAAA,GACxB,KAAA,UACA,EAAA,IAAM,CAAA,EAAG,EAAA;EAKX,KAAA,CAAM,CAAA,EAAG,CAAA;EAIT,IAAA,IAAQ,OAAA;AAAA"}
|