posipaki 0.39.0 → 0.41.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/dist/{client-C5QmGQ6G.d.ts → client-GrwWOUBV.d.ts} +27 -4
- package/dist/client-GrwWOUBV.d.ts.map +1 -0
- package/dist/{define-actor-jG-OgJng.js → define-actor-D7c9vn6S.js} +26 -25
- package/dist/define-actor-D7c9vn6S.js.map +1 -0
- package/dist/hooks-CgXhmY4r.js.map +1 -1
- package/dist/{hooks-C5S7GIX_.d.ts → hooks-D1QaP6Vv.d.ts} +9 -3
- package/dist/hooks-D1QaP6Vv.d.ts.map +1 -0
- package/dist/hooks.d.ts +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/{json1-CPFT4wEX.js → json1-BWi4HzI4.js} +116 -12
- package/dist/json1-BWi4HzI4.js.map +1 -0
- package/dist/plugins/debug-logger.d.ts +2 -2
- package/dist/plugins/tree-introspection.d.ts +2 -2
- package/dist/plugins/tree-introspection.js +1 -1
- package/dist/{process-RsCsXWC1.d.ts → process-BVwU7TQo.d.ts} +2 -2
- package/dist/{process-RsCsXWC1.d.ts.map → process-BVwU7TQo.d.ts.map} +1 -1
- package/dist/{process.async-js1t-fd8.js → process.async-C_-NpMFX.js} +2 -1
- package/dist/process.async-C_-NpMFX.js.map +1 -0
- package/dist/remote/gateway-cli.js +11 -2
- package/dist/remote/gateway-cli.js.map +1 -1
- package/dist/remote/index.d.ts +9 -4
- package/dist/remote/index.d.ts.map +1 -1
- package/dist/remote/index.js +2 -2
- package/dist/remote/node.d.ts +6 -3
- package/dist/remote/node.d.ts.map +1 -1
- package/dist/remote/node.js +7 -6
- package/dist/remote/node.js.map +1 -1
- package/dist/testing/index.d.ts +2 -2
- package/dist/{types-BuI2bfi4.d.ts → types-DYd-DDFB.d.ts} +32 -3
- package/dist/{types-BuI2bfi4.d.ts.map → types-DYd-DDFB.d.ts.map} +1 -1
- package/dist/{version-C-ToFw8V.js → version-CYQgj_GV.js} +2 -2
- package/dist/{version-C-ToFw8V.js.map → version-CYQgj_GV.js.map} +1 -1
- package/dist/xfetch.d.ts +2 -2
- package/package.json +1 -1
- package/dist/client-C5QmGQ6G.d.ts.map +0 -1
- package/dist/define-actor-jG-OgJng.js.map +0 -1
- package/dist/hooks-C5S7GIX_.d.ts.map +0 -1
- package/dist/json1-CPFT4wEX.js.map +0 -1
- package/dist/process.async-js1t-fd8.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { h as MethodOptions, l as ActorDefinition, p as HandlerOptions, v as ReflectionOptions } from "./hooks-
|
|
1
|
+
import { i as Message } from "./types-DYd-DDFB.js";
|
|
2
|
+
import { h as MethodOptions, l as ActorDefinition, p as HandlerOptions, v as ReflectionOptions } from "./hooks-D1QaP6Vv.js";
|
|
3
3
|
//#region src/remote/channel.d.ts
|
|
4
4
|
interface StringTransport {
|
|
5
5
|
send(frame: string): void | Promise<void>;
|
|
@@ -38,6 +38,29 @@ declare function isExit(msg: Record<string, unknown>): msg is {
|
|
|
38
38
|
state: unknown;
|
|
39
39
|
};
|
|
40
40
|
};
|
|
41
|
+
/** How much of an unreadable line is carried: enough to see what it was, never enough to be it.
|
|
42
|
+
* A line that failed to parse is the one line nobody vouched for, so quoting it whole would move
|
|
43
|
+
* whatever it holds — a credential in a paste accident is how that happens — into every log,
|
|
44
|
+
* transcript and context window that later reads the frame. Why, how long, and a short head of
|
|
45
|
+
* it. (`reason` carries a little of the line on its own: a JSON parser says where it gave up and
|
|
46
|
+
* quotes what it was looking at. That is the parser's business, not a promise this file makes.) */
|
|
47
|
+
declare const BAD_PREFIX_LIMIT = 32;
|
|
48
|
+
declare function isBad(msg: Record<string, unknown>): msg is {
|
|
49
|
+
$bad: {
|
|
50
|
+
reason: string;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
/** The frame that says a frame could not be read: why, how long it was, and the head of it. */
|
|
54
|
+
declare function badFrame(reason: string, raw: string): Record<string, unknown>;
|
|
55
|
+
/** Why a frame could not be read, or null when it is not a frame that says so. */
|
|
56
|
+
declare function badReason(msg: Record<string, unknown>): string | null;
|
|
57
|
+
declare function isError(msg: Record<string, unknown>): msg is {
|
|
58
|
+
$error: string;
|
|
59
|
+
};
|
|
60
|
+
/** The frame that says the wire is over: the reason, and the connection ends with it. */
|
|
61
|
+
declare function errorFrame(reason: string): Record<string, unknown>;
|
|
62
|
+
/** The reason a wire error carries, or null when it is not one. */
|
|
63
|
+
declare function errorReason(msg: Record<string, unknown>): string | null;
|
|
41
64
|
type StreamKind = "message" | "state" | "exit";
|
|
42
65
|
declare function isStop(msg: Record<string, unknown>): msg is {
|
|
43
66
|
$stop: Record<string, unknown>;
|
|
@@ -66,5 +89,5 @@ type ClientSpawner<Args> = (args: Args) => Promise<Channel>;
|
|
|
66
89
|
declare const CHANNEL_LOST = "channel-lost";
|
|
67
90
|
declare function remoteClient<Args, State, InMsg extends Message, OutMsg extends Message, Methods extends MethodOptions = MethodOptions, Handlers extends HandlerOptions<InMsg> = HandlerOptions<InMsg>, R extends ReflectionOptions = ReflectionOptions>(name: string, spawner: ClientSpawner<Args>): ActorDefinition<Args, State, InMsg, OutMsg, R>;
|
|
68
91
|
//#endregion
|
|
69
|
-
export {
|
|
70
|
-
//# sourceMappingURL=client-
|
|
92
|
+
export { isStop as S, isPause as _, Channel as a, isResume as b, badFrame as c, errorReason as d, isBad as f, isMsg as g, isInit as h, BAD_PREFIX_LIMIT as i, badReason as l, isExit as m, ClientSpawner as n, StreamKind as o, isError as p, remoteClient as r, StringTransport as s, CHANNEL_LOST as t, errorFrame as u, isProto as v, isState as x, isRelease as y };
|
|
93
|
+
//# sourceMappingURL=client-GrwWOUBV.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-GrwWOUBV.d.ts","names":[],"sources":["../src/remote/channel.ts","../src/remote/client.ts"],"mappings":";;;UAgBiB;EACf,KAAK,uBAAuB;EAC5B,UAAU,UAAU;EACpB;;EAEA,QAAQ;EACR,SAAS;;UAGM;EACf,KAAK,OAAO,iCAAiC;EAC7C,UAAU,UAAU,OAAO;EAC3B;;EAEA,QAAQ;EACR,SAAS;;iBA6CK,QAAQ,KAAK,0BAA0B;EAAS;;iBAGhD,OAAO,KAAK,0BAA0B;EAAS,OAAO;;iBAGtD,QAAQ,KAAK,0BAA0B;EAAS,QAAQ;;iBAGxD,MACd,KAAK,0BACJ;EAAS;IAAQ;IAAkB,MAAM;;;iBAG5B,OACd,KAAK,0BACJ;EAAS;IAAS;IAAc;;;;;;;;;cAkCtB;iBAEG,MAAM,KAAK,0BAA0B;EAAS;IAAQ;;;;iBAKtD,SAAS,gBAAgB,cAAc;;iBAMvC,UAAU,KAAK;iBASf,QAAQ,KAAK,0BAA0B;EAAS;;;iBAKhD,WAAW,iBAAiB;;iBAK5B,YAAY,KAAK;KAcrB;iBA+BI,OAAO,KAAK,0BAA0B;EAAS,OAAO;;iBAGtD,QAAQ,KAAK,0BAA0B;EAAS,QAAQ;;iBAGxD,SAAS,KAAK,0BAA0B;EAAS,SAAS;;iBAG1D,UAAU,KAAK,0BAA0B;EAAS,UAAU;;;;KCxKhE,cAAc,SAAS,MAAM,SAAS,QAAQ;;;;;;;;;;cAY7C;iBAEG,aACd,MACA,OACA,cAAc,SACd,eAAe,SACf,gBAAgB,gBAAgB,eAChC,iBAAiB,eAAe,SAAS,eAAe,QACxD,UAAU,oBAAoB,mBAE9B,cACA,SAAS,cAAc,QACtB,gBAAgB,MAAM,OAAO,OAAO,QAAQ"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as spawnAsync, i as runDispatchAsync } from "./process.async-
|
|
1
|
+
import { a as spawnAsync, i as runDispatchAsync } from "./process.async-C_-NpMFX.js";
|
|
2
2
|
import { a as STOP_SENTINEL, i as PROPAGATE_SENTINEL, t as callHook } from "./hooks-CgXhmY4r.js";
|
|
3
3
|
//#region src/define-actor.ts
|
|
4
4
|
function defineMessages() {}
|
|
@@ -53,8 +53,9 @@ function defineActor(config) {
|
|
|
53
53
|
if (wake) ctx.wake();
|
|
54
54
|
};
|
|
55
55
|
const decorated = /* @__PURE__ */ new Map();
|
|
56
|
-
const
|
|
56
|
+
const actorCtx = {
|
|
57
57
|
ctx,
|
|
58
|
+
self: ctx.self,
|
|
58
59
|
...assembly.methods || {},
|
|
59
60
|
...assembly.$decorate || {},
|
|
60
61
|
reflection: {},
|
|
@@ -62,7 +63,7 @@ function defineActor(config) {
|
|
|
62
63
|
name: ctx.pname,
|
|
63
64
|
id: ctx.id,
|
|
64
65
|
async emit(msg) {
|
|
65
|
-
await callHook(assembly.onEmit, void 0,
|
|
66
|
+
await callHook(assembly.onEmit, void 0, actorCtx, msg, {
|
|
66
67
|
fromName: ctx.pname,
|
|
67
68
|
fromId: ctx.id
|
|
68
69
|
});
|
|
@@ -78,7 +79,7 @@ function defineActor(config) {
|
|
|
78
79
|
decorators: {},
|
|
79
80
|
fork: async (childActor, childArgs, forkOpts) => {
|
|
80
81
|
let child;
|
|
81
|
-
const childName = forkOpts?.name ?? childActor?.name ?? `child-${Object.keys(
|
|
82
|
+
const childName = forkOpts?.name ?? childActor?.name ?? `child-${Object.keys(actorCtx.$child).length}`;
|
|
82
83
|
const treeName = `${ctx.pname}:${childName}`;
|
|
83
84
|
const childAddPlugins = [...assembly.addPlugins || [], ...forkOpts?.addPlugins || []];
|
|
84
85
|
child = await childActor.spawnAsChild(ctx, childArgs, {
|
|
@@ -86,11 +87,11 @@ function defineActor(config) {
|
|
|
86
87
|
parentPlugins: assembly.resolvedPlugins || [],
|
|
87
88
|
...childAddPlugins.length ? { addPlugins: childAddPlugins } : {}
|
|
88
89
|
});
|
|
89
|
-
|
|
90
|
+
actorCtx.$child[child.pname] = child;
|
|
90
91
|
return child;
|
|
91
92
|
}
|
|
92
93
|
};
|
|
93
|
-
actorCtxMap.set(ctx.id,
|
|
94
|
+
actorCtxMap.set(ctx.id, actorCtx);
|
|
94
95
|
/**
|
|
95
96
|
* Lifecycle hooks are the actor's own control flow: if one throws, the
|
|
96
97
|
* state machine is half-applied, so the error is fatal — `onError` still
|
|
@@ -106,40 +107,40 @@ function defineActor(config) {
|
|
|
106
107
|
*/
|
|
107
108
|
const hookErrorHandler = async (e) => {
|
|
108
109
|
try {
|
|
109
|
-
await assembly.onError?.call(
|
|
110
|
+
await assembly.onError?.call(actorCtx, e);
|
|
110
111
|
} catch {}
|
|
111
112
|
return PROPAGATE_SENTINEL;
|
|
112
113
|
};
|
|
113
114
|
ctx.afterExit = async () => {
|
|
114
|
-
await callHook(assembly.afterEnd, hookErrorHandler,
|
|
115
|
+
await callHook(assembly.afterEnd, hookErrorHandler, actorCtx, exitReason);
|
|
115
116
|
};
|
|
116
|
-
for (const [k, v] of decorated)
|
|
117
|
-
await callHook(assembly.beforeStart, hookErrorHandler,
|
|
118
|
-
if (assembly.setup) rawState = await assembly.setup.call(
|
|
117
|
+
for (const [k, v] of decorated) actorCtx[k] = v;
|
|
118
|
+
await callHook(assembly.beforeStart, hookErrorHandler, actorCtx);
|
|
119
|
+
if (assembly.setup) rawState = await assembly.setup.call(actorCtx, args);
|
|
119
120
|
else rawState = null;
|
|
120
|
-
|
|
121
|
+
actorCtx.state = rawState;
|
|
121
122
|
yield hidePrivate(rawState);
|
|
122
|
-
await callHook(assembly.afterStart, hookErrorHandler,
|
|
123
|
+
await callHook(assembly.afterStart, hookErrorHandler, actorCtx);
|
|
123
124
|
yield* runDispatchAsync(ctx.pname, async (stamped) => {
|
|
124
125
|
const [msg, sender] = stamped;
|
|
125
126
|
if (msg.type === "__ADVANCE__") return;
|
|
126
127
|
if (msg.type === "STOP") {
|
|
127
|
-
if (assembly.onStopRequested) await callHook(assembly.onStopRequested, hookErrorHandler,
|
|
128
|
+
if (assembly.onStopRequested) await callHook(assembly.onStopRequested, hookErrorHandler, actorCtx);
|
|
128
129
|
else endWith("stopped");
|
|
129
130
|
return;
|
|
130
131
|
}
|
|
131
132
|
if (msg.type === "EXIT") {
|
|
132
133
|
const childName = sender.fromName;
|
|
133
|
-
if (childName &&
|
|
134
|
-
await callHook(assembly.onChildExit, hookErrorHandler,
|
|
134
|
+
if (childName && actorCtx.$child[childName]) delete actorCtx.$child[childName];
|
|
135
|
+
await callHook(assembly.onChildExit, hookErrorHandler, actorCtx, childName, msg, msg.reason);
|
|
135
136
|
const orphans = msg.orphans;
|
|
136
137
|
if (orphans && orphans.length > 0) for (const orphan of orphans) {
|
|
137
|
-
const decision = assembly.onOrphan ? await callHook(assembly.onOrphan, hookErrorHandler,
|
|
138
|
+
const decision = assembly.onOrphan ? await callHook(assembly.onOrphan, hookErrorHandler, actorCtx, orphan) : "force-stop";
|
|
138
139
|
if (decision === "adopt") {
|
|
139
140
|
ctx.adopt(orphan);
|
|
140
141
|
const idx = ctx.orphans.indexOf(orphan);
|
|
141
142
|
if (idx >= 0) ctx.orphans.splice(idx, 1);
|
|
142
|
-
|
|
143
|
+
actorCtx.$child[orphan.pname] = orphan;
|
|
143
144
|
} else if (decision === "force-stop") {
|
|
144
145
|
orphan.forceStop({ cascade: true });
|
|
145
146
|
const idx = ctx.orphans.indexOf(orphan);
|
|
@@ -149,23 +150,23 @@ function defineActor(config) {
|
|
|
149
150
|
}
|
|
150
151
|
let hookStopped = false;
|
|
151
152
|
if (msg.type !== "STOP" && msg.type !== "EXIT" && assembly.onMessage) {
|
|
152
|
-
if (await callHook(assembly.onMessage, assembly.onError,
|
|
153
|
+
if (await callHook(assembly.onMessage, assembly.onError, actorCtx, msg, sender) === STOP_SENTINEL) hookStopped = true;
|
|
153
154
|
}
|
|
154
155
|
if (msg.type !== "STOP" && !hookStopped) {
|
|
155
156
|
const handler = (assembly.handlers || {})[msg.type] || assembly.onUnhandled;
|
|
156
|
-
await callHook(handler, assembly.onError,
|
|
157
|
+
await callHook(handler, assembly.onError, actorCtx, msg, sender);
|
|
157
158
|
}
|
|
158
159
|
}, () => done);
|
|
159
|
-
await callHook(assembly.beforeEnd, hookErrorHandler,
|
|
160
|
+
await callHook(assembly.beforeEnd, hookErrorHandler, actorCtx, exitReason);
|
|
160
161
|
};
|
|
161
162
|
}
|
|
162
163
|
function attachReflection(proc, reflectionMethods) {
|
|
163
|
-
const
|
|
164
|
-
if (
|
|
164
|
+
const actorCtx = actorCtxMap.get(proc.id);
|
|
165
|
+
if (actorCtx) proc.$reflection = actorCtx.reflection;
|
|
165
166
|
if (!reflectionMethods) return;
|
|
166
167
|
const refl = proc.$reflection;
|
|
167
168
|
const written = Object.entries(reflectionMethods);
|
|
168
|
-
for (const [k, m] of written) refl[k] = async (...args) => await m.apply(
|
|
169
|
+
for (const [k, m] of written) refl[k] = async (...args) => await m.apply(actorCtx, args);
|
|
169
170
|
}
|
|
170
171
|
return {
|
|
171
172
|
fn: makeRuntime(config),
|
|
@@ -193,4 +194,4 @@ function defineActor(config) {
|
|
|
193
194
|
//#endregion
|
|
194
195
|
export { defineMessages as n, defineActor as t };
|
|
195
196
|
|
|
196
|
-
//# sourceMappingURL=define-actor-
|
|
197
|
+
//# sourceMappingURL=define-actor-D7c9vn6S.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-actor-D7c9vn6S.js","names":[],"sources":["../src/define-actor.ts"],"sourcesContent":["// ── 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//\nimport {\n type AsyncProcess,\n runDispatchAsync,\n spawnAsync,\n AnyProcess,\n INTERNAL_ADVANCE,\n} from \"./process.async.js\";\nimport type {\n WithSender,\n SenderInfo,\n AsyncProcessFn,\n Message,\n ExitMessage,\n ForkSite,\n ProcessCtx,\n} from \"./types.js\";\nimport type {\n ActorDefinition,\n ActorConfig,\n ActorContext,\n MethodOptions,\n ActorMessages,\n HandlerOptions,\n HandlerFn,\n ReflectionOptions,\n AnyConfig,\n HidePrivate,\n} from \"./actor-types.js\";\nimport { STOP_SENTINEL, PROPAGATE_SENTINEL } from \"./actor-types.js\";\nimport { ActorDecorated, type ActorPlugin, ActorReflection, callHook } from \"./hooks.js\";\n\nexport function defineMessages<OutMsg extends Message = Message>(): ActorMessages<OutMsg> {\n return undefined as unknown as ActorMessages<OutMsg>;\n}\n\nfunction hidePrivate<T>(value: T): HidePrivate<T> {\n if (value && typeof value === \"object\" && \"private\" in value && \"public\" in value) {\n return value.public as HidePrivate<T>;\n }\n return value as HidePrivate<T>;\n}\n\nfunction resolvePlugins(\n raw: ActorPlugin[] | ((parents: ActorPlugin[]) => ActorPlugin[]) | undefined,\n parentPlugins?: ActorPlugin[],\n addPlugins?: ActorPlugin[],\n): ActorPlugin[] {\n const resolved = !raw\n ? parentPlugins\n ? [...parentPlugins]\n : []\n : Array.isArray(raw)\n ? [...raw]\n : raw(parentPlugins ?? []);\n\n const combined = addPlugins ? [...resolved, ...addPlugins] : resolved;\n\n // Deduplicate by function name\n const seen = new Set<string>();\n return combined.filter((p) => {\n const name = p.name;\n if (!name) {\n console.warn(\"posipaki: plugin has no name, dedup won't work\");\n return true;\n }\n if (seen.has(name)) return false;\n seen.add(name);\n return true;\n });\n}\n\nasync function assembleActor<C>(\n config: C,\n plugins: ActorPlugin[],\n addPlugins?: ActorPlugin[],\n): Promise<C> {\n // Work with the default ActorPlugin type internally, cast back on return\n let cur = config as AnyConfig;\n for (const p of plugins) {\n try {\n cur = await p(cur);\n } catch (e: unknown) {\n console.error(`[assembleActor] plugin \"${p.name || \"?\"}\" failed:`, e);\n }\n }\n cur.resolvedPlugins = plugins;\n if (addPlugins) cur.addPlugins = addPlugins;\n return cur as C;\n}\n\nexport function defineActor<\n Args,\n InternalState,\n InMsg extends Message,\n OutMsg extends Message,\n Methods extends MethodOptions,\n Handlers extends HandlerOptions<InMsg>,\n ReflectionMethods extends ReflectionOptions,\n>(\n config: ActorConfig<Args, InternalState, InMsg, OutMsg, Methods, Handlers, ReflectionMethods>,\n): ActorDefinition<Args, InternalState, InMsg, OutMsg, ReflectionMethods> {\n const actorCtxMap = new Map<\n symbol,\n ActorContext<Args, InternalState, InMsg, OutMsg, Methods, Handlers, ReflectionMethods>\n >();\n\n function makeRuntime(\n assembly: ActorConfig<Args, InternalState, InMsg, OutMsg, Methods, Handlers, ReflectionMethods>,\n ): AsyncProcessFn<Args, HidePrivate<InternalState>, InMsg, OutMsg> {\n return async function* (\n ctx,\n args,\n ): AsyncGenerator<HidePrivate<InternalState | null>, void, WithSender<InMsg>> {\n let done = false;\n let exitReason: unknown;\n let rawState: InternalState = undefined as unknown as InternalState;\n\n /**\n * End this actor, and write the reason down twice: the hooks (`beforeEnd`, `afterEnd`) read\n * the local copy, and the EXIT the parent receives carries it as well. An exit that says\n * only *that* something ended leaves the parent to guess — and a stop that was agreed to, a\n * wire that closed (`CHANNEL_LOST`) and a child that finished are three different things to\n * whoever has to react.\n */\n const endWith = (reason: unknown, wake = false): void => {\n exitReason = reason;\n ctx.exitReason = reason;\n done = true;\n // The loop only re-reads `done` when it is resumed: an idle actor is\n // parked at its dispatch yield and would sit there forever.\n if (wake) ctx.wake();\n };\n\n const decorated = new Map();\n const actorCtx: ActorContext<\n Args,\n InternalState,\n InMsg,\n OutMsg,\n Methods,\n Handlers,\n ReflectionMethods & ActorReflection\n > = {\n ctx,\n // The process itself, as the ctx knows it: the same object this actor is being\n // run as, handed out by an actor that wants to be reached rather than named.\n self: ctx.self as unknown as ActorContext<\n Args,\n InternalState,\n InMsg,\n OutMsg,\n Methods,\n Handlers,\n ReflectionMethods & ActorReflection\n >['self'],\n ...((assembly.methods || {}) as Methods),\n ...((assembly.$decorate || {}) as ActorDecorated),\n reflection: {} as ReflectionMethods & ActorReflection,\n state: rawState,\n name: ctx.pname,\n id: ctx.id,\n async emit(msg: OutMsg) {\n await callHook(assembly.onEmit, undefined, actorCtx, msg, {\n fromName: ctx.pname,\n fromId: ctx.id,\n });\n ctx.toParent(msg);\n },\n agreeToStop() {\n endWith(\"stopped\");\n },\n exit(reason: unknown) {\n endWith(reason, true);\n },\n $child: {} as Record<string, AnyProcess>,\n decorators: {},\n fork: async <A, S, IM extends Message, OM extends InMsg, R extends ReflectionOptions>(\n childActor: ActorDefinition<A, S, IM, OM, R>,\n childArgs?: A,\n forkOpts?: { name?: string; addPlugins?: ActorPlugin[] },\n ): Promise<AsyncProcess<A, HidePrivate<S>, IM, OM, R & ActorReflection>> => {\n let child: AsyncProcess<A, HidePrivate<S>, IM, OM, R & ActorReflection>;\n const childName =\n forkOpts?.name ?? childActor?.name ?? `child-${Object.keys(actorCtx.$child).length}`;\n const treeName = `${ctx.pname}:${childName}`;\n const childAddPlugins = [...(assembly.addPlugins || []), ...(forkOpts?.addPlugins || [])];\n child = await childActor.spawnAsChild(ctx, childArgs!, {\n name: treeName,\n parentPlugins: assembly.resolvedPlugins || [],\n ...(childAddPlugins.length ? { addPlugins: childAddPlugins } : {}),\n });\n actorCtx.$child[child.pname] = child as unknown as AnyProcess;\n return child;\n },\n };\n actorCtxMap.set(ctx.id, actorCtx);\n /**\n * Lifecycle hooks are the actor's own control flow: if one throws, the\n * state machine is half-applied, so the error is fatal — `onError` still\n * observes it (a logger logs it), but it cannot absorb it. The actor goes\n * down and its parent still gets the EXIT, instead of staying up with\n * nothing left to do.\n *\n * Message hooks and handlers are not forced either way: they are called\n * with `onError` itself, and its *return value* decides — nothing means\n * \"handled, carry on\" (the default, and what an observing logger\n * returns), `propagateError()` means \"not handled\", and the actor goes\n * down with the same EXIT.\n */\n const hookErrorHandler = async (e: unknown): Promise<unknown> => {\n try {\n await assembly.onError?.call(actorCtx, e);\n } catch {\n // An error in the error handler must not mask the original one.\n }\n return PROPAGATE_SENTINEL;\n };\n ctx.afterExit = async () => {\n await callHook(assembly.afterEnd, hookErrorHandler, actorCtx, exitReason);\n };\n\n for (const [k, v] of decorated) {\n (actorCtx as Record<string, unknown>)[k] = v;\n }\n\n await callHook(\n assembly.beforeStart,\n hookErrorHandler,\n actorCtx as ActorContext<\n Args,\n never, // state not set yet\n InMsg,\n OutMsg,\n Methods,\n Handlers,\n ReflectionMethods & ActorReflection\n >,\n );\n\n if (assembly.setup) {\n rawState = await assembly.setup.call(\n actorCtx as ActorContext<\n Args,\n never, // break inference cycle here\n InMsg,\n OutMsg,\n Methods,\n Handlers,\n ReflectionMethods & ActorReflection\n >,\n args,\n );\n } else {\n rawState = null as InternalState;\n }\n actorCtx.state = rawState;\n yield hidePrivate(rawState);\n await callHook(assembly.afterStart, hookErrorHandler, actorCtx);\n\n yield* runDispatchAsync<WithSender<InMsg | ExitMessage>>(\n ctx.pname,\n async (stamped) => {\n const [msg, sender] = stamped;\n if (msg.type === INTERNAL_ADVANCE) return; // framework traffic, not a message\n if (msg.type === \"STOP\") {\n if (assembly.onStopRequested) {\n await callHook(assembly.onStopRequested, hookErrorHandler, actorCtx);\n // Hook may call agreeToStop(). If not, actor keeps running.\n } else {\n endWith(\"stopped\");\n }\n return;\n }\n if (msg.type === \"EXIT\") {\n // FIXME: process name are user-specified\n // and should not be used for indexing so\n // we don't get into name conflicts\n const childName = sender.fromName;\n if (childName && actorCtx.$child[childName]) {\n delete actorCtx.$child[childName];\n }\n await callHook(\n assembly.onChildExit,\n hookErrorHandler,\n actorCtx,\n childName,\n msg as ExitMessage,\n (msg as ExitMessage).reason,\n );\n // Orphan policy: a child that exits may leave still-running\n // grandchildren behind (see ctx-orphans / orphan-policy).\n const orphans = (msg as ExitMessage).orphans;\n if (orphans && orphans.length > 0) {\n for (const orphan of orphans) {\n // Default when no hook is defined: hard-kill the orphan.\n const decision = assembly.onOrphan\n ? await callHook(assembly.onOrphan, hookErrorHandler, actorCtx, orphan)\n : \"force-stop\";\n if (decision === \"adopt\") {\n ctx.adopt(orphan);\n const idx = ctx.orphans.indexOf(orphan);\n if (idx >= 0) ctx.orphans.splice(idx, 1);\n actorCtx.$child[orphan.pname] = orphan;\n } else if (decision === \"force-stop\") {\n orphan.forceStop({ cascade: true });\n const idx = ctx.orphans.indexOf(orphan);\n if (idx >= 0) ctx.orphans.splice(idx, 1);\n } else if (decision === \"unparent\") {\n orphan.unparent();\n }\n // 'leave' (or undefined) → do nothing; it propagates on exit.\n }\n }\n }\n let hookStopped = false;\n if (msg.type !== \"STOP\" && msg.type !== \"EXIT\" && assembly.onMessage) {\n const result = await callHook(\n assembly.onMessage,\n assembly.onError,\n actorCtx,\n msg as InMsg,\n sender,\n );\n if (result === STOP_SENTINEL) hookStopped = true;\n }\n if (msg.type !== \"STOP\" && !hookStopped) {\n const handler =\n ((assembly.handlers || ({} as Handlers))[\n msg.type as keyof Handlers\n ] as HandlerFn<InMsg>) || assembly.onUnhandled;\n\n await callHook(handler, assembly.onError, actorCtx, msg as InMsg, sender);\n }\n },\n () => done,\n );\n\n await callHook(assembly.beforeEnd, hookErrorHandler, actorCtx, exitReason);\n };\n }\n\n type ReflectableProcess = {\n id: symbol;\n $reflection: Record<string, Function>;\n };\n function attachReflection(\n proc: ReflectableProcess,\n reflectionMethods: ReflectionMethods | undefined,\n ): void {\n const actorCtx = actorCtxMap.get(proc.id);\n if (actorCtx) {\n // One object under two names: the surface the handle exposes as\n // `$reflection` and the one the actor's own hooks see as `reflection`.\n // Handing the same object to both is what lets a method be added after\n // assembly — the first caller that needs that is a remote peer saying\n // which methods it can answer.\n proc.$reflection = actorCtx.reflection as unknown as Record<string, Function>;\n }\n if (!reflectionMethods) return;\n const refl = proc.$reflection as Record<string, Function>;\n // The declared contract is `ReflectionMethod`, whose parameters are `never`\n // because nothing is called through it; this is the one place that calls\n // what it describes.\n const written = Object.entries(reflectionMethods) as Array<\n [string, (...args: unknown[]) => unknown]\n >;\n for (const [k, m] of written) {\n // A promise whatever the method was written as, so a caller reads the same\n // call the same way whether the actor is here or behind a wire.\n refl[k] = async (...args: unknown[]) => await m.apply(actorCtx, args);\n }\n }\n\n return {\n fn: makeRuntime(config),\n name: config.name,\n pvtPluginsRaw: config.plugins,\n inMessages: config.inMessages,\n outMessages: config.outMessages,\n async spawn(\n args: Args,\n opts?: {\n name?: string;\n toParent?: (msg: OutMsg, from: SenderInfo) => void;\n addPlugins?: ActorPlugin[];\n parentName?: string | null;\n parentId?: symbol | null;\n /**\n * Resolve only once the initial state is available. Default `true`:\n * a spawned process is safe to send to when `spawn()` returns. Pass\n * `false` for the raw handle — the caller then has to `await\n * proc.ready()` itself before sending anything it wants handled.\n */\n awaitReady?: boolean;\n },\n ): Promise<\n AsyncProcess<\n Args,\n HidePrivate<InternalState>,\n InMsg,\n OutMsg,\n ReflectionMethods & ActorReflection\n >\n > {\n const plugs = resolvePlugins(config.plugins, undefined, opts?.addPlugins);\n const assembly = await assembleActor(config, plugs, opts?.addPlugins);\n const runtime = makeRuntime(assembly);\n const proc = spawnAsync(\n runtime,\n opts?.name ?? assembly.name ?? \"actor\",\n opts?.toParent,\n opts?.parentName,\n opts?.parentId,\n )(args);\n attachReflection(proc, assembly.$reflectionMethods as ReflectionMethods);\n if (opts?.awaitReady !== false) await proc.ready();\n return proc as AsyncProcess<\n Args,\n HidePrivate<InternalState>,\n InMsg,\n OutMsg,\n ReflectionMethods & ActorReflection\n >;\n },\n async spawnAsChild(\n ctx: ForkSite,\n args: Args,\n opts?: {\n name?: string;\n parentPlugins?: ActorPlugin[];\n addPlugins?: ActorPlugin[];\n },\n ) {\n const plugs = resolvePlugins(config.plugins, opts?.parentPlugins, opts?.addPlugins);\n const assembly = await assembleActor(config, plugs, opts?.addPlugins);\n const runtime = makeRuntime(assembly);\n const proc = ctx.fork(runtime, opts?.name ?? assembly.name ?? \"child\")(args);\n attachReflection(proc, assembly.$reflectionMethods as ReflectionMethods);\n return proc as AsyncProcess<\n Args,\n HidePrivate<InternalState>,\n InMsg,\n OutMsg,\n ReflectionMethods & ActorReflection\n >;\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks-CgXhmY4r.js","names":[],"sources":["../src/actor-types.ts","../src/hooks.ts"],"sourcesContent":["// ── defineActor types ────────────────────────────────────────────────────────\n//\n// Shared between define-actor.ts (implementation) and consumers that want\n// to reference the config/context/definition shapes without importing the\n// implementation module directly.\n\nimport type { SenderInfo, AsyncProcessFn, Message, ProcessCtx, ExitMessage } from \"./types.js\";\nimport type { ActorDecorated, ActorReflection } from \"./hooks.js\";\nimport type { AnyProcess, AsyncProcess } from \"./process.async.js\";\n\n// Internal marker do not use\nexport type ActorMessages<M extends Message> = {\n __tag_messages: M;\n};\nexport interface MethodOptions {\n [key: string]: Function;\n}\nexport type HandlerFn<InMsg extends Message> = (\n msg: InMsg,\n sender: SenderInfo,\n) => void | Promise<void>;\nexport type HandlerOptions<InMsg extends Message> = Omit<\n {\n [K in InMsg[\"type\"]]: HandlerFn<Extract<InMsg, { type: K }>>;\n },\n \"STOP\"\n>;\n/**\n * A reflection method, async by contract: it answers with a promise, even when\n * it has the answer already. The same call has to read the same way whether the\n * actor is here or behind a wire, and a caller that had to know which of the two\n * it was holding would be reading the shape of the deployment, not the answer.\n *\n * The parameters are `never` because nothing is called through this type — it is\n * the contract every declared method is checked against.\n */\nexport type ReflectionMethod = (...args: never[]) => Promise<unknown>;\n/** The reflection surface a process exposes: named methods, each of them async\n * by {@link ReflectionMethod}'s contract. */\nexport interface ReflectionOptions {\n [name: string]: ReflectionMethod;\n}\nexport type Paired<Priv, Pub> = { public: Pub; private: Priv };\nexport type HidePrivate<T> = T extends Paired<unknown, unknown> ? T[\"public\"] : T;\n\nexport type SpawnedFrom<T extends ActorDefinition<any, any, any, any, any>> =\n T extends ActorDefinition<infer A, infer S, infer IM, infer OM, infer R>\n ? AsyncProcess<A, HidePrivate<S>, IM, OM, R & ActorReflection>\n : never;\n\nexport interface ActorDefinition<\n Args,\n InternalState,\n InMsg extends Message,\n OutMsg extends Message,\n ReflectionMethods extends ReflectionOptions,\n> {\n fn: AsyncProcessFn<Args, HidePrivate<InternalState>, InMsg, OutMsg>;\n /** Preferred process name (from config.name). */\n name?: string;\n /** Raw plugin config (array or transform). Resolved at fork time. @internal */\n pvtPluginsRaw?: ActorPlugin[] | PluginTransform;\n /** Spawn this actor as a standalone process. */\n spawn(\n args: Args,\n opts?: {\n name?: string;\n toParent?: (msg: OutMsg, from: SenderInfo) => void;\n addPlugins?: ActorPlugin[];\n parentName?: string | null;\n parentId?: symbol | null;\n /** Wait for the initial state before resolving. Default `true`. */\n awaitReady?: boolean;\n },\n ): Promise<\n AsyncProcess<\n Args,\n HidePrivate<InternalState>,\n InMsg,\n OutMsg,\n ReflectionMethods & ActorReflection\n >\n >;\n /** Spawn this actor as a child of the calling process.\n * `ctx` must be able to fork children that emit `OutMsg` — i.e. the\n * parent's in-message is a supertype of this actor's out-message. */\n spawnAsChild<PMO extends Message>(\n ctx: ProcessCtx<unknown, unknown, OutMsg, PMO>,\n args: Args,\n opts?: {\n name?: string;\n parentPlugins?: ActorPlugin[];\n addPlugins?: ActorPlugin[];\n },\n ): Promise<\n AsyncProcess<\n Args,\n HidePrivate<InternalState>,\n InMsg,\n OutMsg,\n ReflectionMethods & ActorReflection\n >\n >;\n inMessages: ActorMessages<InMsg> | undefined;\n outMessages: ActorMessages<OutMsg> | undefined;\n}\n/**\n * The config object as a user writes it.\n *\n * Hooks take their `this` from the `ThisType` at the end of this type: a per-hook\n * annotation erases the inferred methods surface. `beforeStart` and `setup` keep\n * one, for the state.\n */\nexport type ActorConfig<\n Args,\n InternalState,\n InMsg extends Message,\n OutMsg extends Message,\n Methods extends MethodOptions,\n Handlers extends HandlerOptions<InMsg>,\n ReflectionMethods extends ReflectionOptions,\n> = {\n /** Preferred process name. Used by ctx.fork() when no explicit name is given. */\n name?: string;\n plugins?: ActorPlugin[] | PluginTransform;\n /** Non-overridable plugins added at spawn time (spawn/fork opts).\n * Set by the framework during assembly, not by user config. @internal */\n addPlugins?: ActorPlugin[];\n /** Resolved plugin chain (raw config transformed against parent plugins).\n * Set by the framework during assembly, replacing `plugins` (the raw form).\n * @internal */\n resolvedPlugins?: ActorPlugin[];\n outMessages?: ActorMessages<OutMsg>;\n inMessages?: ActorMessages<InMsg>;\n /** Fires once `self` is built, before `setup`. State is not yet set\n * (`this.state` is `never`). Use for registering the process/ctx before\n * any child is forked in `setup`. */\n beforeStart?: (\n this: ActorContext<\n Args,\n never,\n InMsg,\n OutMsg,\n MethodOptions,\n Handlers,\n ReflectionMethods & ActorReflection\n >,\n ) => void | Promise<void>;\n\n /** `this.state` is `never`: the returned value is the state. */\n setup?: (\n this: ActorContext<\n Args,\n never,\n InMsg,\n OutMsg,\n MethodOptions,\n Handlers,\n ReflectionMethods & ActorReflection\n >,\n args: Args,\n ) => Promise<InternalState> | InternalState;\n\n afterStart?: () => void | Promise<void>;\n\n onStopRequested?: () => HookResult | Promise<HookResult>;\n\n beforeEnd?: (reason?: unknown) => HookResult | Promise<HookResult>;\n\n /**\n * Runs once the actor has ended — after its EXIT. It is *not* called when the\n * actor never started (`setup()` or a pre-start hook threw): the state is\n * typed as present here, and an actor that never got one has nothing to end.\n */\n afterEnd?: (reason?: unknown) => HookResult | Promise<HookResult>;\n\n onError?: (error?: unknown) => HookResult | ErrorResult | Promise<HookResult | ErrorResult>;\n\n onEmit?: (msg: OutMsg, sender: SenderInfo) => HookResult | Promise<HookResult>;\n\n onMessage?: (msg: InMsg, sender: SenderInfo) => HookResult | Promise<HookResult>;\n\n onUnhandled?: (msg: Message, sender: SenderInfo) => void | Promise<void>;\n\n /** A child ended. `exit` is what it sent — its orphans travel there — and `reason` is what it\n * ended *for*: the value `exit(reason)` was given, `\"stopped\"` for a stop that was agreed to,\n * `CHANNEL_LOST` when the wire under a proxy went, or undefined when nobody asked. */\n onChildExit?: (\n name: string,\n exit: ExitMessage,\n reason: unknown,\n ) => HookResult | Promise<HookResult>;\n\n /** Fires for each orphan a child leaves behind (in its EXIT). Return the\n * policy: `'adopt'` (promote to a child, draining its buffer), `'force-stop'`\n * (hard-kill), `'unparent'` (drop its buffer, keep it running in `orphans`),\n * or `'leave'` (keep buffering, propagate up on my exit). When no `onOrphan`\n * is defined, the default is `'force-stop'`. */\n onOrphan?: (orphan: AnyProcess) => OrphanDecision | void | Promise<OrphanDecision | void>;\n\n handlers: Handlers &\n ThisType<\n ActorContext<\n Args,\n InternalState,\n InMsg,\n OutMsg,\n Methods,\n Handlers,\n ReflectionMethods & ActorReflection\n >\n >;\n\n methods?: Methods &\n ThisType<\n ActorContext<\n Args,\n InternalState,\n InMsg,\n OutMsg,\n Methods,\n Handlers,\n ReflectionMethods & ActorReflection\n >\n >;\n $reflectionMethods?: ReflectionMethods &\n ThisType<\n ActorContext<\n Args,\n InternalState,\n InMsg,\n OutMsg,\n Methods,\n Handlers,\n ActorReflection & ActorReflection\n >\n >;\n // for plugin use only\n $decorate?: Partial<ActorDecorated>;\n} & ThisType<\n ActorContext<Args, InternalState, InMsg, OutMsg, Methods, Handlers, ReflectionMethods & ActorReflection>\n>;\nexport type AnyConfig = ActorConfig<\n unknown,\n unknown,\n Message,\n Message,\n MethodOptions,\n HandlerOptions<Message>,\n ReflectionOptions\n>;\n\n// ── stop propagation sentinel ────────────────────────────────────────────\n\n/** Returned by onMessage hooks to prevent further dispatch. */\nexport const STOP_SENTINEL = Symbol(\"posipaki.stopPropagation\");\n\n/** Type-safe sentinel for short-circuiting onMessage hooks. */\nexport const stopPropagation = (): typeof STOP_SENTINEL => STOP_SENTINEL;\n\n/**\n * Returned by an `onError` handler that only *observed* the error — logged it,\n * counted it — and did not handle it. The framework then lets the error\n * propagate: the actor still goes down and its parent still gets the EXIT.\n * Without this an observing handler (the bundled debug logger is one) silently\n * turns a broken lifecycle hook into a survivor with nothing left to do.\n */\nexport const PROPAGATE_SENTINEL = Symbol(\"posipaki.propagateError\");\n\n/** Type-safe sentinel for an onError handler that did not handle the error. */\nexport const propagateError = (): typeof PROPAGATE_SENTINEL => PROPAGATE_SENTINEL;\n\n/** Return type of onError handlers: void (handled) or sentinel (not handled). */\nexport type ErrorResult = void | typeof PROPAGATE_SENTINEL;\n\n// ── hook function types ──────────────────────────────────────────────────\n\n/** Return type of onMessage hooks: void (continue) or sentinel (stop). */\nexport type HookResult = void | typeof STOP_SENTINEL;\n\n/** Decision returned by `onOrphan` for how to handle an inherited orphan. */\nexport type OrphanDecision = \"adopt\" | \"force-stop\" | \"unparent\" | \"leave\";\n// ── plugin types ─────────────────────────────────────────────────────────\n\n/** A reusable unit of actor behaviour. */\nexport type ActorPlugin<C = AnyConfig> = (config: C) => C | Promise<C>;\n\n/** Transform parent plugins into child plugins. */\nexport type PluginTransform = (parentPlugins: ActorPlugin[]) => ActorPlugin[];\n\nexport type ActorContext<\n Args,\n InternalState,\n InMsg extends Message,\n OutMsg extends Message,\n Methods extends MethodOptions,\n Handlers extends HandlerOptions<InMsg>,\n ReflectionMethods extends ReflectionOptions,\n> = Methods &\n ActorDecorated & {\n state: InternalState;\n name: string;\n id: symbol;\n\n emit: (msg: OutMsg) => void;\n agreeToStop: () => void;\n\n reflection: ThisType<\n ActorContext<Args, InternalState, InMsg, OutMsg, Methods, Handlers, ReflectionMethods>\n > &\n ReflectionMethods;\n exit: (reason?: unknown) => void;\n\n $child: Record<string, AnyProcess>;\n\n fork<A, S, IM extends Message, OM extends InMsg, R extends ReflectionOptions>(\n actor: ActorDefinition<A, S, IM, OM, R>,\n args?: A,\n opts?: {\n name?: string;\n addPlugins?: ActorPlugin[];\n },\n ): Promise<AsyncProcess<A, HidePrivate<S>, IM, OM, R & ActorReflection>>;\n\n ctx: ProcessCtx<Args, HidePrivate<InternalState>, InMsg, OutMsg>;\n };\n\n\n\n/**\n * The context a config's hooks are typed against, for a `Partial<>` overlay:\n * a mapped type drops the marker `ActorConfig` carries.\n */\nexport type ActorContextOf<C> = C extends ActorConfig<\n infer Args,\n infer InternalState,\n infer InMsg extends Message,\n infer OutMsg extends Message,\n infer Methods extends MethodOptions,\n infer Handlers,\n infer ReflectionMethods extends ReflectionOptions\n>\n ? ActorContext<\n Args,\n InternalState,\n InMsg,\n OutMsg,\n Methods,\n Handlers extends HandlerOptions<InMsg> ? Handlers : HandlerOptions<InMsg>,\n ReflectionMethods & ActorReflection\n >\n : never;\n","// ── Lifecycle Hooks ──────────────────────────────────────────────────────\n//\n// Extends ProcessCtx and defineActor with observable lifecycle hooks.\n// Hooks are additive — multiple callbacks can register for the same hook\n// point, and they fire in registration order.\n\nimport {\n STOP_SENTINEL,\n stopPropagation,\n PROPAGATE_SENTINEL,\n propagateError,\n} from \"./actor-types.js\";\nimport type {\n HookResult,\n ActorPlugin,\n PluginTransform,\n AnyConfig,\n ActorContextOf,\n ReflectionOptions,\n} from \"./actor-types.js\";\n\n// ── stop propagation sentinel ────────────────────────────────────────────\n\n// Re-exports from actor-types.ts (backward compatibility)\nexport { STOP_SENTINEL, stopPropagation, PROPAGATE_SENTINEL, propagateError };\nexport type { HookResult, ActorPlugin, PluginTransform };\n\n// ── type augmentation (Fastify-style) ────────────────────────────────────\n\n/**\n * Interface that plugins can augment via declaration merging.\n * Plugins ship a .d.ts that adds properties to this interface,\n * making them available on `this` in handlers and methods.\n *\n * Example (in a plugin's .d.ts):\n * declare module 'posipaki' {\n * interface ActorDecorated {\n * log: Logger;\n * }\n * }\n */\nexport interface ActorDecorated {}\n/** Reflection methods plugins and the framework add to every actor. Merged by\n * declaration, and held to {@link ReflectionOptions}' contract: async. */\nexport interface ActorReflection extends ReflectionOptions {}\n\n// ── chainHook ────────────────────────────────────────────────────────────\n\n/**\n * Compose two lifecycle hooks so that `plugin` fires first.\n *\n * If `plugin` returns the STOP_SENTINEL (via stopPropagation()),\n * `existing` is skipped entirely and the sentinel propagates.\n * Otherwise `existing` fires and its return value is used.\n *\n * Typical use in a plugin:\n * (cfg) => ({ ...cfg, onStart: chainHook(cfg.onStart, myOnStart) })\n *\n * @param existing - the current hook on the config (may be undefined)\n * @param plugin - the new hook to prepend\n * @returns a composed hook suitable for ActorConfig\n */\nexport function chainHook<TThis, TArgs extends unknown[]>(\n existing: ((this: TThis, ...args: TArgs) => HookResult | Promise<HookResult>) | undefined,\n plugin: (this: TThis, ...args: TArgs) => HookResult | Promise<HookResult>,\n): (this: TThis, ...args: TArgs) => HookResult | Promise<HookResult> {\n if (!existing) return plugin;\n return async function (this: TThis, ...args: TArgs): Promise<HookResult> {\n const result = await plugin.call(this, ...args);\n if (result === STOP_SENTINEL) return result;\n return await existing.call(this, ...args);\n } as (this: TThis, ...args: TArgs) => HookResult | Promise<HookResult>;\n}\n\n// ── mergeConfigs ─────────────────────────────────────────────────────────\n\n/**\n * Merge an overlay config into a base config, auto-chaining every on* hook.\n *\n * Each key in `overlay` that starts with \"on\" followed by an uppercase\n * letter (onStart, onMessage, onEmit, etc.) is composed via chainHook()\n * so that the overlay hook fires before the base hook, and stopPropagation\n * is respected.\n *\n * Typical use in a plugin:\n * return mergeConfigs(cfg, {\n * onStart() { this.log.info('starting'); },\n * beforeEnd() { this.log.info('stopping'); },\n * });\n *\n * @param base - the existing config (ActorConfig)\n * @param overlay - new hook implementations to prepend\n * @returns a new config with hooks chained\n */\nexport function mergeConfigs<C extends AnyConfig>(\n base: C,\n overlay: Partial<C> & ThisType<ActorContextOf<C>>,\n): C {\n const result = { ...base } as Record<string, unknown>;\n for (const key of Object.keys(overlay as Record<string, unknown>)) {\n const val = (overlay as Record<string, unknown>)[key];\n if (typeof val === \"function\" && /^(on|after|before)[A-Z]/.test(key)) {\n result[key] = chainHook(\n (base as Record<string, unknown>)[key] as (\n this: unknown,\n ...args: unknown[]\n ) => HookResult | Promise<HookResult>,\n val as (this: unknown, ...args: unknown[]) => HookResult | Promise<HookResult>,\n );\n } else {\n result[key] = val;\n }\n }\n result.$reflectionMethods = {};\n Object.assign(\n result.$reflectionMethods as object,\n \"$reflectionMethods\" in base ? base.$reflectionMethods : {},\n \"$reflectionMethods\" in overlay ? overlay.$reflectionMethods : {},\n );\n\n return result as unknown as C;\n}\n\nexport type Hook<T, I extends unknown[], O> = (this: T, ...args: I) => O;\n\nexport async function callHook<T, I extends unknown[], O>(\n fn: Hook<T, I, O> | undefined,\n eh: ((e: unknown) => unknown) | undefined,\n thisArg: T,\n ...args: I\n): Promise<O | undefined> {\n if (fn) {\n try {\n return await fn.call(thisArg, ...args);\n } catch (e) {\n if (eh) {\n let decision: unknown;\n try {\n decision = await eh(e);\n } catch {\n // An error inside the error handler must not mask the original one.\n }\n // An observing handler says so by returning the sentinel: the error it\n // did not handle keeps propagating, so the actor still goes down.\n if (decision === PROPAGATE_SENTINEL) throw e;\n } else {\n throw e;\n }\n }\n }\n return undefined;\n}\n"],"mappings":";;AA+PA,MAAa,gBAAgB,OAAO,0BAA0B;;AAG9D,MAAa,wBAA8C;;;;;;;;AAS3D,MAAa,qBAAqB,OAAO,yBAAyB;;AAGlE,MAAa,uBAAkD;;;;;;;;;;;;;;;;;AChN/D,SAAgB,UACd,UACA,QACmE;CACnE,IAAI,CAAC,UAAU,OAAO;CACtB,OAAO,eAA6B,GAAG,MAAkC;EACvE,MAAM,SAAS,MAAM,OAAO,KAAK,MAAM,GAAG,IAAI;EAC9C,IAAI,WAAW,eAAe,OAAO;EACrC,OAAO,MAAM,SAAS,KAAK,MAAM,GAAG,IAAI;CAC1C;AACF;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,aACd,MACA,SACG;CACH,MAAM,SAAS,EAAE,GAAG,KAAK;CACzB,KAAK,MAAM,OAAO,OAAO,KAAK,OAAkC,GAAG;EACjE,MAAM,MAAO,QAAoC;EACjD,IAAI,OAAO,QAAQ,cAAc,0BAA0B,KAAK,GAAG,GACjE,OAAO,OAAO,UACX,KAAiC,MAIlC,GACF;OAEA,OAAO,OAAO;CAElB;CACA,OAAO,qBAAqB,CAAC;CAC7B,OAAO,OACL,OAAO,oBACP,wBAAwB,OAAO,KAAK,qBAAqB,CAAC,GAC1D,wBAAwB,UAAU,QAAQ,qBAAqB,CAAC,CAClE;CAEA,OAAO;AACT;AAIA,eAAsB,SACpB,IACA,IACA,SACA,GAAG,MACqB;CACxB,IAAI,IACF,IAAI;EACF,OAAO,MAAM,GAAG,KAAK,SAAS,GAAG,IAAI;CACvC,SAAS,GAAG;EACV,IAAI,IAAI;GACN,IAAI;GACJ,IAAI;IACF,WAAW,MAAM,GAAG,CAAC;GACvB,QAAQ,CAER;GAGA,IAAI,aAAa,oBAAoB,MAAM;EAC7C,OACE,MAAM;CAEV;AAGJ"}
|
|
1
|
+
{"version":3,"file":"hooks-CgXhmY4r.js","names":[],"sources":["../src/actor-types.ts","../src/hooks.ts"],"sourcesContent":["// ── defineActor types ────────────────────────────────────────────────────────\n//\n// Shared between define-actor.ts (implementation) and consumers that want\n// to reference the config/context/definition shapes without importing the\n// implementation module directly.\n\nimport type { SenderInfo, AsyncProcessFn, Message, ProcessCtx, ExitMessage, ForkSite } from \"./types.js\";\nimport type { ActorDecorated, ActorReflection } from \"./hooks.js\";\nimport type { AnyProcess, AsyncProcess } from \"./process.async.js\";\n\n// Internal marker do not use\nexport type ActorMessages<M extends Message> = {\n __tag_messages: M;\n};\nexport interface MethodOptions {\n [key: string]: Function;\n}\nexport type HandlerFn<InMsg extends Message> = (\n msg: InMsg,\n sender: SenderInfo,\n) => void | Promise<void>;\nexport type HandlerOptions<InMsg extends Message> = Omit<\n {\n [K in InMsg[\"type\"]]: HandlerFn<Extract<InMsg, { type: K }>>;\n },\n \"STOP\"\n>;\n/**\n * A reflection method, async by contract: it answers with a promise, even when\n * it has the answer already. The same call has to read the same way whether the\n * actor is here or behind a wire, and a caller that had to know which of the two\n * it was holding would be reading the shape of the deployment, not the answer.\n *\n * The parameters are `never` because nothing is called through this type — it is\n * the contract every declared method is checked against.\n */\nexport type ReflectionMethod = (...args: never[]) => Promise<unknown>;\n/** The reflection surface a process exposes: named methods, each of them async\n * by {@link ReflectionMethod}'s contract. */\nexport interface ReflectionOptions {\n [name: string]: ReflectionMethod;\n}\nexport type Paired<Priv, Pub> = { public: Pub; private: Priv };\nexport type HidePrivate<T> = T extends Paired<unknown, unknown> ? T[\"public\"] : T;\n\nexport type SpawnedFrom<T extends ActorDefinition<any, any, any, any, any>> =\n T extends ActorDefinition<infer A, infer S, infer IM, infer OM, infer R>\n ? AsyncProcess<A, HidePrivate<S>, IM, OM, R & ActorReflection>\n : never;\n\nexport interface ActorDefinition<\n Args,\n InternalState,\n InMsg extends Message,\n OutMsg extends Message,\n ReflectionMethods extends ReflectionOptions,\n> {\n fn: AsyncProcessFn<Args, HidePrivate<InternalState>, InMsg, OutMsg>;\n /** Preferred process name (from config.name). */\n name?: string;\n /** Raw plugin config (array or transform). Resolved at fork time. @internal */\n pvtPluginsRaw?: ActorPlugin[] | PluginTransform;\n /** Spawn this actor as a standalone process. */\n spawn(\n args: Args,\n opts?: {\n name?: string;\n toParent?: (msg: OutMsg, from: SenderInfo) => void;\n addPlugins?: ActorPlugin[];\n parentName?: string | null;\n parentId?: symbol | null;\n /** Wait for the initial state before resolving. Default `true`. */\n awaitReady?: boolean;\n },\n ): Promise<\n AsyncProcess<\n Args,\n HidePrivate<InternalState>,\n InMsg,\n OutMsg,\n ReflectionMethods & ActorReflection\n >\n >;\n /** Spawn this actor as a child of the calling process.\n * `ctx` must be able to fork children that emit `OutMsg` — i.e. the\n * parent's in-message is a supertype of this actor's out-message. */\n spawnAsChild(\n ctx: ForkSite,\n args: Args,\n opts?: {\n name?: string;\n parentPlugins?: ActorPlugin[];\n addPlugins?: ActorPlugin[];\n },\n ): Promise<\n AsyncProcess<\n Args,\n HidePrivate<InternalState>,\n InMsg,\n OutMsg,\n ReflectionMethods & ActorReflection\n >\n >;\n inMessages: ActorMessages<InMsg> | undefined;\n outMessages: ActorMessages<OutMsg> | undefined;\n}\n/**\n * The config object as a user writes it.\n *\n * Hooks take their `this` from the `ThisType` at the end of this type: a per-hook\n * annotation erases the inferred methods surface. `beforeStart` and `setup` keep\n * one, for the state.\n */\nexport type ActorConfig<\n Args,\n InternalState,\n InMsg extends Message,\n OutMsg extends Message,\n Methods extends MethodOptions,\n Handlers extends HandlerOptions<InMsg>,\n ReflectionMethods extends ReflectionOptions,\n> = {\n /** Preferred process name. Used by ctx.fork() when no explicit name is given. */\n name?: string;\n plugins?: ActorPlugin[] | PluginTransform;\n /** Non-overridable plugins added at spawn time (spawn/fork opts).\n * Set by the framework during assembly, not by user config. @internal */\n addPlugins?: ActorPlugin[];\n /** Resolved plugin chain (raw config transformed against parent plugins).\n * Set by the framework during assembly, replacing `plugins` (the raw form).\n * @internal */\n resolvedPlugins?: ActorPlugin[];\n outMessages?: ActorMessages<OutMsg>;\n inMessages?: ActorMessages<InMsg>;\n /** Fires once `self` is built, before `setup`. State is not yet set\n * (`this.state` is `never`). Use for registering the process/ctx before\n * any child is forked in `setup`. */\n beforeStart?: (\n this: ActorContext<\n Args,\n never,\n InMsg,\n OutMsg,\n MethodOptions,\n Handlers,\n ReflectionMethods & ActorReflection\n >,\n ) => void | Promise<void>;\n\n /** `this.state` is `never`: the returned value is the state. */\n setup?: (\n this: ActorContext<\n Args,\n never,\n InMsg,\n OutMsg,\n MethodOptions,\n Handlers,\n ReflectionMethods & ActorReflection\n >,\n args: Args,\n ) => Promise<InternalState> | InternalState;\n\n afterStart?: () => void | Promise<void>;\n\n onStopRequested?: () => HookResult | Promise<HookResult>;\n\n beforeEnd?: (reason?: unknown) => HookResult | Promise<HookResult>;\n\n /**\n * Runs once the actor has ended — after its EXIT. It is *not* called when the\n * actor never started (`setup()` or a pre-start hook threw): the state is\n * typed as present here, and an actor that never got one has nothing to end.\n */\n afterEnd?: (reason?: unknown) => HookResult | Promise<HookResult>;\n\n onError?: (error?: unknown) => HookResult | ErrorResult | Promise<HookResult | ErrorResult>;\n\n onEmit?: (msg: OutMsg, sender: SenderInfo) => HookResult | Promise<HookResult>;\n\n onMessage?: (msg: InMsg, sender: SenderInfo) => HookResult | Promise<HookResult>;\n\n onUnhandled?: (msg: Message, sender: SenderInfo) => void | Promise<void>;\n\n /** A child ended. `exit` is what it sent — its orphans travel there — and `reason` is what it\n * ended *for*: the value `exit(reason)` was given, `\"stopped\"` for a stop that was agreed to,\n * `CHANNEL_LOST` when the wire under a proxy went, or undefined when nobody asked. */\n onChildExit?: (\n name: string,\n exit: ExitMessage,\n reason: unknown,\n ) => HookResult | Promise<HookResult>;\n\n /** Fires for each orphan a child leaves behind (in its EXIT). Return the\n * policy: `'adopt'` (promote to a child, draining its buffer), `'force-stop'`\n * (hard-kill), `'unparent'` (drop its buffer, keep it running in `orphans`),\n * or `'leave'` (keep buffering, propagate up on my exit). When no `onOrphan`\n * is defined, the default is `'force-stop'`. */\n onOrphan?: (orphan: AnyProcess) => OrphanDecision | void | Promise<OrphanDecision | void>;\n\n handlers: Handlers &\n ThisType<\n ActorContext<\n Args,\n InternalState,\n InMsg,\n OutMsg,\n Methods,\n Handlers,\n ReflectionMethods & ActorReflection\n >\n >;\n\n methods?: Methods &\n ThisType<\n ActorContext<\n Args,\n InternalState,\n InMsg,\n OutMsg,\n Methods,\n Handlers,\n ReflectionMethods & ActorReflection\n >\n >;\n $reflectionMethods?: ReflectionMethods &\n ThisType<\n ActorContext<\n Args,\n InternalState,\n InMsg,\n OutMsg,\n Methods,\n Handlers,\n ActorReflection & ActorReflection\n >\n >;\n // for plugin use only\n $decorate?: Partial<ActorDecorated>;\n} & ThisType<\n ActorContext<Args, InternalState, InMsg, OutMsg, Methods, Handlers, ReflectionMethods & ActorReflection>\n>;\nexport type AnyConfig = ActorConfig<\n unknown,\n unknown,\n Message,\n Message,\n MethodOptions,\n HandlerOptions<Message>,\n ReflectionOptions\n>;\n\n// ── stop propagation sentinel ────────────────────────────────────────────\n\n/** Returned by onMessage hooks to prevent further dispatch. */\nexport const STOP_SENTINEL = Symbol(\"posipaki.stopPropagation\");\n\n/** Type-safe sentinel for short-circuiting onMessage hooks. */\nexport const stopPropagation = (): typeof STOP_SENTINEL => STOP_SENTINEL;\n\n/**\n * Returned by an `onError` handler that only *observed* the error — logged it,\n * counted it — and did not handle it. The framework then lets the error\n * propagate: the actor still goes down and its parent still gets the EXIT.\n * Without this an observing handler (the bundled debug logger is one) silently\n * turns a broken lifecycle hook into a survivor with nothing left to do.\n */\nexport const PROPAGATE_SENTINEL = Symbol(\"posipaki.propagateError\");\n\n/** Type-safe sentinel for an onError handler that did not handle the error. */\nexport const propagateError = (): typeof PROPAGATE_SENTINEL => PROPAGATE_SENTINEL;\n\n/** Return type of onError handlers: void (handled) or sentinel (not handled). */\nexport type ErrorResult = void | typeof PROPAGATE_SENTINEL;\n\n// ── hook function types ──────────────────────────────────────────────────\n\n/** Return type of onMessage hooks: void (continue) or sentinel (stop). */\nexport type HookResult = void | typeof STOP_SENTINEL;\n\n/** Decision returned by `onOrphan` for how to handle an inherited orphan. */\nexport type OrphanDecision = \"adopt\" | \"force-stop\" | \"unparent\" | \"leave\";\n// ── plugin types ─────────────────────────────────────────────────────────\n\n/** A reusable unit of actor behaviour. */\nexport type ActorPlugin<C = AnyConfig> = (config: C) => C | Promise<C>;\n\n/** Transform parent plugins into child plugins. */\nexport type PluginTransform = (parentPlugins: ActorPlugin[]) => ActorPlugin[];\n\nexport type ActorContext<\n Args,\n InternalState,\n InMsg extends Message,\n OutMsg extends Message,\n Methods extends MethodOptions,\n Handlers extends HandlerOptions<InMsg>,\n ReflectionMethods extends ReflectionOptions,\n> = Methods &\n ActorDecorated & {\n state: InternalState;\n name: string;\n id: symbol;\n /** This actor as a process: the handle another process can be given so that it can\n * send here. There is no other way to obtain one — a process is reachable because\n * somebody holds it, and this is how it hands itself over. The same object as\n * `ctx.self`, and as the one a spawner, a parent or a remote handle stands for.\n * Erased, like every handle that leaves: whoever receives it declares what it is. */\n self: AsyncProcess<\n Args,\n HidePrivate<InternalState>,\n InMsg,\n OutMsg,\n ReflectionMethods & ActorReflection\n >;\n\n emit: (msg: OutMsg) => void;\n agreeToStop: () => void;\n\n reflection: ThisType<\n ActorContext<Args, InternalState, InMsg, OutMsg, Methods, Handlers, ReflectionMethods>\n > &\n ReflectionMethods;\n exit: (reason?: unknown) => void;\n\n $child: Record<string, AnyProcess>;\n\n fork<A, S, IM extends Message, OM extends InMsg, R extends ReflectionOptions>(\n actor: ActorDefinition<A, S, IM, OM, R>,\n args?: A,\n opts?: {\n name?: string;\n addPlugins?: ActorPlugin[];\n },\n ): Promise<AsyncProcess<A, HidePrivate<S>, IM, OM, R & ActorReflection>>;\n\n ctx: ProcessCtx<Args, HidePrivate<InternalState>, InMsg, OutMsg>;\n };\n\n\n\n/**\n * The context a config's hooks are typed against, for a `Partial<>` overlay:\n * a mapped type drops the marker `ActorConfig` carries.\n */\nexport type ActorContextOf<C> = C extends ActorConfig<\n infer Args,\n infer InternalState,\n infer InMsg extends Message,\n infer OutMsg extends Message,\n infer Methods extends MethodOptions,\n infer Handlers,\n infer ReflectionMethods extends ReflectionOptions\n>\n ? ActorContext<\n Args,\n InternalState,\n InMsg,\n OutMsg,\n Methods,\n Handlers extends HandlerOptions<InMsg> ? Handlers : HandlerOptions<InMsg>,\n ReflectionMethods & ActorReflection\n >\n : never;\n","// ── Lifecycle Hooks ──────────────────────────────────────────────────────\n//\n// Extends ProcessCtx and defineActor with observable lifecycle hooks.\n// Hooks are additive — multiple callbacks can register for the same hook\n// point, and they fire in registration order.\n\nimport {\n STOP_SENTINEL,\n stopPropagation,\n PROPAGATE_SENTINEL,\n propagateError,\n} from \"./actor-types.js\";\nimport type {\n HookResult,\n ActorPlugin,\n PluginTransform,\n AnyConfig,\n ActorContextOf,\n ReflectionOptions,\n} from \"./actor-types.js\";\n\n// ── stop propagation sentinel ────────────────────────────────────────────\n\n// Re-exports from actor-types.ts (backward compatibility)\nexport { STOP_SENTINEL, stopPropagation, PROPAGATE_SENTINEL, propagateError };\nexport type { HookResult, ActorPlugin, PluginTransform };\n\n// ── type augmentation (Fastify-style) ────────────────────────────────────\n\n/**\n * Interface that plugins can augment via declaration merging.\n * Plugins ship a .d.ts that adds properties to this interface,\n * making them available on `this` in handlers and methods.\n *\n * Example (in a plugin's .d.ts):\n * declare module 'posipaki' {\n * interface ActorDecorated {\n * log: Logger;\n * }\n * }\n */\nexport interface ActorDecorated {}\n/** Reflection methods plugins and the framework add to every actor. Merged by\n * declaration, and held to {@link ReflectionOptions}' contract: async. */\nexport interface ActorReflection extends ReflectionOptions {}\n\n// ── chainHook ────────────────────────────────────────────────────────────\n\n/**\n * Compose two lifecycle hooks so that `plugin` fires first.\n *\n * If `plugin` returns the STOP_SENTINEL (via stopPropagation()),\n * `existing` is skipped entirely and the sentinel propagates.\n * Otherwise `existing` fires and its return value is used.\n *\n * Typical use in a plugin:\n * (cfg) => ({ ...cfg, onStart: chainHook(cfg.onStart, myOnStart) })\n *\n * @param existing - the current hook on the config (may be undefined)\n * @param plugin - the new hook to prepend\n * @returns a composed hook suitable for ActorConfig\n */\nexport function chainHook<TThis, TArgs extends unknown[]>(\n existing: ((this: TThis, ...args: TArgs) => HookResult | Promise<HookResult>) | undefined,\n plugin: (this: TThis, ...args: TArgs) => HookResult | Promise<HookResult>,\n): (this: TThis, ...args: TArgs) => HookResult | Promise<HookResult> {\n if (!existing) return plugin;\n return async function (this: TThis, ...args: TArgs): Promise<HookResult> {\n const result = await plugin.call(this, ...args);\n if (result === STOP_SENTINEL) return result;\n return await existing.call(this, ...args);\n } as (this: TThis, ...args: TArgs) => HookResult | Promise<HookResult>;\n}\n\n// ── mergeConfigs ─────────────────────────────────────────────────────────\n\n/**\n * Merge an overlay config into a base config, auto-chaining every on* hook.\n *\n * Each key in `overlay` that starts with \"on\" followed by an uppercase\n * letter (onStart, onMessage, onEmit, etc.) is composed via chainHook()\n * so that the overlay hook fires before the base hook, and stopPropagation\n * is respected.\n *\n * Typical use in a plugin:\n * return mergeConfigs(cfg, {\n * onStart() { this.log.info('starting'); },\n * beforeEnd() { this.log.info('stopping'); },\n * });\n *\n * @param base - the existing config (ActorConfig)\n * @param overlay - new hook implementations to prepend\n * @returns a new config with hooks chained\n */\nexport function mergeConfigs<C extends AnyConfig>(\n base: C,\n overlay: Partial<C> & ThisType<ActorContextOf<C>>,\n): C {\n const result = { ...base } as Record<string, unknown>;\n for (const key of Object.keys(overlay as Record<string, unknown>)) {\n const val = (overlay as Record<string, unknown>)[key];\n if (typeof val === \"function\" && /^(on|after|before)[A-Z]/.test(key)) {\n result[key] = chainHook(\n (base as Record<string, unknown>)[key] as (\n this: unknown,\n ...args: unknown[]\n ) => HookResult | Promise<HookResult>,\n val as (this: unknown, ...args: unknown[]) => HookResult | Promise<HookResult>,\n );\n } else {\n result[key] = val;\n }\n }\n result.$reflectionMethods = {};\n Object.assign(\n result.$reflectionMethods as object,\n \"$reflectionMethods\" in base ? base.$reflectionMethods : {},\n \"$reflectionMethods\" in overlay ? overlay.$reflectionMethods : {},\n );\n\n return result as unknown as C;\n}\n\nexport type Hook<T, I extends unknown[], O> = (this: T, ...args: I) => O;\n\nexport async function callHook<T, I extends unknown[], O>(\n fn: Hook<T, I, O> | undefined,\n eh: ((e: unknown) => unknown) | undefined,\n thisArg: T,\n ...args: I\n): Promise<O | undefined> {\n if (fn) {\n try {\n return await fn.call(thisArg, ...args);\n } catch (e) {\n if (eh) {\n let decision: unknown;\n try {\n decision = await eh(e);\n } catch {\n // An error inside the error handler must not mask the original one.\n }\n // An observing handler says so by returning the sentinel: the error it\n // did not handle keeps propagating, so the actor still goes down.\n if (decision === PROPAGATE_SENTINEL) throw e;\n } else {\n throw e;\n }\n }\n }\n return undefined;\n}\n"],"mappings":";;AA+PA,MAAa,gBAAgB,OAAO,0BAA0B;;AAG9D,MAAa,wBAA8C;;;;;;;;AAS3D,MAAa,qBAAqB,OAAO,yBAAyB;;AAGlE,MAAa,uBAAkD;;;;;;;;;;;;;;;;;AChN/D,SAAgB,UACd,UACA,QACmE;CACnE,IAAI,CAAC,UAAU,OAAO;CACtB,OAAO,eAA6B,GAAG,MAAkC;EACvE,MAAM,SAAS,MAAM,OAAO,KAAK,MAAM,GAAG,IAAI;EAC9C,IAAI,WAAW,eAAe,OAAO;EACrC,OAAO,MAAM,SAAS,KAAK,MAAM,GAAG,IAAI;CAC1C;AACF;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,aACd,MACA,SACG;CACH,MAAM,SAAS,EAAE,GAAG,KAAK;CACzB,KAAK,MAAM,OAAO,OAAO,KAAK,OAAkC,GAAG;EACjE,MAAM,MAAO,QAAoC;EACjD,IAAI,OAAO,QAAQ,cAAc,0BAA0B,KAAK,GAAG,GACjE,OAAO,OAAO,UACX,KAAiC,MAIlC,GACF;OAEA,OAAO,OAAO;CAElB;CACA,OAAO,qBAAqB,CAAC;CAC7B,OAAO,OACL,OAAO,oBACP,wBAAwB,OAAO,KAAK,qBAAqB,CAAC,GAC1D,wBAAwB,UAAU,QAAQ,qBAAqB,CAAC,CAClE;CAEA,OAAO;AACT;AAIA,eAAsB,SACpB,IACA,IACA,SACA,GAAG,MACqB;CACxB,IAAI,IACF,IAAI;EACF,OAAO,MAAM,GAAG,KAAK,SAAS,GAAG,IAAI;CACvC,SAAS,GAAG;EACV,IAAI,IAAI;GACN,IAAI;GACJ,IAAI;IACF,WAAW,MAAM,GAAG,CAAC;GACvB,QAAQ,CAER;GAGA,IAAI,aAAa,oBAAoB,MAAM;EAC7C,OACE,MAAM;CAEV;AAGJ"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as ProcessCtx, f as AnyProcess, i as Message, n as ExitMessage, p as AsyncProcess, r as ForkSite, s as SenderInfo, t as AsyncProcessFn } from "./types-DYd-DDFB.js";
|
|
2
2
|
//#region src/actor-types.d.ts
|
|
3
3
|
type ActorMessages<M extends Message> = {
|
|
4
4
|
__tag_messages: M;
|
|
@@ -50,7 +50,7 @@ interface ActorDefinition<Args, InternalState, InMsg extends Message, OutMsg ext
|
|
|
50
50
|
/** Spawn this actor as a child of the calling process.
|
|
51
51
|
* `ctx` must be able to fork children that emit `OutMsg` — i.e. the
|
|
52
52
|
* parent's in-message is a supertype of this actor's out-message. */
|
|
53
|
-
spawnAsChild
|
|
53
|
+
spawnAsChild(ctx: ForkSite, args: Args, opts?: {
|
|
54
54
|
name?: string;
|
|
55
55
|
parentPlugins?: ActorPlugin[];
|
|
56
56
|
addPlugins?: ActorPlugin[];
|
|
@@ -141,6 +141,12 @@ type ActorContext<Args, InternalState, InMsg extends Message, OutMsg extends Mes
|
|
|
141
141
|
state: InternalState;
|
|
142
142
|
name: string;
|
|
143
143
|
id: symbol;
|
|
144
|
+
/** This actor as a process: the handle another process can be given so that it can
|
|
145
|
+
* send here. There is no other way to obtain one — a process is reachable because
|
|
146
|
+
* somebody holds it, and this is how it hands itself over. The same object as
|
|
147
|
+
* `ctx.self`, and as the one a spawner, a parent or a remote handle stands for.
|
|
148
|
+
* Erased, like every handle that leaves: whoever receives it declares what it is. */
|
|
149
|
+
self: AsyncProcess<Args, HidePrivate<InternalState>, InMsg, OutMsg, ReflectionMethods & ActorReflection>;
|
|
144
150
|
emit: (msg: OutMsg) => void;
|
|
145
151
|
agreeToStop: () => void;
|
|
146
152
|
reflection: ThisType<ActorContext<Args, InternalState, InMsg, OutMsg, Methods, Handlers, ReflectionMethods>> & ReflectionMethods;
|
|
@@ -213,4 +219,4 @@ type Hook<T, I extends unknown[], O> = (this: T, ...args: I) => O;
|
|
|
213
219
|
declare function callHook<T, I extends unknown[], O>(fn: Hook<T, I, O> | undefined, eh: ((e: unknown) => unknown) | undefined, thisArg: T, ...args: I): Promise<O | undefined>;
|
|
214
220
|
//#endregion
|
|
215
221
|
export { stopPropagation as S, PluginTransform as _, chainHook as a, SpawnedFrom as b, ActorContext as c, ActorPlugin as d, HandlerFn as f, PROPAGATE_SENTINEL as g, MethodOptions as h, callHook as i, ActorDefinition as l, HookResult as m, ActorReflection as n, mergeConfigs as o, HandlerOptions as p, Hook as r, ActorConfig as s, ActorDecorated as t, ActorMessages as u, ReflectionOptions as v, propagateError as x, STOP_SENTINEL as y };
|
|
216
|
-
//# sourceMappingURL=hooks-
|
|
222
|
+
//# sourceMappingURL=hooks-D1QaP6Vv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks-D1QaP6Vv.d.ts","names":[],"sources":["../src/actor-types.ts","../src/hooks.ts"],"mappings":"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"}
|
package/dist/hooks.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { S as stopPropagation, _ as PluginTransform, a as chainHook, d as ActorPlugin, g as PROPAGATE_SENTINEL, i as callHook, m as HookResult, n as ActorReflection, o as mergeConfigs, r as Hook, t as ActorDecorated, x as propagateError, y as STOP_SENTINEL } from "./hooks-
|
|
1
|
+
import { S as stopPropagation, _ as PluginTransform, a as chainHook, d as ActorPlugin, g as PROPAGATE_SENTINEL, i as callHook, m as HookResult, n as ActorReflection, o as mergeConfigs, r as Hook, t as ActorDecorated, x as propagateError, y as STOP_SENTINEL } from "./hooks-D1QaP6Vv.js";
|
|
2
2
|
export { ActorDecorated, type ActorPlugin, ActorReflection, Hook, type HookResult, PROPAGATE_SENTINEL, type PluginTransform, STOP_SENTINEL, callHook, chainHook, mergeConfigs, propagateError, stopPropagation };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { S as stopPropagation, _ as PluginTransform, a as chainHook, b as SpawnedFrom, c as ActorContext, d as ActorPlugin, f as HandlerFn, h as MethodOptions, i as callHook, l as ActorDefinition, m as HookResult, n as ActorReflection, o as mergeConfigs, p as HandlerOptions, s as ActorConfig, t as ActorDecorated, u as ActorMessages, v as ReflectionOptions, x as propagateError } from "./hooks-
|
|
3
|
-
import { n as spawn, t as Process } from "./process-
|
|
1
|
+
import { a as ProcessCtx, c as SenderOrigin, d as WithoutSender, f as AnyProcess, h as spawnAsync, i as Message, l as StopMessage, m as runDispatchAsync, n as ExitMessage, o as ProcessFn, p as AsyncProcess, s as SenderInfo, t as AsyncProcessFn, u as WithSender } from "./types-DYd-DDFB.js";
|
|
2
|
+
import { S as stopPropagation, _ as PluginTransform, a as chainHook, b as SpawnedFrom, c as ActorContext, d as ActorPlugin, f as HandlerFn, h as MethodOptions, i as callHook, l as ActorDefinition, m as HookResult, n as ActorReflection, o as mergeConfigs, p as HandlerOptions, s as ActorConfig, t as ActorDecorated, u as ActorMessages, v as ReflectionOptions, x as propagateError } from "./hooks-D1QaP6Vv.js";
|
|
3
|
+
import { n as spawn, t as Process } from "./process-BVwU7TQo.js";
|
|
4
4
|
//#region src/util.d.ts
|
|
5
5
|
type ReducerClosure<M> = (msg: M) => void;
|
|
6
6
|
type ReadyFn = () => boolean;
|
|
@@ -20,7 +20,7 @@ declare function defineActor<Args, InternalState, InMsg extends Message, OutMsg
|
|
|
20
20
|
//#endregion
|
|
21
21
|
//#region src/version.d.ts
|
|
22
22
|
/** posipaki's release version. Must equal package.json's `version`. */
|
|
23
|
-
declare const LIB_VERSION = "0.
|
|
23
|
+
declare const LIB_VERSION = "0.41.0";
|
|
24
24
|
//#endregion
|
|
25
25
|
export { type ActorConfig, type ActorContext, type ActorDecorated, type ActorDefinition, type ActorPlugin, type ActorReflection, type AnyProcess, AsyncProcess, type AsyncProcessFn, type ExitMessage, type HandlerFn, type HandlerOptions, type HookResult, LIB_VERSION, type Message, type MethodOptions, type PluginTransform, Process, type ProcessCtx, type ProcessFn, type SenderInfo, type SenderOrigin, type SpawnedFrom, type StopMessage, type WithSender, type WithoutSender, asyncify, callHook, chainHook, defineActor, defineMessages, mergeConfigs, propagateError, runDispatch, runDispatchAsync, spawn, spawnAsync, stopPropagation };
|
|
26
26
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/util.ts","../src/adapters.ts","../src/define-actor.ts","../src/version.ts"],"mappings":";;;;KAOK,eAAe,MAAM,KAAK;KAC1B;;;;;;iBASK,YAAY,GACpB,cACA,IAAI,eAAe,IACnB,UAAS,SACT,uBACC,sBAAsB;;;iBCpBT,SAAS,GAAG,GAAG,WAAW,SAAS,WAAW,SAC5D,IAAI,UAAU,GAAG,GAAG,IAAI,MACvB,eAAe,GAAG,GAAG,IAAI;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/util.ts","../src/adapters.ts","../src/define-actor.ts","../src/version.ts"],"mappings":";;;;KAOK,eAAe,MAAM,KAAK;KAC1B;;;;;;iBASK,YAAY,GACpB,cACA,IAAI,eAAe,IACnB,UAAS,SACT,uBACC,sBAAsB;;;iBCpBT,SAAS,GAAG,GAAG,WAAW,SAAS,WAAW,SAC5D,IAAI,UAAU,GAAG,GAAG,IAAI,MACvB,eAAe,GAAG,GAAG,IAAI;;;iBCgCZ,eAAe,eAAe,UAAU,YAAY,cAAc;iBA2DlE,YACd,MACA,eACA,cAAc,SACd,eAAe,SACf,gBAAgB,eAChB,iBAAiB,eAAe,QAChC,0BAA0B,mBAE1B,QAAQ,YAAY,MAAM,eAAe,OAAO,QAAQ,SAAS,UAAU,qBAC1E,gBAAgB,MAAM,eAAe,OAAO,QAAQ;;;;cC3F1C"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { i as runDispatch } from "./util-Brtfgv68.js";
|
|
2
|
-
import { a as spawnAsync, i as runDispatchAsync, o as asyncify, t as AsyncProcess } from "./process.async-
|
|
2
|
+
import { a as spawnAsync, i as runDispatchAsync, o as asyncify, t as AsyncProcess } from "./process.async-C_-NpMFX.js";
|
|
3
3
|
import { n as chainHook, o as propagateError, r as mergeConfigs, s as stopPropagation, t as callHook } from "./hooks-CgXhmY4r.js";
|
|
4
|
-
import { n as defineMessages, t as defineActor } from "./define-actor-
|
|
5
|
-
import { t as LIB_VERSION } from "./version-
|
|
4
|
+
import { n as defineMessages, t as defineActor } from "./define-actor-D7c9vn6S.js";
|
|
5
|
+
import { t as LIB_VERSION } from "./version-CYQgj_GV.js";
|
|
6
6
|
//#region src/process.ts
|
|
7
7
|
/** @deprecated Use {@link spawnAsync} instead. */
|
|
8
8
|
function spawn(fn, pname, tp) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { r as makeWaiter } from "./util-Brtfgv68.js";
|
|
2
|
-
import { r as isProcess } from "./process.async-
|
|
2
|
+
import { r as isProcess } from "./process.async-C_-NpMFX.js";
|
|
3
3
|
import { s as stopPropagation } from "./hooks-CgXhmY4r.js";
|
|
4
|
-
import { t as defineActor } from "./define-actor-
|
|
4
|
+
import { t as defineActor } from "./define-actor-D7c9vn6S.js";
|
|
5
5
|
//#region src/remote/process-ref.ts
|
|
6
6
|
/** The key that marks a process reference inside an otherwise JSON value. */
|
|
7
7
|
const PROCESS_REF = "$p";
|
|
@@ -238,6 +238,46 @@ function isMsg(msg) {
|
|
|
238
238
|
function isExit(msg) {
|
|
239
239
|
return "$exit" in msg;
|
|
240
240
|
}
|
|
241
|
+
const BAD_KEY = "$bad";
|
|
242
|
+
/** How much of an unreadable line is carried: enough to see what it was, never enough to be it.
|
|
243
|
+
* A line that failed to parse is the one line nobody vouched for, so quoting it whole would move
|
|
244
|
+
* whatever it holds — a credential in a paste accident is how that happens — into every log,
|
|
245
|
+
* transcript and context window that later reads the frame. Why, how long, and a short head of
|
|
246
|
+
* it. (`reason` carries a little of the line on its own: a JSON parser says where it gave up and
|
|
247
|
+
* quotes what it was looking at. That is the parser's business, not a promise this file makes.) */
|
|
248
|
+
const BAD_PREFIX_LIMIT = 32;
|
|
249
|
+
function isBad(msg) {
|
|
250
|
+
return BAD_KEY in msg;
|
|
251
|
+
}
|
|
252
|
+
/** The frame that says a frame could not be read: why, how long it was, and the head of it. */
|
|
253
|
+
function badFrame(reason, raw) {
|
|
254
|
+
const prefix = raw.length > 32 ? `${raw.slice(0, 32)}…` : raw;
|
|
255
|
+
return { [BAD_KEY]: {
|
|
256
|
+
reason,
|
|
257
|
+
length: raw.length,
|
|
258
|
+
prefix
|
|
259
|
+
} };
|
|
260
|
+
}
|
|
261
|
+
/** Why a frame could not be read, or null when it is not a frame that says so. */
|
|
262
|
+
function badReason(msg) {
|
|
263
|
+
const body = msg[BAD_KEY];
|
|
264
|
+
if (typeof body !== "object" || body === null) return null;
|
|
265
|
+
const reason = body.reason;
|
|
266
|
+
return typeof reason === "string" ? reason : null;
|
|
267
|
+
}
|
|
268
|
+
const ERROR_KEY = "$error";
|
|
269
|
+
function isError(msg) {
|
|
270
|
+
return ERROR_KEY in msg;
|
|
271
|
+
}
|
|
272
|
+
/** The frame that says the wire is over: the reason, and the connection ends with it. */
|
|
273
|
+
function errorFrame(reason) {
|
|
274
|
+
return { [ERROR_KEY]: reason };
|
|
275
|
+
}
|
|
276
|
+
/** The reason a wire error carries, or null when it is not one. */
|
|
277
|
+
function errorReason(msg) {
|
|
278
|
+
const reason = msg[ERROR_KEY];
|
|
279
|
+
return typeof reason === "string" ? reason : null;
|
|
280
|
+
}
|
|
241
281
|
/** The three, in the one order they are ever named in — the order a tune frame
|
|
242
282
|
* carries them in, so two ways of asking for the same thing look the same. */
|
|
243
283
|
const STREAM_KINDS = [
|
|
@@ -1019,7 +1059,13 @@ async function serveRemoteActor(actor, spawner) {
|
|
|
1019
1059
|
});
|
|
1020
1060
|
});
|
|
1021
1061
|
channel.removeHandler();
|
|
1022
|
-
const
|
|
1062
|
+
const firstFrame = waiting.shift();
|
|
1063
|
+
const initProblem = badReason(firstFrame) ?? errorReason(firstFrame);
|
|
1064
|
+
if (initProblem !== null) {
|
|
1065
|
+
await write(errorFrame(`expected $init: ${initProblem}`)).catch(() => void 0);
|
|
1066
|
+
throw new Error(`serveRemoteActor: expected $init (${initProblem})`);
|
|
1067
|
+
}
|
|
1068
|
+
const initFrame = decodeFrame(firstFrame, handleFor);
|
|
1023
1069
|
if (!isInit(initFrame)) throw new Error("serveRemoteActor: expected $init");
|
|
1024
1070
|
const init = initFrame.$init;
|
|
1025
1071
|
const stated = init.rootName;
|
|
@@ -1040,7 +1086,28 @@ async function serveRemoteActor(actor, spawner) {
|
|
|
1040
1086
|
channel.onClose(() => {
|
|
1041
1087
|
proc.stop();
|
|
1042
1088
|
});
|
|
1089
|
+
/**
|
|
1090
|
+
* A frame that was not a frame is the connection's problem and not the process's: the far side is
|
|
1091
|
+
* told what could not be read, and then this connection ends — the actor it was serving ends with
|
|
1092
|
+
* it, that being what it was spawned for. The host everything else runs in is never the cost of
|
|
1093
|
+
* one peer that is not speaking the protocol.
|
|
1094
|
+
*
|
|
1095
|
+
* The write is awaited only so the reason is out before the wire closes. A wire that has already
|
|
1096
|
+
* gone ends nothing twice, and the ending is the part that matters.
|
|
1097
|
+
*/
|
|
1098
|
+
const endUnreadable = async (reason) => {
|
|
1099
|
+
console.warn(`a frame from the far side could not be read: ${reason}`);
|
|
1100
|
+
try {
|
|
1101
|
+
await write(errorFrame(reason));
|
|
1102
|
+
} catch {}
|
|
1103
|
+
await proc.stop();
|
|
1104
|
+
};
|
|
1043
1105
|
function dispatch(raw) {
|
|
1106
|
+
const wireProblem = badReason(raw) ?? errorReason(raw);
|
|
1107
|
+
if (wireProblem !== null) {
|
|
1108
|
+
endUnreadable(wireProblem);
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1044
1111
|
const frame = decodeFrame(raw, handleFor);
|
|
1045
1112
|
if (calls.settle(frame)) return;
|
|
1046
1113
|
const to = frameTo(frame);
|
|
@@ -1204,7 +1271,30 @@ function remoteClient(name, spawner) {
|
|
|
1204
1271
|
code: null,
|
|
1205
1272
|
state: currentState
|
|
1206
1273
|
}));
|
|
1274
|
+
/**
|
|
1275
|
+
* The wire is over, whoever ended it: the far side stopped talking, or stopped making sense.
|
|
1276
|
+
* What was in flight fails, handles disconnect, nothing more is said about the processes of
|
|
1277
|
+
* this side's own, and the proxy ends with CHANNEL_LOST — a proxy that stayed up would be one
|
|
1278
|
+
* whose parent still believes the far side is listening, which is exactly the belief a display
|
|
1279
|
+
* panel that went dark punishes.
|
|
1280
|
+
*
|
|
1281
|
+
* Said once: a wire can go in more than one way at once, and it only goes once.
|
|
1282
|
+
*/
|
|
1283
|
+
let wireEnded = false;
|
|
1284
|
+
const wireGone = () => {
|
|
1285
|
+
if (wireEnded) return;
|
|
1286
|
+
wireEnded = true;
|
|
1287
|
+
calls.rejectAll();
|
|
1288
|
+
for (const held of table.handles()) if (isRemoteProcess(held)) held.disconnect();
|
|
1289
|
+
streams.stopAll();
|
|
1290
|
+
this.exit(CHANNEL_LOST);
|
|
1291
|
+
};
|
|
1207
1292
|
channel.onMessage((raw) => {
|
|
1293
|
+
if ((badReason(raw) ?? errorReason(raw)) !== null) {
|
|
1294
|
+
channel.close();
|
|
1295
|
+
wireGone();
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1208
1298
|
const frame = decodeFrame(raw, handleFor);
|
|
1209
1299
|
if (calls.settle(frame)) return;
|
|
1210
1300
|
const to = frameTo(frame);
|
|
@@ -1256,12 +1346,7 @@ function remoteClient(name, spawner) {
|
|
|
1256
1346
|
const call = asReflectionCall(frame);
|
|
1257
1347
|
if (call) answerCall(call, mine, write, table);
|
|
1258
1348
|
});
|
|
1259
|
-
channel.onClose(() =>
|
|
1260
|
-
calls.rejectAll();
|
|
1261
|
-
for (const held of table.handles()) if (isRemoteProcess(held)) held.disconnect();
|
|
1262
|
-
streams.stopAll();
|
|
1263
|
-
this.exit(CHANNEL_LOST);
|
|
1264
|
-
});
|
|
1349
|
+
channel.onClose(() => wireGone());
|
|
1265
1350
|
await start;
|
|
1266
1351
|
return {
|
|
1267
1352
|
public: currentState,
|
|
@@ -1296,16 +1381,35 @@ const VERSION = "json.v1";
|
|
|
1296
1381
|
function encode(frame) {
|
|
1297
1382
|
return JSON.stringify(frame);
|
|
1298
1383
|
}
|
|
1384
|
+
/** The frame a line carries, throwing on a line that is not JSON. `readFrame` is how the
|
|
1385
|
+
* seam reads a line; this is the primitive underneath it. */
|
|
1299
1386
|
function decode(str) {
|
|
1300
1387
|
return JSON.parse(str);
|
|
1301
1388
|
}
|
|
1389
|
+
/** A line read as a frame: the frame itself, or the frame that says this one could not be
|
|
1390
|
+
* read. Never throws — see the note at the top of this file. */
|
|
1391
|
+
function readFrame(str) {
|
|
1392
|
+
let parsed;
|
|
1393
|
+
try {
|
|
1394
|
+
parsed = decode(str);
|
|
1395
|
+
} catch (err) {
|
|
1396
|
+
return badFrame(err instanceof Error ? err.message : String(err), str);
|
|
1397
|
+
}
|
|
1398
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return badFrame(`a frame is one JSON object, and this was ${kindOf(parsed)}`, str);
|
|
1399
|
+
return parsed;
|
|
1400
|
+
}
|
|
1401
|
+
function kindOf(value) {
|
|
1402
|
+
if (value === null) return "null";
|
|
1403
|
+
if (Array.isArray(value)) return "an array";
|
|
1404
|
+
return `a ${typeof value}`;
|
|
1405
|
+
}
|
|
1302
1406
|
function json1Channel(transport) {
|
|
1303
1407
|
return {
|
|
1304
1408
|
send(frame) {
|
|
1305
1409
|
return transport.send(encode(frame));
|
|
1306
1410
|
},
|
|
1307
1411
|
onMessage(handler) {
|
|
1308
|
-
transport.onMessage((str) => handler(
|
|
1412
|
+
transport.onMessage((str) => handler(readFrame(str)));
|
|
1309
1413
|
},
|
|
1310
1414
|
removeHandler() {
|
|
1311
1415
|
transport.removeHandler();
|
|
@@ -1319,6 +1423,6 @@ function json1Channel(transport) {
|
|
|
1319
1423
|
};
|
|
1320
1424
|
}
|
|
1321
1425
|
//#endregion
|
|
1322
|
-
export { isState as _,
|
|
1426
|
+
export { isRelease as C, PROCESS_REF as D, isStop as E, isRemoteProcess as O, isProto as S, isState as T, isError as _, readFrame as a, isMsg as b, serveRemoteActor as c, BAD_PREFIX_LIMIT as d, badFrame as f, isBad as g, errorReason as h, json1Channel as i, rootIdFor as k, makeSender as l, errorFrame as m, decode as n, CHANNEL_LOST as o, badReason as p, encode as r, remoteClient as s, VERSION as t, RemoteProcess as u, isExit as v, isResume as w, isPause as x, isInit as y };
|
|
1323
1427
|
|
|
1324
|
-
//# sourceMappingURL=json1-
|
|
1428
|
+
//# sourceMappingURL=json1-BWi4HzI4.js.map
|