posipaki 0.37.1 → 0.38.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/define-actor-CCJy7LDp.js +189 -0
- package/dist/define-actor-CCJy7LDp.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/{json1-DYq9g7im.js → json1-DZ8N0TuZ.js} +191 -5
- package/dist/json1-DZ8N0TuZ.js.map +1 -0
- package/dist/plugins/tree-introspection.d.ts +22 -2
- package/dist/plugins/tree-introspection.d.ts.map +1 -1
- package/dist/plugins/tree-introspection.js +47 -4
- package/dist/plugins/tree-introspection.js.map +1 -1
- package/dist/{define-actor-CZysIKH4.js → process.async-CdQ_zpKp.js} +2 -186
- package/dist/process.async-CdQ_zpKp.js.map +1 -0
- package/dist/remote/index.d.ts +67 -1
- package/dist/remote/index.d.ts.map +1 -1
- package/dist/remote/index.js +1 -1
- package/dist/remote/node.js +2 -2
- package/dist/{version-98a9sol-.js → version-BnBkASor.js} +2 -2
- package/dist/{version-98a9sol-.js.map → version-BnBkASor.js.map} +1 -1
- package/package.json +1 -1
- package/dist/define-actor-CZysIKH4.js.map +0 -1
- package/dist/json1-DYq9g7im.js.map +0 -1
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { a as spawnAsync, i as runDispatchAsync } from "./process.async-CdQ_zpKp.js";
|
|
2
|
+
import { a as STOP_SENTINEL, i as PROPAGATE_SENTINEL, t as callHook } from "./hooks-CgXhmY4r.js";
|
|
3
|
+
//#region src/define-actor.ts
|
|
4
|
+
function defineMessages() {}
|
|
5
|
+
function hidePrivate(value) {
|
|
6
|
+
if (value && typeof value === "object" && "private" in value && "public" in value) return value.public;
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
function resolvePlugins(raw, parentPlugins, addPlugins) {
|
|
10
|
+
const resolved = !raw ? parentPlugins ? [...parentPlugins] : [] : Array.isArray(raw) ? [...raw] : raw(parentPlugins ?? []);
|
|
11
|
+
const combined = addPlugins ? [...resolved, ...addPlugins] : resolved;
|
|
12
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13
|
+
return combined.filter((p) => {
|
|
14
|
+
const name = p.name;
|
|
15
|
+
if (!name) {
|
|
16
|
+
console.warn("posipaki: plugin has no name, dedup won't work");
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
if (seen.has(name)) return false;
|
|
20
|
+
seen.add(name);
|
|
21
|
+
return true;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async function assembleActor(config, plugins, addPlugins) {
|
|
25
|
+
let cur = config;
|
|
26
|
+
for (const p of plugins) try {
|
|
27
|
+
cur = await p(cur);
|
|
28
|
+
} catch (e) {
|
|
29
|
+
console.error(`[assembleActor] plugin "${p.name || "?"}" failed:`, e);
|
|
30
|
+
}
|
|
31
|
+
cur.resolvedPlugins = plugins;
|
|
32
|
+
if (addPlugins) cur.addPlugins = addPlugins;
|
|
33
|
+
return cur;
|
|
34
|
+
}
|
|
35
|
+
function defineActor(config) {
|
|
36
|
+
const actorCtxMap = /* @__PURE__ */ new Map();
|
|
37
|
+
function makeRuntime(assembly) {
|
|
38
|
+
return async function* (ctx, args) {
|
|
39
|
+
let done = false;
|
|
40
|
+
let exitReason;
|
|
41
|
+
let rawState = void 0;
|
|
42
|
+
const decorated = /* @__PURE__ */ new Map();
|
|
43
|
+
const self = {
|
|
44
|
+
ctx,
|
|
45
|
+
...assembly.methods || {},
|
|
46
|
+
...assembly.$decorate || {},
|
|
47
|
+
reflection: {},
|
|
48
|
+
state: rawState,
|
|
49
|
+
name: ctx.pname,
|
|
50
|
+
id: ctx.id,
|
|
51
|
+
async emit(msg) {
|
|
52
|
+
await callHook(assembly.onEmit, void 0, self, msg, {
|
|
53
|
+
fromName: ctx.pname,
|
|
54
|
+
fromId: ctx.id
|
|
55
|
+
});
|
|
56
|
+
ctx.toParent(msg);
|
|
57
|
+
},
|
|
58
|
+
agreeToStop() {
|
|
59
|
+
exitReason = "stopped";
|
|
60
|
+
done = true;
|
|
61
|
+
},
|
|
62
|
+
exit(reason) {
|
|
63
|
+
exitReason = reason;
|
|
64
|
+
done = true;
|
|
65
|
+
ctx.wake();
|
|
66
|
+
},
|
|
67
|
+
$child: {},
|
|
68
|
+
decorators: {},
|
|
69
|
+
fork: async (childActor, childArgs, forkOpts) => {
|
|
70
|
+
let child;
|
|
71
|
+
const childName = forkOpts?.name ?? childActor?.name ?? `child-${Object.keys(self.$child).length}`;
|
|
72
|
+
const treeName = `${ctx.pname}:${childName}`;
|
|
73
|
+
const childAddPlugins = [...assembly.addPlugins || [], ...forkOpts?.addPlugins || []];
|
|
74
|
+
child = await childActor.spawnAsChild(ctx, childArgs, {
|
|
75
|
+
name: treeName,
|
|
76
|
+
parentPlugins: assembly.resolvedPlugins || [],
|
|
77
|
+
...childAddPlugins.length ? { addPlugins: childAddPlugins } : {}
|
|
78
|
+
});
|
|
79
|
+
self.$child[child.pname] = child;
|
|
80
|
+
return child;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
actorCtxMap.set(ctx.id, self);
|
|
84
|
+
/**
|
|
85
|
+
* Lifecycle hooks are the actor's own control flow: if one throws, the
|
|
86
|
+
* state machine is half-applied, so the error is fatal — `onError` still
|
|
87
|
+
* observes it (a logger logs it), but it cannot absorb it. The actor goes
|
|
88
|
+
* down and its parent still gets the EXIT, instead of staying up with
|
|
89
|
+
* nothing left to do.
|
|
90
|
+
*
|
|
91
|
+
* Message hooks and handlers are not forced either way: they are called
|
|
92
|
+
* with `onError` itself, and its *return value* decides — nothing means
|
|
93
|
+
* "handled, carry on" (the default, and what an observing logger
|
|
94
|
+
* returns), `propagateError()` means "not handled", and the actor goes
|
|
95
|
+
* down with the same EXIT.
|
|
96
|
+
*/
|
|
97
|
+
const hookErrorHandler = async (e) => {
|
|
98
|
+
try {
|
|
99
|
+
await assembly.onError?.call(self, e);
|
|
100
|
+
} catch {}
|
|
101
|
+
return PROPAGATE_SENTINEL;
|
|
102
|
+
};
|
|
103
|
+
ctx.afterExit = async () => {
|
|
104
|
+
await callHook(assembly.afterEnd, hookErrorHandler, self, exitReason);
|
|
105
|
+
};
|
|
106
|
+
for (const [k, v] of decorated) self[k] = v;
|
|
107
|
+
await callHook(assembly.beforeStart, hookErrorHandler, self);
|
|
108
|
+
if (assembly.setup) rawState = await assembly.setup.call(self, args);
|
|
109
|
+
else rawState = null;
|
|
110
|
+
self.state = rawState;
|
|
111
|
+
yield hidePrivate(rawState);
|
|
112
|
+
await callHook(assembly.afterStart, hookErrorHandler, self);
|
|
113
|
+
yield* runDispatchAsync(ctx.pname, async (stamped) => {
|
|
114
|
+
const [msg, sender] = stamped;
|
|
115
|
+
if (msg.type === "__ADVANCE__") return;
|
|
116
|
+
if (msg.type === "STOP") {
|
|
117
|
+
if (assembly.onStopRequested) await callHook(assembly.onStopRequested, hookErrorHandler, self);
|
|
118
|
+
else {
|
|
119
|
+
exitReason = "stopped";
|
|
120
|
+
done = true;
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (msg.type === "EXIT") {
|
|
125
|
+
const childName = sender.fromName;
|
|
126
|
+
if (childName && self.$child[childName]) delete self.$child[childName];
|
|
127
|
+
await callHook(assembly.onChildExit, hookErrorHandler, self, childName, msg);
|
|
128
|
+
const orphans = msg.orphans;
|
|
129
|
+
if (orphans && orphans.length > 0) for (const orphan of orphans) {
|
|
130
|
+
const decision = assembly.onOrphan ? await callHook(assembly.onOrphan, hookErrorHandler, self, orphan) : "force-stop";
|
|
131
|
+
if (decision === "adopt") {
|
|
132
|
+
ctx.adopt(orphan);
|
|
133
|
+
const idx = ctx.orphans.indexOf(orphan);
|
|
134
|
+
if (idx >= 0) ctx.orphans.splice(idx, 1);
|
|
135
|
+
self.$child[orphan.pname] = orphan;
|
|
136
|
+
} else if (decision === "force-stop") {
|
|
137
|
+
orphan.forceStop({ cascade: true });
|
|
138
|
+
const idx = ctx.orphans.indexOf(orphan);
|
|
139
|
+
if (idx >= 0) ctx.orphans.splice(idx, 1);
|
|
140
|
+
} else if (decision === "unparent") orphan.unparent();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
let hookStopped = false;
|
|
144
|
+
if (msg.type !== "STOP" && msg.type !== "EXIT" && assembly.onMessage) {
|
|
145
|
+
if (await callHook(assembly.onMessage, assembly.onError, self, msg, sender) === STOP_SENTINEL) hookStopped = true;
|
|
146
|
+
}
|
|
147
|
+
if (msg.type !== "STOP" && !hookStopped) {
|
|
148
|
+
const handler = (assembly.handlers || {})[msg.type] || assembly.onUnhandled;
|
|
149
|
+
await callHook(handler, assembly.onError, self, msg, sender);
|
|
150
|
+
}
|
|
151
|
+
}, () => done);
|
|
152
|
+
await callHook(assembly.beforeEnd, hookErrorHandler, self, exitReason);
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function attachReflection(proc, reflectionMethods) {
|
|
156
|
+
const self = actorCtxMap.get(proc.id);
|
|
157
|
+
if (self) proc.$reflection = self.reflection;
|
|
158
|
+
if (!reflectionMethods) return;
|
|
159
|
+
const refl = proc.$reflection;
|
|
160
|
+
const written = Object.entries(reflectionMethods);
|
|
161
|
+
for (const [k, m] of written) refl[k] = async (...args) => await m.apply(self, args);
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
fn: makeRuntime(config),
|
|
165
|
+
name: config.name,
|
|
166
|
+
pvtPluginsRaw: config.plugins,
|
|
167
|
+
inMessages: config.inMessages,
|
|
168
|
+
outMessages: config.outMessages,
|
|
169
|
+
async spawn(args, opts) {
|
|
170
|
+
const assembly = await assembleActor(config, resolvePlugins(config.plugins, void 0, opts?.addPlugins), opts?.addPlugins);
|
|
171
|
+
const runtime = makeRuntime(assembly);
|
|
172
|
+
const proc = spawnAsync(runtime, opts?.name ?? assembly.name ?? "actor", opts?.toParent, opts?.parentName, opts?.parentId)(args);
|
|
173
|
+
attachReflection(proc, assembly.$reflectionMethods);
|
|
174
|
+
if (opts?.awaitReady !== false) await proc.ready();
|
|
175
|
+
return proc;
|
|
176
|
+
},
|
|
177
|
+
async spawnAsChild(ctx, args, opts) {
|
|
178
|
+
const assembly = await assembleActor(config, resolvePlugins(config.plugins, opts?.parentPlugins, opts?.addPlugins), opts?.addPlugins);
|
|
179
|
+
const runtime = makeRuntime(assembly);
|
|
180
|
+
const proc = ctx.fork(runtime, opts?.name ?? assembly.name ?? "child")(args);
|
|
181
|
+
attachReflection(proc, assembly.$reflectionMethods);
|
|
182
|
+
return proc;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
//#endregion
|
|
187
|
+
export { defineMessages as n, defineActor as t };
|
|
188
|
+
|
|
189
|
+
//# sourceMappingURL=define-actor-CCJy7LDp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-actor-CCJy7LDp.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 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 const decorated = new Map();\n const self: ActorContext<\n Args,\n InternalState,\n InMsg,\n OutMsg,\n Methods,\n Handlers,\n ReflectionMethods & ActorReflection\n > = {\n ctx,\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, self, msg, {\n fromName: ctx.pname,\n fromId: ctx.id,\n });\n ctx.toParent(msg);\n },\n agreeToStop() {\n exitReason = \"stopped\";\n done = true;\n },\n exit(reason: unknown) {\n 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 ctx.wake();\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(self.$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 self.$child[child.pname] = child as unknown as AnyProcess;\n return child;\n },\n };\n actorCtxMap.set(ctx.id, self);\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(self, 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, self, exitReason);\n };\n\n for (const [k, v] of decorated) {\n (self as Record<string, unknown>)[k] = v;\n }\n\n await callHook(\n assembly.beforeStart,\n hookErrorHandler,\n self 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 self 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 self.state = rawState;\n yield hidePrivate(rawState);\n await callHook(assembly.afterStart, hookErrorHandler, self);\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, self);\n // Hook may call agreeToStop(). If not, actor keeps running.\n } else {\n exitReason = \"stopped\";\n done = true;\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 && self.$child[childName]) {\n delete self.$child[childName];\n }\n await callHook(\n assembly.onChildExit,\n hookErrorHandler,\n self,\n childName,\n msg as ExitMessage,\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, self, 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 self.$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 self,\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, self, msg as InMsg, sender);\n }\n },\n () => done,\n );\n\n await callHook(assembly.beforeEnd, hookErrorHandler, self, 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 self = actorCtxMap.get(proc.id);\n if (self) {\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 = self.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(self, 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: ProcessCtx<unknown, unknown, OutMsg, Message>,\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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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -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.38.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.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { i as runDispatch } from "./util-Brtfgv68.js";
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as spawnAsync, i as runDispatchAsync, o as asyncify, t as AsyncProcess } from "./process.async-CdQ_zpKp.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 { t as
|
|
4
|
+
import { n as defineMessages, t as defineActor } from "./define-actor-CCJy7LDp.js";
|
|
5
|
+
import { t as LIB_VERSION } from "./version-BnBkASor.js";
|
|
5
6
|
//#region src/process.ts
|
|
6
7
|
/** @deprecated Use {@link spawnAsync} instead. */
|
|
7
8
|
function spawn(fn, pname, tp) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/process.ts","../src/index.ts"],"sourcesContent":["import { AsyncProcess } from \"./process.async.js\";\nimport { asyncify } from \"./adapters.js\";\nimport type { Message, SenderInfo, 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<A, S, IM extends Message = Message, OM extends Message = ExitMessage>(\n fn: ProcessFn<A, S, IM, OM>,\n pname: string,\n tp?: (m: OM, from: SenderInfo) => void,\n): (a: A) => Process<A, S, IM, OM> {\n return (a: A) => new Process(fn, pname, tp).start(a);\n}\n\n/** @deprecated Use {@link AsyncProcess} instead. Sync processes are\n * wrapped via asyncify internally; spawnAsync handles both. */\nclass Process<A, S, IM extends Message, OM extends Message> extends AsyncProcess<A, S, IM, OM, {}> {\n constructor(fn: ProcessFn<A, S, IM, OM>, pname: string, tp?: (m: OM, from: SenderInfo) => void) {\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.pvtTick();\n }\n}\n\nexport { Process };\n","/**\n * Posipaki — Erlang-inspired lightweight actor processes built on\n * generator functions. Processes communicate via message-passing,\n * can fork children, and expose their state reactively.\n *\n * @module\n */\n\nimport { Process, spawn } from \"./process.js\";\nimport { runDispatch } from \"./util.js\";\nimport { AsyncProcess, spawnAsync, runDispatchAsync } from \"./process.async.js\";\nimport { asyncify } from \"./adapters.js\";\n\nexport { Process, spawn, runDispatch };\nexport { AsyncProcess, spawnAsync, runDispatchAsync, asyncify };\nexport type { AnyProcess } from \"./process.async.js\";\n\nexport type {\n Message,\n WithSender,\n WithoutSender,\n ExitMessage,\n StopMessage,\n ProcessFn,\n ProcessCtx,\n AsyncProcessFn,\n SenderOrigin,\n SenderInfo,\n} from \"./types.js\";\n\nexport { defineActor, defineMessages } from \"./define-actor.js\";\nexport type {\n ActorDefinition,\n SpawnedFrom,\n ActorConfig,\n ActorContext,\n MethodOptions,\n HandlerOptions,\n HandlerFn,\n} from \"./actor-types.js\";\n\n// ── hooks ─────────────────────────────────────────────────────────────────\n\nexport { stopPropagation, propagateError, mergeConfigs, chainHook, callHook } from \"./hooks.js\";\nexport type {\n HookResult,\n ActorPlugin,\n ActorDecorated,\n ActorReflection,\n PluginTransform,\n} from \"./hooks.js\";\nexport { LIB_VERSION } from \"./version.js\";\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/process.ts","../src/index.ts"],"sourcesContent":["import { AsyncProcess } from \"./process.async.js\";\nimport { asyncify } from \"./adapters.js\";\nimport type { Message, SenderInfo, 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<A, S, IM extends Message = Message, OM extends Message = ExitMessage>(\n fn: ProcessFn<A, S, IM, OM>,\n pname: string,\n tp?: (m: OM, from: SenderInfo) => void,\n): (a: A) => Process<A, S, IM, OM> {\n return (a: A) => new Process(fn, pname, tp).start(a);\n}\n\n/** @deprecated Use {@link AsyncProcess} instead. Sync processes are\n * wrapped via asyncify internally; spawnAsync handles both. */\nclass Process<A, S, IM extends Message, OM extends Message> extends AsyncProcess<A, S, IM, OM, {}> {\n constructor(fn: ProcessFn<A, S, IM, OM>, pname: string, tp?: (m: OM, from: SenderInfo) => void) {\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.pvtTick();\n }\n}\n\nexport { Process };\n","/**\n * Posipaki — Erlang-inspired lightweight actor processes built on\n * generator functions. Processes communicate via message-passing,\n * can fork children, and expose their state reactively.\n *\n * @module\n */\n\nimport { Process, spawn } from \"./process.js\";\nimport { runDispatch } from \"./util.js\";\nimport { AsyncProcess, spawnAsync, runDispatchAsync } from \"./process.async.js\";\nimport { asyncify } from \"./adapters.js\";\n\nexport { Process, spawn, runDispatch };\nexport { AsyncProcess, spawnAsync, runDispatchAsync, asyncify };\nexport type { AnyProcess } from \"./process.async.js\";\n\nexport type {\n Message,\n WithSender,\n WithoutSender,\n ExitMessage,\n StopMessage,\n ProcessFn,\n ProcessCtx,\n AsyncProcessFn,\n SenderOrigin,\n SenderInfo,\n} from \"./types.js\";\n\nexport { defineActor, defineMessages } from \"./define-actor.js\";\nexport type {\n ActorDefinition,\n SpawnedFrom,\n ActorConfig,\n ActorContext,\n MethodOptions,\n HandlerOptions,\n HandlerFn,\n} from \"./actor-types.js\";\n\n// ── hooks ─────────────────────────────────────────────────────────────────\n\nexport { stopPropagation, propagateError, mergeConfigs, chainHook, callHook } from \"./hooks.js\";\nexport type {\n HookResult,\n ActorPlugin,\n ActorDecorated,\n ActorReflection,\n PluginTransform,\n} from \"./hooks.js\";\nexport { LIB_VERSION } from \"./version.js\";\n"],"mappings":";;;;;;;AAQA,SAAgB,MACd,IACA,OACA,IACiC;CACjC,QAAQ,MAAS,IAAI,QAAQ,IAAI,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;AACrD;;;AAIA,IAAM,UAAN,cAAoE,aAA+B;CACjG,YAAY,IAA6B,OAAe,IAAwC;EAC9F,MAAM,SAAS,EAAE,GAAG,OAAO,EAAE;CAC/B;CAEA,MAAM,GAAM;EACV,MAAM,MAAM,CAAC;EACb,OAAO;CACT;CACA,OAAsB;EACpB,OAAO,MAAM,QAAQ;CACvB;AACF"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { r as makeWaiter } from "./util-Brtfgv68.js";
|
|
2
|
-
import {
|
|
2
|
+
import { r as isProcess } from "./process.async-CdQ_zpKp.js";
|
|
3
3
|
import { s as stopPropagation } from "./hooks-CgXhmY4r.js";
|
|
4
|
+
import { t as defineActor } from "./define-actor-CCJy7LDp.js";
|
|
4
5
|
//#region src/remote/process-ref.ts
|
|
5
6
|
/** The key that marks a process reference inside an otherwise JSON value. */
|
|
6
7
|
const PROCESS_REF = "$p";
|
|
@@ -340,6 +341,42 @@ function jsonProblem(value) {
|
|
|
340
341
|
}
|
|
341
342
|
//#endregion
|
|
342
343
|
//#region src/remote/remote-process.ts
|
|
344
|
+
/**
|
|
345
|
+
* What a reference with nothing keeping it amounts to. It names a process this side can
|
|
346
|
+
* hear nothing from and ask nothing of, so it is not a process in any sense that matters
|
|
347
|
+
* here, and it is written as what it is wherever it turns up: a state somebody is reading,
|
|
348
|
+
* a log line, anything at all that stringifies.
|
|
349
|
+
*/
|
|
350
|
+
const UNSTABLE_REFERENCE = "UnstableReference";
|
|
351
|
+
/** What a process left behind when it ended. */
|
|
352
|
+
/**
|
|
353
|
+
* The references inside a value that crossed. A handle is the one thing in a frame that is
|
|
354
|
+
* not JSON, so wherever one sits, it came here on somebody else's frame and whoever is
|
|
355
|
+
* holding that value is holding it. Plain objects and arrays only: a value of another kind
|
|
356
|
+
* is not a shape this walks, and its references are not found.
|
|
357
|
+
*/
|
|
358
|
+
function refsInside(value) {
|
|
359
|
+
const found = [];
|
|
360
|
+
const seen = /* @__PURE__ */ new Set();
|
|
361
|
+
const walk = (node) => {
|
|
362
|
+
if (isRemoteProcess(node)) {
|
|
363
|
+
found.push(node);
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
if (typeof node !== "object" || node === null) return;
|
|
367
|
+
if (seen.has(node)) return;
|
|
368
|
+
seen.add(node);
|
|
369
|
+
if (Array.isArray(node)) {
|
|
370
|
+
for (const item of node) walk(item);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const proto = Object.getPrototypeOf(node);
|
|
374
|
+
if (proto !== Object.prototype && proto !== null) return;
|
|
375
|
+
for (const item of Object.values(node)) walk(item);
|
|
376
|
+
};
|
|
377
|
+
walk(value);
|
|
378
|
+
return found;
|
|
379
|
+
}
|
|
343
380
|
var RemoteProcess = class {
|
|
344
381
|
constructor(ref, send, fromName, letGo) {
|
|
345
382
|
this.state = null;
|
|
@@ -350,6 +387,10 @@ var RemoteProcess = class {
|
|
|
350
387
|
this.pvtExitWaiter = null;
|
|
351
388
|
this.pvtMessageSubs = [];
|
|
352
389
|
this.pvtStateSubs = [];
|
|
390
|
+
this.pvtReadyWaiters = [];
|
|
391
|
+
this.pvtRefCount = 0;
|
|
392
|
+
this.pvtOwnedRefs = [];
|
|
393
|
+
this.pvtTransientRefs = [];
|
|
353
394
|
this.pvtWanted = /* @__PURE__ */ new Set();
|
|
354
395
|
this.ref = ref;
|
|
355
396
|
this.pname = ref.pname;
|
|
@@ -380,6 +421,7 @@ var RemoteProcess = class {
|
|
|
380
421
|
if (!this.pvtConnected) return;
|
|
381
422
|
this.pvtConnected = false;
|
|
382
423
|
this.pvtStateSubs.forEach((fn) => fn());
|
|
424
|
+
this.pvtSettleReady();
|
|
383
425
|
this.pvtExitWaiter?.reject(/* @__PURE__ */ new Error(`${this.pname} cannot be reached: the connection is closed`));
|
|
384
426
|
}
|
|
385
427
|
/** Whether the process it names has ended, as far as this side has been told. */
|
|
@@ -400,6 +442,121 @@ var RemoteProcess = class {
|
|
|
400
442
|
if (why !== null) throw new Error(why);
|
|
401
443
|
}
|
|
402
444
|
/**
|
|
445
|
+
* Keep this reference: from here it is counted, which is what lets anything be asked of
|
|
446
|
+
* it. It returns the handle, so what is kept is written in one line —
|
|
447
|
+
* `this.pvtRemoteRefsHeld.push(ref.holdRef())` — and a table of them reads as what it is.
|
|
448
|
+
*/
|
|
449
|
+
holdRef() {
|
|
450
|
+
this.pvtRefCount += 1;
|
|
451
|
+
return this;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Give the count back. Whoever gives the last one back lets the reference go, which is
|
|
455
|
+
* what tells the far side that nothing here wants to hear about the process again. A
|
|
456
|
+
* reference nobody ever kept is let go of by this too, which is how whoever receives one
|
|
457
|
+
* they do not want is rid of it.
|
|
458
|
+
*/
|
|
459
|
+
releaseRef() {
|
|
460
|
+
if (this.pvtRefCount > 0) this.pvtRefCount -= 1;
|
|
461
|
+
if (this.pvtRefCount > 0) return;
|
|
462
|
+
if (this.pvtWhyNotAskable() === null) {
|
|
463
|
+
this.release();
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
this.pvtDropBinding();
|
|
467
|
+
}
|
|
468
|
+
/** Let go of the binding here without asking the far side for anything: what the process
|
|
469
|
+
* was is over, or the wire that carried it is. */
|
|
470
|
+
pvtDropBinding() {
|
|
471
|
+
if (this.pvtReleased) return;
|
|
472
|
+
this.pvtReleased = true;
|
|
473
|
+
this.pvtLetGo?.(this.ref.id);
|
|
474
|
+
this.pvtSettleReady();
|
|
475
|
+
}
|
|
476
|
+
/** How many times this reference has been kept. */
|
|
477
|
+
refCount() {
|
|
478
|
+
return this.pvtRefCount;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* What this is, as text: a handle nothing here keeps is an unstable reference and says
|
|
482
|
+
* so, and one that is kept says which process it names and by which id.
|
|
483
|
+
*/
|
|
484
|
+
[Symbol.for("nodejs.util.inspect.custom")]() {
|
|
485
|
+
return this.pvtRefCount === 0 ? UNSTABLE_REFERENCE : `RemoteProcess(${this.ref.id}: ${this.pname})`;
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* What this is, in JSON: the same reference the wire would carry, or the unstable thing
|
|
489
|
+
* it is when nothing keeps it. Written so that an unheld reference cannot be mistaken
|
|
490
|
+
* for a process that merely looks odd in a state, and so that one that is kept reads as
|
|
491
|
+
* the reference it is rather than as whatever fields it happens to have.
|
|
492
|
+
*/
|
|
493
|
+
toJSON() {
|
|
494
|
+
if (this.pvtRefCount === 0) return UNSTABLE_REFERENCE;
|
|
495
|
+
return { ["$p"]: {
|
|
496
|
+
id: this.ref.id,
|
|
497
|
+
pname: this.pname
|
|
498
|
+
} };
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Remember the references inside a value that arrived here to be passed on. They are not
|
|
502
|
+
* counted — the receiver is the one to deal with them — and this is what keeps one from
|
|
503
|
+
* being nobody's for ever: when this handle is let go of, they are dropped with it.
|
|
504
|
+
*/
|
|
505
|
+
markTransient(value) {
|
|
506
|
+
for (const ref of refsInside(value)) {
|
|
507
|
+
if (ref.id === this.id || ref.pvtReleased) continue;
|
|
508
|
+
this.pvtDropTransient(ref);
|
|
509
|
+
if (this.pvtOwnedRefs.includes(ref) || this.pvtTransientRefs.includes(ref)) continue;
|
|
510
|
+
this.pvtTransientRefs.push(ref);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
/** Stop treating a reference as one that was only passing through. */
|
|
514
|
+
pvtDropTransient(ref) {
|
|
515
|
+
const at = this.pvtTransientRefs.indexOf(ref);
|
|
516
|
+
if (at >= 0) this.pvtTransientRefs.splice(at, 1);
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Keep the references sitting in what this process holds. What it holds is here, so the
|
|
520
|
+
* thing keeping those references alive is this handle, and it holds them until it is let
|
|
521
|
+
* go of itself.
|
|
522
|
+
*/
|
|
523
|
+
pvtHoldOwned(value) {
|
|
524
|
+
for (const ref of refsInside(value)) {
|
|
525
|
+
if (ref.id === this.id || ref.pvtReleased) continue;
|
|
526
|
+
if (this.pvtOwnedRefs.includes(ref)) continue;
|
|
527
|
+
this.pvtDropTransient(ref);
|
|
528
|
+
this.pvtOwnedRefs.push(ref.holdRef());
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
/** Nothing is heard about a process nothing here keeps. A subscription, a tuning and a
|
|
532
|
+
* wait for the state are all ways of hearing, so they are where a reference has to have
|
|
533
|
+
* been kept first — and where the mistake says what to do about it. */
|
|
534
|
+
pvtRequireHeld(what) {
|
|
535
|
+
if (this.pvtRefCount > 0) return;
|
|
536
|
+
throw new Error(`${this.pname} is not held by anyone here: call holdRef() to keep this reference, or releaseRef() to let it go, before ${what}`);
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Wait until what it holds is here.
|
|
540
|
+
*
|
|
541
|
+
* A handle starts out knowing nothing about the process: the far side says what a
|
|
542
|
+
* process holds when something over here asks to hear it, so the asking and the waiting
|
|
543
|
+
* are one act, and this is both. It settles with whatever has arrived when nothing more
|
|
544
|
+
* can: a process that has ended has said its last, and a connection that has gone has
|
|
545
|
+
* nothing left to say. Asking twice is asking about what is already here.
|
|
546
|
+
*/
|
|
547
|
+
ready() {
|
|
548
|
+
if (this.state !== null || this.pvtWhyNotAskable() !== null) return Promise.resolve();
|
|
549
|
+
this.pvtRequireHeld("waiting for what it holds");
|
|
550
|
+
this.pvtWant("state");
|
|
551
|
+
return new Promise((resolve) => this.pvtReadyWaiters.push(resolve));
|
|
552
|
+
}
|
|
553
|
+
/** Everyone waiting for what it holds has heard it, or never will. */
|
|
554
|
+
pvtSettleReady() {
|
|
555
|
+
const waiting = this.pvtReadyWaiters;
|
|
556
|
+
this.pvtReadyWaiters = [];
|
|
557
|
+
for (const resolve of waiting) resolve();
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
403
560
|
* Wait for the process to end. Its exit crosses back like everything else it
|
|
404
561
|
* does, and what it left behind is the answer.
|
|
405
562
|
*
|
|
@@ -435,8 +592,15 @@ var RemoteProcess = class {
|
|
|
435
592
|
release() {
|
|
436
593
|
this.pvtRequireAskable();
|
|
437
594
|
this.pvtReleased = true;
|
|
595
|
+
const owned = this.pvtOwnedRefs;
|
|
596
|
+
this.pvtOwnedRefs = [];
|
|
597
|
+
for (const ref of owned) if (!ref.pvtReleased) ref.releaseRef();
|
|
598
|
+
const transient = this.pvtTransientRefs;
|
|
599
|
+
this.pvtTransientRefs = [];
|
|
600
|
+
for (const ref of transient) if (!ref.pvtReleased && ref.pvtRefCount === 0) ref.release();
|
|
438
601
|
this.pvtLetGo?.(this.ref.id);
|
|
439
602
|
this.pvtSend({ $release: {} }, this.ref.id);
|
|
603
|
+
this.pvtSettleReady();
|
|
440
604
|
}
|
|
441
605
|
/** Stop feeding it messages. It is still there: `send` still reaches it. */
|
|
442
606
|
pause() {
|
|
@@ -460,6 +624,7 @@ var RemoteProcess = class {
|
|
|
460
624
|
*/
|
|
461
625
|
tune(kinds) {
|
|
462
626
|
this.pvtRequireAskable();
|
|
627
|
+
this.pvtRequireHeld("tuning what is heard about it");
|
|
463
628
|
this.pvtTune(kinds === "silent" ? [] : kinds);
|
|
464
629
|
}
|
|
465
630
|
/** Ask the far side to say exactly this much, and nothing when it is already
|
|
@@ -488,6 +653,7 @@ var RemoteProcess = class {
|
|
|
488
653
|
} }, this.ref.id);
|
|
489
654
|
}
|
|
490
655
|
subscribe(channel, cb) {
|
|
656
|
+
this.pvtRequireHeld(`subscribing to what it ${channel === "message" ? "says" : "holds"}`);
|
|
491
657
|
if (channel === "message") {
|
|
492
658
|
const fn = cb;
|
|
493
659
|
this.pvtMessageSubs.push(fn);
|
|
@@ -507,11 +673,14 @@ var RemoteProcess = class {
|
|
|
507
673
|
receiveState(state) {
|
|
508
674
|
if (this.pvtReleased) return;
|
|
509
675
|
this.state = Object.assign(this.state ?? {}, state);
|
|
676
|
+
this.pvtHoldOwned(state);
|
|
510
677
|
this.pvtStateSubs.forEach((fn) => fn());
|
|
678
|
+
this.pvtSettleReady();
|
|
511
679
|
}
|
|
512
680
|
/** A message it emitted. */
|
|
513
681
|
receiveMessage(msg, fromName) {
|
|
514
682
|
if (this.pvtReleased) return;
|
|
683
|
+
this.markTransient(msg);
|
|
515
684
|
this.pvtMessageSubs.forEach((fn) => fn(msg, fromName));
|
|
516
685
|
}
|
|
517
686
|
/** It has ended, and this is what it left behind. The last thing said about a
|
|
@@ -521,12 +690,17 @@ var RemoteProcess = class {
|
|
|
521
690
|
if (this.pvtExit !== null || this.pvtReleased) return;
|
|
522
691
|
this.pvtExit = exit;
|
|
523
692
|
this.pvtExitWaiter?.resolve(exit);
|
|
693
|
+
this.pvtSettleReady();
|
|
524
694
|
}
|
|
525
695
|
/** The methods it can answer, as functions that ask it for an answer. `call`
|
|
526
696
|
* is the connection's own: a handle knows what to ask for, not how a call is
|
|
527
697
|
* made or paired with its answer. */
|
|
528
698
|
receiveMethods(names, call) {
|
|
529
|
-
for (const name of names) this.$reflection[name] = (...args) =>
|
|
699
|
+
for (const name of names) this.$reflection[name] = async (...args) => {
|
|
700
|
+
const value = await call(name, args);
|
|
701
|
+
this.markTransient(value);
|
|
702
|
+
return value;
|
|
703
|
+
};
|
|
530
704
|
}
|
|
531
705
|
};
|
|
532
706
|
//#endregion
|
|
@@ -716,9 +890,20 @@ var CallSide = class {
|
|
|
716
890
|
});
|
|
717
891
|
});
|
|
718
892
|
}
|
|
719
|
-
/**
|
|
893
|
+
/**
|
|
894
|
+
* One function per announced name, on the surface whoever calls reads.
|
|
895
|
+
*
|
|
896
|
+
* An answer that carries a reference hands it to whoever asked, which makes it theirs to
|
|
897
|
+
* deal with: the handle that was asked is told about it, so that one nobody keeps is let
|
|
898
|
+
* go of with that handle rather than being nobody's for ever.
|
|
899
|
+
*/
|
|
720
900
|
install(surface, methods, to) {
|
|
721
|
-
for (const method of methods) surface[method] = (...callArgs) =>
|
|
901
|
+
for (const method of methods) surface[method] = async (...callArgs) => {
|
|
902
|
+
const value = await this.call(method, callArgs, to);
|
|
903
|
+
const owner = this.pvtTable.farHandleFor(to);
|
|
904
|
+
if (isRemoteProcess(owner)) owner.markTransient(value);
|
|
905
|
+
return value;
|
|
906
|
+
};
|
|
722
907
|
}
|
|
723
908
|
/**
|
|
724
909
|
* An answer to a call this side made: the call waiting under that `seq` is settled
|
|
@@ -980,6 +1165,7 @@ function remoteClient(name, spawner) {
|
|
|
980
1165
|
pname: fromName
|
|
981
1166
|
}, sendTo, fromName, (id) => table.release(id));
|
|
982
1167
|
table.bindFar(table.farRootId(), farRoot);
|
|
1168
|
+
farRoot.holdRef();
|
|
983
1169
|
let started = null;
|
|
984
1170
|
const start = new Promise((resolve) => {
|
|
985
1171
|
started = resolve;
|
|
@@ -1121,4 +1307,4 @@ function json1Channel(transport) {
|
|
|
1121
1307
|
//#endregion
|
|
1122
1308
|
export { isStop as _, remoteClient as a, rootIdFor as b, RemoteProcess as c, isMsg as d, isPause as f, isState as g, isResume as h, json1Channel as i, isExit as l, isRelease as m, decode as n, serveRemoteActor as o, isProto as p, encode as r, makeSender as s, VERSION as t, isInit as u, PROCESS_REF as v, isRemoteProcess as y };
|
|
1123
1309
|
|
|
1124
|
-
//# sourceMappingURL=json1-
|
|
1310
|
+
//# sourceMappingURL=json1-DZ8N0TuZ.js.map
|