@mysten-incubation/devstack 0.2.0 → 0.3.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/dashboard-ui/assets/{grpc-a4usE3Nk.js → grpc-Clz0oOtc.js} +1 -1
- package/dashboard-ui/assets/{index-CRYJ4pru.js → index-Cmqv9kiU.js} +39 -33
- package/dashboard-ui/index.html +1 -1
- package/dist/api/run-stack-internal.mjs +1 -0
- package/dist/api/run-stack-internal.mjs.map +1 -1
- package/dist/build-integrations/playwright/config.d.mts +2 -1
- package/dist/build-integrations/playwright/config.mjs +1 -1
- package/dist/build-integrations/playwright/config.mjs.map +1 -1
- package/dist/build-integrations/vite/index.d.mts +39 -9
- package/dist/build-integrations/vite/index.mjs +47 -11
- package/dist/build-integrations/vite/index.mjs.map +1 -1
- package/dist/build-integrations/vitest/config.mjs +3 -4
- package/dist/build-integrations/vitest/config.mjs.map +1 -1
- package/dist/cli/doctor-probes.mjs +7 -2
- package/dist/cli/doctor-probes.mjs.map +1 -1
- package/dist/cli/main.mjs +2 -2
- package/dist/cli/main.mjs.map +1 -1
- package/dist/cli/wirings/codegen.mjs +35 -35
- package/dist/cli/wirings/codegen.mjs.map +1 -1
- package/dist/cli/wirings/up.mjs +7 -0
- package/dist/cli/wirings/up.mjs.map +1 -1
- package/dist/orchestrators/boot.mjs +12 -2
- package/dist/orchestrators/boot.mjs.map +1 -1
- package/dist/orchestrators/codegen/service.mjs +23 -8
- package/dist/orchestrators/codegen/service.mjs.map +1 -1
- package/dist/plugins/dashboard/domain.mjs +18 -1
- package/dist/plugins/dashboard/domain.mjs.map +1 -1
- package/dist/plugins/dashboard/schema/root.mjs +2 -2
- package/dist/plugins/dashboard/schema/root.mjs.map +1 -1
- package/dist/plugins/dashboard/schema/types.mjs +15 -2
- package/dist/plugins/dashboard/schema/types.mjs.map +1 -1
- package/dist/plugins/deepbook/index.d.mts +7 -3
- package/dist/plugins/deepbook/index.mjs +3 -3
- package/dist/plugins/deepbook/index.mjs.map +1 -1
- package/dist/plugins/sui/index.d.mts +10 -10
- package/dist/plugins/sui/move-summary-runner.mjs +29 -2
- package/dist/plugins/sui/move-summary-runner.mjs.map +1 -1
- package/dist/substrate/runtime/index.mjs +1 -0
- package/dist/substrate/runtime/lifecycle/file-watcher.d.mts +1 -0
- package/dist/substrate/runtime/lifecycle/file-watcher.mjs +54 -0
- package/dist/substrate/runtime/lifecycle/file-watcher.mjs.map +1 -0
- package/dist/substrate/runtime/lifecycle/index.mjs +1 -0
- package/dist/substrate/runtime/lifecycle/watch-attribution.mjs +76 -6
- package/dist/substrate/runtime/lifecycle/watch-attribution.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/start-supervisor.mjs +2 -6
- package/dist/substrate/runtime/supervisor/start-supervisor.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -14,13 +14,83 @@ const buildWatchIndex = (nodes) => {
|
|
|
14
14
|
return out;
|
|
15
15
|
};
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* Given the firing `firedPath` (already debounced + content-hash dedup'd
|
|
18
|
+
* by L0), return the plugin keys whose declared paths match.
|
|
19
|
+
*
|
|
20
|
+
* Matching is glob-aware via minimatch semantics — the substrate-level
|
|
21
|
+
* attribution here delegates to a supplied `match` predicate to keep
|
|
22
|
+
* this module dependency-free. The supervisor wires the matcher in.
|
|
21
23
|
*/
|
|
22
|
-
const
|
|
24
|
+
const attribute = (index, firedPath, match) => {
|
|
25
|
+
const out = /* @__PURE__ */ new Set();
|
|
26
|
+
for (const entry of index) for (const pattern of entry.paths) if (match(pattern, firedPath)) {
|
|
27
|
+
out.add(entry.pluginKey);
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
};
|
|
32
|
+
const REGEXP_SPECIALS = new Set(".+^${}()|[]\\".split(""));
|
|
33
|
+
/**
|
|
34
|
+
* Compile a watch glob to an anchored `RegExp`. Supports the only glob
|
|
35
|
+
* features the package plugin emits (architecture § WatchDecl):
|
|
36
|
+
* - `**\/` — zero or more path segments (so `root/**\/*.move` matches both
|
|
37
|
+
* `root/x.move` and `root/sources/x.move`)
|
|
38
|
+
* - `**` — any characters including `/` (trailing-segment form)
|
|
39
|
+
* - `*` — any characters except `/` (within one segment)
|
|
40
|
+
* - `?` — one character except `/`
|
|
41
|
+
* Everything else is matched literally. This is the dependency-free stand-in
|
|
42
|
+
* for the architecture's "minimatch" matcher — devstack pulls in no glob
|
|
43
|
+
* library (cf. the hand-rolled `collectHashedSources`).
|
|
44
|
+
*/
|
|
45
|
+
const globToRegExp = (glob) => {
|
|
46
|
+
let body = "";
|
|
47
|
+
for (let i = 0; i < glob.length; i += 1) {
|
|
48
|
+
const c = glob[i];
|
|
49
|
+
if (c === "*" && glob[i + 1] === "*") if (glob[i + 2] === "/") {
|
|
50
|
+
body += "(?:.*/)?";
|
|
51
|
+
i += 2;
|
|
52
|
+
} else {
|
|
53
|
+
body += ".*";
|
|
54
|
+
i += 1;
|
|
55
|
+
}
|
|
56
|
+
else if (c === "*") body += "[^/]*";
|
|
57
|
+
else if (c === "?") body += "[^/]";
|
|
58
|
+
else if (REGEXP_SPECIALS.has(c)) body += `\\${c}`;
|
|
59
|
+
else body += c;
|
|
60
|
+
}
|
|
61
|
+
return new RegExp(`^${body}$`);
|
|
62
|
+
};
|
|
63
|
+
/** Glob-aware matcher — the production matcher the supervisor wires into
|
|
64
|
+
* `attribute`. Honors `**`, `*`, `?`; everything else is literal. Both
|
|
65
|
+
* sides are normalized to POSIX separators first: the watcher feeds paths
|
|
66
|
+
* from `join(root, filename)`, which is `\`-separated on Windows, while
|
|
67
|
+
* glob patterns are always `/`-separated. */
|
|
68
|
+
const toPosix = (p) => p.replace(/\\/g, "/");
|
|
69
|
+
const globMatch = (pattern, path) => globToRegExp(toPosix(pattern)).test(toPosix(path));
|
|
70
|
+
/** Literal directory prefix of a watch glob — the path up to (but not
|
|
71
|
+
* including) the first segment containing a glob metacharacter. Both
|
|
72
|
+
* `root/**\/*.move` and the exact `root/Move.toml` collapse to `root`,
|
|
73
|
+
* so the L0 watcher places ONE recursive watch per source tree. A
|
|
74
|
+
* pattern whose literal prefix has no directory separator — a bare
|
|
75
|
+
* basename (`Move.toml`), a leading glob (`*.move` ⇒ empty prefix), or a
|
|
76
|
+
* same-segment glob (`src*`) — collapses to `.` (the cwd), never to a
|
|
77
|
+
* bare filename: `fs.watch` must be handed a directory, and `globMatch`
|
|
78
|
+
* still filters the events. */
|
|
79
|
+
const literalRoot = (pattern) => {
|
|
80
|
+
const metaIdx = pattern.search(/[*?]/);
|
|
81
|
+
const literal = metaIdx === -1 ? pattern : pattern.slice(0, metaIdx);
|
|
82
|
+
const slash = literal.lastIndexOf("/");
|
|
83
|
+
if (slash === 0) return "/";
|
|
84
|
+
return slash > 0 ? literal.slice(0, slash) : ".";
|
|
85
|
+
};
|
|
86
|
+
/** Distinct recursive-watch roots for a watch index — the directories the
|
|
87
|
+
* L0 file watcher hands to `fs.watch(root, { recursive: true })`. */
|
|
88
|
+
const deriveWatchRoots = (index) => {
|
|
89
|
+
const roots = /* @__PURE__ */ new Set();
|
|
90
|
+
for (const entry of index) for (const pattern of entry.paths) roots.add(literalRoot(pattern));
|
|
91
|
+
return [...roots];
|
|
92
|
+
};
|
|
23
93
|
//#endregion
|
|
24
|
-
export { buildWatchIndex,
|
|
94
|
+
export { attribute, buildWatchIndex, deriveWatchRoots, globMatch };
|
|
25
95
|
|
|
26
96
|
//# sourceMappingURL=watch-attribution.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watch-attribution.mjs","names":[],"sources":["../../../../src/substrate/runtime/lifecycle/watch-attribution.ts"],"sourcesContent":["// Watch-path → owning plugin attribution.\n//\n// Architecture § L3 Watch dispatcher: \"collects all plugin `watch`\n// declarations, receives watcher events from L0 (which already\n// debounced + dedup'd), triggers selective restart through the\n// scheduler's invalidate-with-cascade.\"\n//\n// L0 owns the file watcher (debounce + content-hash dedup). The\n// supervisor's role at watch time is just: given a fired path, which\n// plugin row(s) should be invalidated? This module builds and queries\n// that index. Selective-restart is the supervisor's concern; watch\n// attribution stays pure.\n\nimport type { PluginKey } from '../../brand.ts';\nimport type { DepNode } from './dep-graph.ts';\n\n/** Per-plugin watch entry — a copy of the plugin's declared `watch`\n * decl with the owning key attached. */\nexport interface WatchEntry {\n\treadonly pluginKey: PluginKey;\n\treadonly paths: ReadonlyArray<string>;\n\t/** Whether downstream consumers should cascade-invalidate when this\n\t * fires. Defaults to true (architecture § WatchDecl). */\n\treadonly cascade: boolean;\n}\n\n/** Build the watch index from a resolved dep-graph's nodes. */\nexport const buildWatchIndex = (\n\tnodes: ReadonlyMap<PluginKey, DepNode>,\n): ReadonlyArray<WatchEntry> => {\n\tconst out: WatchEntry[] = [];\n\tfor (const [key, node] of nodes) {\n\t\tconst watch = node.member.watch;\n\t\tif (watch === undefined || watch.paths.length === 0) continue;\n\t\tout.push({\n\t\t\tpluginKey: key,\n\t\t\tpaths: watch.paths,\n\t\t\tcascade: watch.cascade ?? true,\n\t\t});\n\t}\n\treturn out;\n};\n\n/**\n * Given the firing `firedPath` (already debounced + content-hash dedup'd\n * by L0), return the plugin keys whose declared paths match.\n *\n * Matching is glob-aware via minimatch semantics — the substrate-level\n * attribution here delegates to a supplied `match` predicate to keep\n * this module dependency-free. The supervisor wires the matcher in.\n */\nexport const attribute = (\n\tindex: ReadonlyArray<WatchEntry>,\n\tfiredPath: string,\n\tmatch: (pattern: string, path: string) => boolean,\n): ReadonlySet<PluginKey> => {\n\tconst out = new Set<PluginKey>();\n\tfor (const entry of index) {\n\t\tfor (const pattern of entry.paths) {\n\t\t\tif (match(pattern, firedPath)) {\n\t\t\t\tout.add(entry.pluginKey);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn out;\n};\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"watch-attribution.mjs","names":[],"sources":["../../../../src/substrate/runtime/lifecycle/watch-attribution.ts"],"sourcesContent":["// Watch-path → owning plugin attribution.\n//\n// Architecture § L3 Watch dispatcher: \"collects all plugin `watch`\n// declarations, receives watcher events from L0 (which already\n// debounced + dedup'd), triggers selective restart through the\n// scheduler's invalidate-with-cascade.\"\n//\n// L0 owns the file watcher (debounce + content-hash dedup). The\n// supervisor's role at watch time is just: given a fired path, which\n// plugin row(s) should be invalidated? This module builds and queries\n// that index. Selective-restart is the supervisor's concern; watch\n// attribution stays pure.\n\nimport type { PluginKey } from '../../brand.ts';\nimport type { DepNode } from './dep-graph.ts';\n\n/** Per-plugin watch entry — a copy of the plugin's declared `watch`\n * decl with the owning key attached. */\nexport interface WatchEntry {\n\treadonly pluginKey: PluginKey;\n\treadonly paths: ReadonlyArray<string>;\n\t/** Whether downstream consumers should cascade-invalidate when this\n\t * fires. Defaults to true (architecture § WatchDecl). */\n\treadonly cascade: boolean;\n}\n\n/** Build the watch index from a resolved dep-graph's nodes. */\nexport const buildWatchIndex = (\n\tnodes: ReadonlyMap<PluginKey, DepNode>,\n): ReadonlyArray<WatchEntry> => {\n\tconst out: WatchEntry[] = [];\n\tfor (const [key, node] of nodes) {\n\t\tconst watch = node.member.watch;\n\t\tif (watch === undefined || watch.paths.length === 0) continue;\n\t\tout.push({\n\t\t\tpluginKey: key,\n\t\t\tpaths: watch.paths,\n\t\t\tcascade: watch.cascade ?? true,\n\t\t});\n\t}\n\treturn out;\n};\n\n/**\n * Given the firing `firedPath` (already debounced + content-hash dedup'd\n * by L0), return the plugin keys whose declared paths match.\n *\n * Matching is glob-aware via minimatch semantics — the substrate-level\n * attribution here delegates to a supplied `match` predicate to keep\n * this module dependency-free. The supervisor wires the matcher in.\n */\nexport const attribute = (\n\tindex: ReadonlyArray<WatchEntry>,\n\tfiredPath: string,\n\tmatch: (pattern: string, path: string) => boolean,\n): ReadonlySet<PluginKey> => {\n\tconst out = new Set<PluginKey>();\n\tfor (const entry of index) {\n\t\tfor (const pattern of entry.paths) {\n\t\t\tif (match(pattern, firedPath)) {\n\t\t\t\tout.add(entry.pluginKey);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn out;\n};\n\nconst REGEXP_SPECIALS = new Set('.+^${}()|[]\\\\'.split(''));\n\n/**\n * Compile a watch glob to an anchored `RegExp`. Supports the only glob\n * features the package plugin emits (architecture § WatchDecl):\n * - `**\\/` — zero or more path segments (so `root/**\\/*.move` matches both\n * `root/x.move` and `root/sources/x.move`)\n * - `**` — any characters including `/` (trailing-segment form)\n * - `*` — any characters except `/` (within one segment)\n * - `?` — one character except `/`\n * Everything else is matched literally. This is the dependency-free stand-in\n * for the architecture's \"minimatch\" matcher — devstack pulls in no glob\n * library (cf. the hand-rolled `collectHashedSources`).\n */\nconst globToRegExp = (glob: string): RegExp => {\n\tlet body = '';\n\tfor (let i = 0; i < glob.length; i += 1) {\n\t\tconst c = glob[i]!;\n\t\tif (c === '*' && glob[i + 1] === '*') {\n\t\t\tif (glob[i + 2] === '/') {\n\t\t\t\tbody += '(?:.*/)?';\n\t\t\t\ti += 2;\n\t\t\t} else {\n\t\t\t\tbody += '.*';\n\t\t\t\ti += 1;\n\t\t\t}\n\t\t} else if (c === '*') {\n\t\t\tbody += '[^/]*';\n\t\t} else if (c === '?') {\n\t\t\tbody += '[^/]';\n\t\t} else if (REGEXP_SPECIALS.has(c)) {\n\t\t\tbody += `\\\\${c}`;\n\t\t} else {\n\t\t\tbody += c;\n\t\t}\n\t}\n\treturn new RegExp(`^${body}$`);\n};\n\n/** Glob-aware matcher — the production matcher the supervisor wires into\n * `attribute`. Honors `**`, `*`, `?`; everything else is literal. Both\n * sides are normalized to POSIX separators first: the watcher feeds paths\n * from `join(root, filename)`, which is `\\`-separated on Windows, while\n * glob patterns are always `/`-separated. */\nconst toPosix = (p: string): string => p.replace(/\\\\/g, '/');\n\nexport const globMatch = (pattern: string, path: string): boolean =>\n\tglobToRegExp(toPosix(pattern)).test(toPosix(path));\n\n/** Literal directory prefix of a watch glob — the path up to (but not\n * including) the first segment containing a glob metacharacter. Both\n * `root/**\\/*.move` and the exact `root/Move.toml` collapse to `root`,\n * so the L0 watcher places ONE recursive watch per source tree. A\n * pattern whose literal prefix has no directory separator — a bare\n * basename (`Move.toml`), a leading glob (`*.move` ⇒ empty prefix), or a\n * same-segment glob (`src*`) — collapses to `.` (the cwd), never to a\n * bare filename: `fs.watch` must be handed a directory, and `globMatch`\n * still filters the events. */\nconst literalRoot = (pattern: string): string => {\n\tconst metaIdx = pattern.search(/[*?]/);\n\tconst literal = metaIdx === -1 ? pattern : pattern.slice(0, metaIdx);\n\tconst slash = literal.lastIndexOf('/');\n\tif (slash === 0) return '/'; // pattern sits at filesystem root\n\treturn slash > 0 ? literal.slice(0, slash) : '.';\n};\n\n/** Distinct recursive-watch roots for a watch index — the directories the\n * L0 file watcher hands to `fs.watch(root, { recursive: true })`. */\nexport const deriveWatchRoots = (index: ReadonlyArray<WatchEntry>): ReadonlyArray<string> => {\n\tconst roots = new Set<string>();\n\tfor (const entry of index) {\n\t\tfor (const pattern of entry.paths) {\n\t\t\troots.add(literalRoot(pattern));\n\t\t}\n\t}\n\treturn [...roots];\n};\n"],"mappings":";;AA2BA,MAAa,mBACZ,UAC+B;CAC/B,MAAM,MAAoB,CAAC;CAC3B,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO;EAChC,MAAM,QAAQ,KAAK,OAAO;EAC1B,IAAI,UAAU,KAAA,KAAa,MAAM,MAAM,WAAW,GAAG;EACrD,IAAI,KAAK;GACR,WAAW;GACX,OAAO,MAAM;GACb,SAAS,MAAM,WAAW;EAC3B,CAAC;CACF;CACA,OAAO;AACR;;;;;;;;;AAUA,MAAa,aACZ,OACA,WACA,UAC4B;CAC5B,MAAM,sBAAM,IAAI,IAAe;CAC/B,KAAK,MAAM,SAAS,OACnB,KAAK,MAAM,WAAW,MAAM,OAC3B,IAAI,MAAM,SAAS,SAAS,GAAG;EAC9B,IAAI,IAAI,MAAM,SAAS;EACvB;CACD;CAGF,OAAO;AACR;AAEA,MAAM,kBAAkB,IAAI,IAAI,gBAAgB,MAAM,EAAE,CAAC;;;;;;;;;;;;;AAczD,MAAM,gBAAgB,SAAyB;CAC9C,IAAI,OAAO;CACX,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;EACxC,MAAM,IAAI,KAAK;EACf,IAAI,MAAM,OAAO,KAAK,IAAI,OAAO,KAChC,IAAI,KAAK,IAAI,OAAO,KAAK;GACxB,QAAQ;GACR,KAAK;EACN,OAAO;GACN,QAAQ;GACR,KAAK;EACN;OACM,IAAI,MAAM,KAChB,QAAQ;OACF,IAAI,MAAM,KAChB,QAAQ;OACF,IAAI,gBAAgB,IAAI,CAAC,GAC/B,QAAQ,KAAK;OAEb,QAAQ;CAEV;CACA,OAAO,IAAI,OAAO,IAAI,KAAK,EAAE;AAC9B;;;;;;AAOA,MAAM,WAAW,MAAsB,EAAE,QAAQ,OAAO,GAAG;AAE3D,MAAa,aAAa,SAAiB,SAC1C,aAAa,QAAQ,OAAO,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,CAAC;;;;;;;;;;AAWlD,MAAM,eAAe,YAA4B;CAChD,MAAM,UAAU,QAAQ,OAAO,MAAM;CACrC,MAAM,UAAU,YAAY,KAAK,UAAU,QAAQ,MAAM,GAAG,OAAO;CACnE,MAAM,QAAQ,QAAQ,YAAY,GAAG;CACrC,IAAI,UAAU,GAAG,OAAO;CACxB,OAAO,QAAQ,IAAI,QAAQ,MAAM,GAAG,KAAK,IAAI;AAC9C;;;AAIA,MAAa,oBAAoB,UAA4D;CAC5F,MAAM,wBAAQ,IAAI,IAAY;CAC9B,KAAK,MAAM,SAAS,OACnB,KAAK,MAAM,WAAW,MAAM,OAC3B,MAAM,IAAI,YAAY,OAAO,CAAC;CAGhC,OAAO,CAAC,GAAG,KAAK;AACjB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SupervisorBootError } from "./errors.mjs";
|
|
2
2
|
import { noopContributionDispatcher } from "./contribution-dispatcher.mjs";
|
|
3
3
|
import { resolveGraph } from "../lifecycle/dep-graph.mjs";
|
|
4
|
-
import { buildWatchIndex,
|
|
4
|
+
import { attribute, buildWatchIndex, globMatch } from "../lifecycle/watch-attribution.mjs";
|
|
5
5
|
import { plan } from "../reconcile/graph.mjs";
|
|
6
6
|
import { installSignalHandler } from "../lifecycle/signals.mjs";
|
|
7
7
|
import "../lifecycle/index.mjs";
|
|
@@ -200,11 +200,7 @@ const startSupervisor = (stack, identity, state, pluginContext = Context.empty()
|
|
|
200
200
|
});
|
|
201
201
|
const watchIndex = buildWatchIndex(graph.nodes);
|
|
202
202
|
const notifyWatchFire = (path) => Effect.gen(function* () {
|
|
203
|
-
const matched =
|
|
204
|
-
for (const entry of watchIndex) for (const pat of entry.paths) if (exactPrefixMatch(pat, path)) {
|
|
205
|
-
matched.add(entry.pluginKey);
|
|
206
|
-
break;
|
|
207
|
-
}
|
|
203
|
+
const matched = attribute(watchIndex, path, globMatch);
|
|
208
204
|
if (matched.size === 0) return;
|
|
209
205
|
for (const key of matched) yield* Queue.offer(commands, {
|
|
210
206
|
tag: "selective-restart.requested",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start-supervisor.mjs","names":["Logger"],"sources":["../../../../src/substrate/runtime/supervisor/start-supervisor.ts"],"sourcesContent":["// Top-level supervisor entry — composes every per-concern module.\n//\n// Responsibilities (in order):\n// 1. Seed projection identity + booting slice so early-mounted\n// renderers see a complete baseline.\n// 2. Resolve the dep graph + boot the registry.\n// 3. Build the substrate-wiring bag: logger overlay, runtime root, and\n// the closed contribution dispatcher.\n// 4. Compose the `SupervisorState` record (one bag passed to every\n// module).\n// 5. Build `runInitialAcquire` — the deferred initial-acquire body.\n// 6. Build the watch index + `notifyWatchFire`.\n// 7. Fork the command loop + signal handler.\n// 8. Install the scope-close finalizer (reverse-dep teardown).\n// 9. Return the handle.\n\nimport {\n\tContext,\n\tDeferred,\n\tEffect,\n\tExit,\n\ttype Fiber,\n\tQueue,\n\tRef,\n\tScope,\n\tSubscriptionRef,\n} from 'effect';\n\nimport type { PluginKey } from '../../brand.ts';\nimport type { EngineCommand, EngineEvent } from '../../events.ts';\nimport type { Identity } from '../../identity.ts';\nimport type { LifecycleStatus } from '../../lifecycle.ts';\nimport type { AccountProjection, SubscribableState } from '../../projection.ts';\nimport {\n\tnoopContributionDispatcher,\n\ttype ContributionDispatcher,\n} from './contribution-dispatcher.ts';\nimport {\n\tLogger,\n\tLogStore,\n\tmakeLogStore,\n\ttype LoggerShape,\n\ttype LogStoreConfig,\n} from '../observability/index.ts';\nimport { ControlPlaneService } from '../control-plane/service.ts';\nimport { controlPlaneDomainFromContext } from '../control-plane/domain.ts';\nimport { RuntimeRoot } from '../paths.ts';\nimport { declareAccount, setIdentity } from '../projection/update.ts';\nimport {\n\tbuildWatchIndex,\n\texactPrefixMatch,\n\tinstallSignalHandler,\n\tresolveGraph,\n\ttype PluginRegistry,\n\ttype ResolvedGraph,\n\ttype UnknownDependency,\n\ttype WatchEntry,\n} from '../lifecycle/index.ts';\nimport { acquireFullGraph, buildRegistry } from './acquire-node.ts';\nimport { commandLoop } from './command-loop.ts';\nimport { runPostAcquireHook } from './background-tasks.ts';\nimport {\n\tSupervisorBootError,\n\ttype SupervisorError,\n\ttype SupervisorPostAcquireFailed,\n} from './errors.ts';\nimport {\n\tallReadyOrTerminal,\n\ttype BackgroundTaskSlot,\n\ttype QueuedCommand,\n\ttype SupervisorCommandHandler,\n\ttype SupervisorPostAcquireHook,\n\ttype SupervisorState,\n} from './state.ts';\nimport type { SupervisedStack } from './types.ts';\nimport { teardownKeys } from './teardown.ts';\nimport { plan } from '../reconcile/graph.ts';\nimport {\n\tbuildTransitionEmitter,\n\tnoopLogger,\n\tOptionalService,\n\tsetCyclePhase,\n\twithEventPublishingLogger,\n} from './wiring.ts';\n\nconst loggerAccess = OptionalService(Logger);\nconst runtimeRootAccess = OptionalService(RuntimeRoot);\n\nexport interface SupervisorHandle {\n\treadonly identity: Identity;\n\treadonly graph: ResolvedGraph;\n\treadonly registry: PluginRegistry;\n\treadonly events: Queue.Dequeue<EngineEvent>;\n\treadonly commands: Queue.Enqueue<EngineCommand>;\n\treadonly runCommand: (command: EngineCommand) => Effect.Effect<void, unknown>;\n\treadonly state: SubscriptionRef.SubscriptionRef<SubscribableState>;\n\treadonly watchIndex: ReadonlyArray<WatchEntry>;\n\t/** Fire a watch event for the given path. Substrate-level glue:\n\t * the thick L0 watcher calls this; tests call it directly. */\n\treadonly notifyWatchFire: (path: string) => Effect.Effect<void>;\n\t/** Block until the supervisor's command loop sets the shutdown\n\t * latch. The outer runtime then closes the supervisor scope, which\n\t * tears down every plugin in reverse-dep order. */\n\treadonly awaitShutdown: Effect.Effect<void>;\n}\n\nexport interface SupervisorStartup {\n\treadonly handle: SupervisorHandle;\n\treadonly runInitialAcquire: Effect.Effect<void, SupervisorPostAcquireFailed, never>;\n}\n\nexport interface SupervisorStartupOptions {\n\treadonly commandLoop?: boolean;\n\treadonly devstackVersion?: string;\n\t/** Per-service log-store tuning. Absent fields fall back to the\n\t * `DEVSTACK_DASHBOARD_LOG_*` env vars, then the module defaults\n\t * (2000 records/service, 256 services). Threaded into the\n\t * process-scoped `makeLogStore` below. */\n\treadonly logStore?: LogStoreConfig;\n}\n\nconst pendingAccountProjection = (\n\trowKey: PluginKey,\n\tresourceId: string,\n\tupdatedAt: number,\n): AccountProjection | null => {\n\tif (!resourceId.startsWith('account/')) return null;\n\tconst name = resourceId.slice('account/'.length);\n\tif (name.length === 0) return null;\n\treturn {\n\t\tkey: resourceId as `account/${string}`,\n\t\trowKey,\n\t\tname,\n\t\taddress: null,\n\t\tscheme: null,\n\t\tsource: null,\n\t\tfunding: { status: 'pending', balanceMist: null, requestedMist: null, entries: [] },\n\t\twalletVisible: false,\n\t\tupdatedAt,\n\t};\n};\n\n/**\n * Prepare a supervisor for `stack` without running the initial acquire.\n * The returned `SupervisorHandle` is Scope-managed; the supervisor's\n * lifecycle is the surrounding Scope's lifecycle. Signal handlers,\n * the command loop, and every plugin's scope are children of the\n * supervisor scope. Callers that mount renderers can subscribe to the\n * returned state before invoking `runInitialAcquire`.\n *\n * `pluginContext` carries the substrate-context services available to\n * each plugin's `acquire` body (`IdentityContext`,\n * `ContainerRuntimeService`, `RuntimeRoot`, `StackPathsService`, etc.).\n * Plugins declare what they need by yielding the corresponding\n * `Context.Service` tag from within `Effect.gen`; the supervisor\n * provides this context before running the acquire effect so the\n * plugin's R-channel narrows to `Scope.Scope` (then to `never` after\n * the per-plugin Scope is provided).\n *\n * Contribution dispatch: the supervisor replays each plugin's\n * buffered ctx contributions through the closed `dispatcher`\n * (snapshotable/routable/codegenable/projection/strategy-contributor).\n * Production callers pass `buildProductionContributionDispatcher(...)`;\n * bare smoke tests omit it and get the no-op dispatcher.\n */\nexport const startSupervisor = (\n\tstack: SupervisedStack,\n\tidentity: Identity,\n\tstate: SubscriptionRef.SubscriptionRef<SubscribableState>,\n\tpluginContext: Context.Context<never> = Context.empty(),\n\tdispatcher: ContributionDispatcher = noopContributionDispatcher,\n\tcommandHandler?: SupervisorCommandHandler,\n\tpostAcquireHook?: SupervisorPostAcquireHook,\n\toptions: SupervisorStartupOptions = {},\n): Effect.Effect<SupervisorStartup, SupervisorBootError | UnknownDependency, Scope.Scope> =>\n\tEffect.gen(function* () {\n\t\t// Optional Logger service — pull from the plugin context if the\n\t\t// caller layered it in (CLI / e2e do). Bare smoke tests get the\n\t\t// no-op fallback so they remain log-free.\n\t\tconst baseLogger: LoggerShape = loggerAccess.read(pluginContext, noopLogger);\n\n\t\tyield* baseLogger.log('supervisor', null, {\n\t\t\tlevel: 'debug',\n\t\t\tmessage: 'supervisor boot start',\n\t\t\tfields: {\n\t\t\t\tapp: identity.app,\n\t\t\t\tstack: identity.stack,\n\t\t\t\tnetwork: identity.network,\n\t\t\t\tmemberCount: stack.members.length,\n\t\t\t},\n\t\t});\n\n\t\t// Seed the projection's identity and booting slices before\n\t\t// acquire starts so renderers mounting early have a complete\n\t\t// baseline.\n\t\tyield* SubscriptionRef.update(\n\t\t\tstate,\n\t\t\t(s) =>\n\t\t\t\t({\n\t\t\t\t\t...setIdentity(s, {\n\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t\tnetwork: identity.network,\n\t\t\t\t\t}),\n\t\t\t\t\tcycle: {\n\t\t\t\t\t\t...s.cycle,\n\t\t\t\t\t\tstartedAt: s.cycle.startedAt === 0 ? Date.now() : s.cycle.startedAt,\n\t\t\t\t\t\tphase: 'booting',\n\t\t\t\t\t},\n\t\t\t\t}) satisfies SubscribableState,\n\t\t);\n\n\t\t// Resolve the dep graph.\n\t\tconst graph = yield* resolveGraph(stack.members).pipe(\n\t\t\tEffect.mapError((cause) => new SupervisorBootError({ cause })),\n\t\t);\n\n\t\t// Event hub + command channel.\n\t\tconst hub = yield* Queue.unbounded<EngineEvent>();\n\t\tconst commands = yield* Queue.unbounded<EngineCommand>();\n\t\tconst queuedCommands = yield* Queue.unbounded<QueuedCommand>();\n\t\t// Background-task fiber slot: the live stack-restart fiber (or `null`\n\t\t// when idle). The fiber IS the running state — see `BackgroundTaskSlot`.\n\t\t// Forked into `supervisorScope` (below) via `Effect.forkIn`, interrupted\n\t\t// via `Fiber.interrupt`. (Snapshot capture/restore run inline in the\n\t\t// command loop now — the bounce — so they have no forked slot.)\n\t\tconst stackRestartTask: BackgroundTaskSlot = yield* Ref.make<Fiber.Fiber<void, never> | null>(\n\t\t\tnull,\n\t\t);\n\t\tconst shutdownLatch = yield* Ref.make(false);\n\t\tconst shutdownComplete = yield* Deferred.make<void>();\n\t\tconst initialAcquireStarted = yield* Ref.make(false);\n\n\t\t// Observability store (process-scoped, like `state`/`hub`): a\n\t\t// cross-service queryable log ring fed off the SAME logger path that\n\t\t// feeds the projection's per-row tail. Survives `stack.restart`\n\t\t// because it lives in this closure (only `cycle.id` bumps on restart).\n\t\t// The dashboard reads it via the control-plane `domain`.\n\t\tconst logStore = yield* makeLogStore(options.logStore ?? {});\n\t\tconst logger = withEventPublishingLogger(baseLogger, state, hub, logStore);\n\n\t\t// The control-plane command verbs, both offering onto the SAME\n\t\t// `queuedCommands` seam the single command-loop consumer drains —\n\t\t// distinguished only by the QueuedCommand kind:\n\t\t// - `publishCommand` — fire-and-forget; offers a `fire-and-forget`\n\t\t// QueuedCommand and returns immediately.\n\t\t// - `submitCommand` — offers a `submitted` QueuedCommand carrying a\n\t\t// completion deferred, then AWAITs the real exit. So a destructive\n\t\t// command like `snapshot.restore` (which removes live managed\n\t\t// containers then re-acquires) runs in-band with the loop, never\n\t\t// racing the live supervisor out-of-band. Re-fails with the\n\t\t// handler's cause.\n\t\t// (Both feed `queuedCommands`, NOT the public `commands` queue + bridge —\n\t\t// that queue stays for the signal handler and cross-process callers.)\n\t\tconst publishCommand = (command: EngineCommand): Effect.Effect<void> =>\n\t\t\tEffect.asVoid(Queue.offer(queuedCommands, { kind: 'fire-and-forget', command }));\n\t\tconst submitCommand = (command: EngineCommand): Effect.Effect<void, unknown> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst completion = yield* Deferred.make<Exit.Exit<void, unknown>>();\n\t\t\t\tyield* Queue.offer(queuedCommands, {\n\t\t\t\t\tkind: 'submitted',\n\t\t\t\t\tsubmission: { command, completion },\n\t\t\t\t});\n\t\t\t\tconst exit = yield* Deferred.await(completion);\n\t\t\t\tif (Exit.isFailure(exit)) {\n\t\t\t\t\treturn yield* Effect.failCause(exit.cause);\n\t\t\t\t}\n\t\t\t});\n\n\t\t// Per-plugin scopes parent off the supervisor scope.\n\t\tconst supervisorScope = yield* Effect.scope;\n\n\t\tconst emit = buildTransitionEmitter(state, hub);\n\t\tconst registry = yield* buildRegistry(graph, supervisorScope, emit);\n\n\t\t// Build the control-plane `domain` accessor surface from the data\n\t\t// the supervisor holds at wiring time: the resolved registry +\n\t\t// graph, plus the (optional) snapshot orchestrator / container\n\t\t// runtime / filesystem the caller may have layered into\n\t\t// `pluginContext`. The projection stays CLOSED — `domain` reads\n\t\t// resolved plugin VALUES via the name-blind registry seam, never\n\t\t// the projection. The registry is process-scoped, so the closure\n\t\t// stays valid across `stack.restart` (only `cycle.id` bumps).\n\t\tconst controlPlaneDomain = controlPlaneDomainFromContext({\n\t\t\tpluginContext,\n\t\t\tgraph,\n\t\t\tstackOptions: stack.options,\n\t\t\tdevstackVersion: options.devstackVersion ?? null,\n\t\t\tregistry,\n\t\t\tlogStore,\n\t\t});\n\n\t\tconst pluginRuntimeContext = pluginContext.pipe(\n\t\t\tContext.add(Logger, Logger.of(logger)),\n\t\t\t// Observability store. Exposed so the control-plane `domain` can\n\t\t\t// query the cross-service log ring.\n\t\t\tContext.add(LogStore, LogStore.of(logStore)),\n\t\t\t// Expose the control plane (live projection + fire-and-forget\n\t\t\t// command dispatch + the plugin-domain accessor surface) to\n\t\t\t// in-process surfaces like the dashboard plugin. Reads the same\n\t\t\t// `state` ref and `commands` queue the supervisor itself drives.\n\t\t\tContext.add(\n\t\t\t\tControlPlaneService,\n\t\t\t\tControlPlaneService.of({\n\t\t\t\t\tstate,\n\t\t\t\t\tpublishCommand,\n\t\t\t\t\tsubmitCommand,\n\t\t\t\t\tdomain: controlPlaneDomain,\n\t\t\t\t}),\n\t\t\t),\n\t\t) as Context.Context<never>;\n\n\t\t// Declare a row for every plugin so the projection's\n\t\t// `lifecycle.statusChanged` events have a row to attach to.\n\t\t// `section` is plugin-declared at `definePlugin({ section })`\n\t\t// time; we stamp it onto the row here so the TUI groups rows\n\t\t// without pattern-matching on plugin-name substrings.\n\t\tfor (const [key, node] of graph.nodes) {\n\t\t\tconst declaredAccount = pendingAccountProjection(key, node.member.id, Date.now());\n\t\t\tyield* SubscriptionRef.update(state, (s) => ({\n\t\t\t\t...(declaredAccount === null ? s : declareAccount(s, declaredAccount)),\n\t\t\t\trows: s.rows.some((r) => r.key === key)\n\t\t\t\t\t? s.rows\n\t\t\t\t\t: [\n\t\t\t\t\t\t\t...s.rows,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\t\trole: node.member.role,\n\t\t\t\t\t\t\t\tstatus: 'pending' as LifecycleStatus,\n\t\t\t\t\t\t\t\tphase: null,\n\t\t\t\t\t\t\t\tlastError: null,\n\t\t\t\t\t\t\t\tlogTail: { lines: [], level: 'info' as const, truncated: false },\n\t\t\t\t\t\t\t\tendpoints: [],\n\t\t\t\t\t\t\t\tselectiveRestartHighlight: false,\n\t\t\t\t\t\t\t\tsection: node.member.section,\n\t\t\t\t\t\t\t\tendpointSection: node.member.endpointSection ?? node.member.section,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t}));\n\t\t}\n\n\t\t// Extract `RuntimeRoot` from the plugin context — recorded on\n\t\t// `SupervisorState.runtimeRoot` and read by the post-acquire hook\n\t\t// + background tasks (NOT threaded into the acquire path, which is\n\t\t// name-blind). Fallback to '' for bare smoke tests that don't wire\n\t\t// RuntimeRoot; emit a logWarning when that path fires so\n\t\t// production-style misconfigurations are visible (plugins computing\n\t\t// `${runtimeRoot}/foo` would otherwise silently resolve to\n\t\t// host-filesystem root).\n\t\tconst runtimeRootResolved = runtimeRootAccess.read(pluginContext, { root: '' });\n\t\tif (runtimeRootResolved.root === '') {\n\t\t\tyield* Effect.logWarning(\n\t\t\t\t'supervisor: RuntimeRoot missing from pluginContext; falling back to empty root. ' +\n\t\t\t\t\t'Production wiring must layer `layerRuntimeRoot(...)` into pluginContext.',\n\t\t\t);\n\t\t}\n\t\tconst runtimeRoot = runtimeRootResolved.root;\n\n\t\tconst enableCommandLoop = options.commandLoop !== false;\n\t\tif (enableCommandLoop) {\n\t\t\tyield* Effect.forkScoped(installSignalHandler(commands));\n\t\t\tyield* Effect.forkScoped(\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\twhile (true) {\n\t\t\t\t\t\tconst command = yield* Queue.take(commands);\n\t\t\t\t\t\tyield* Queue.offer(queuedCommands, { kind: 'fire-and-forget', command });\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tconst supervisorState: SupervisorState = {\n\t\t\tgraph,\n\t\t\tregistry,\n\t\t\tref: state,\n\t\t\thub,\n\t\t\tqueuedCommands,\n\t\t\tsupervisorScope,\n\t\t\tstackRestartTask,\n\t\t\tshutdownLatch,\n\t\t\tshutdownComplete,\n\t\t\tpluginContext: pluginRuntimeContext,\n\t\t\tdispatcher,\n\t\t\tlogger,\n\t\t\tidentity,\n\t\t\truntimeRoot,\n\t\t\tcommandHandler,\n\t\t\tpostAcquireHook,\n\t\t};\n\n\t\tconst runInitialAcquire = Effect.gen(function* () {\n\t\t\tconst alreadyStarted = yield* Ref.modify(initialAcquireStarted, (started) => [started, true]);\n\t\t\tif (alreadyStarted) return;\n\t\t\tif (yield* Ref.get(shutdownLatch)) return;\n\t\t\tyield* acquireFullGraph(\n\t\t\t\tgraph,\n\t\t\t\tregistry,\n\t\t\t\tstate,\n\t\t\t\thub,\n\t\t\t\tpluginRuntimeContext,\n\t\t\t\tdispatcher,\n\t\t\t\tlogger,\n\t\t\t\tidentity,\n\t\t\t\tsupervisorScope,\n\t\t\t);\n\t\t\tif (yield* Ref.get(shutdownLatch)) return;\n\t\t\tconst initialReady = yield* allReadyOrTerminal(graph, registry);\n\t\t\tif (initialReady) {\n\t\t\t\tyield* runPostAcquireHook(supervisorState);\n\t\t\t\tif (!(yield* Ref.get(shutdownLatch))) {\n\t\t\t\t\tyield* setCyclePhase(state, 'running');\n\t\t\t\t}\n\t\t\t} else if (!(yield* Ref.get(shutdownLatch))) {\n\t\t\t\tyield* setCyclePhase(state, 'running');\n\t\t\t}\n\t\t});\n\n\t\t// Build the watch index up front; the supervisor exposes\n\t\t// `notifyWatchFire` so the L0 thick watcher can call into it.\n\t\tconst watchIndex = buildWatchIndex(graph.nodes);\n\n\t\tconst notifyWatchFire = (path: string): Effect.Effect<void> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst matched = new Set<PluginKey>();\n\t\t\t\tfor (const entry of watchIndex) {\n\t\t\t\t\tfor (const pat of entry.paths) {\n\t\t\t\t\t\tif (exactPrefixMatch(pat, path)) {\n\t\t\t\t\t\t\tmatched.add(entry.pluginKey);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (matched.size === 0) return;\n\t\t\t\t// One `selective-restart.requested` per matched plugin, in\n\t\t\t\t// match order. (Previously the head was offered separately\n\t\t\t\t// from the tail — identical behaviour, one fewer branch.)\n\t\t\t\tfor (const key of matched) {\n\t\t\t\t\tyield* Queue.offer(commands, {\n\t\t\t\t\t\ttag: 'selective-restart.requested',\n\t\t\t\t\t\tpluginKey: key,\n\t\t\t\t\t} satisfies EngineCommand);\n\t\t\t\t}\n\t\t\t});\n\n\t\t// Fork the command loop.\n\t\tif (enableCommandLoop) {\n\t\t\tyield* Effect.forkScoped(commandLoop(supervisorState));\n\t\t}\n\n\t\t// Tear-down finalizer: closes every plugin scope in reverse-dep\n\t\t// order. The supervisor scope itself closes when the surrounding\n\t\t// caller closes — `Scope.close` cascades to plugin scopes.\n\t\tyield* Effect.addFinalizer(() =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst fullDrain = plan(graph, {\n\t\t\t\t\tkind: 'graph-keys',\n\t\t\t\t\tkeys: [...graph.nodes.keys()],\n\t\t\t\t});\n\t\t\t\tyield* teardownKeys(graph, registry, fullDrain.teardownOrder).pipe(\n\t\t\t\t\tEffect.catch(() => Effect.void),\n\t\t\t\t);\n\t\t\t}),\n\t\t);\n\n\t\tconst awaitShutdown: Effect.Effect<void> = Deferred.await(shutdownComplete);\n\n\t\t// The programmable `runCommand` surface is exactly the submit-and-await\n\t\t// path the control plane uses; share the one implementation.\n\t\tconst runCommand = submitCommand;\n\n\t\tconst handle = {\n\t\t\tidentity,\n\t\t\tgraph,\n\t\t\tregistry,\n\t\t\tevents: hub,\n\t\t\tcommands,\n\t\t\trunCommand,\n\t\t\tstate,\n\t\t\twatchIndex,\n\t\t\tnotifyWatchFire,\n\t\t\tawaitShutdown,\n\t\t} satisfies SupervisorHandle;\n\t\treturn { handle, runInitialAcquire } satisfies SupervisorStartup;\n\t});\n\n/**\n * Boot a supervisor for `stack` and wait for the initial acquire to\n * finish before returning the handle. Existing callers that only need\n * a ready-or-failed handle keep this simpler entry point; live surfaces\n * that need startup display use `startSupervisor`.\n */\nexport const supervise = (\n\tstack: SupervisedStack,\n\tidentity: Identity,\n\tstate: SubscriptionRef.SubscriptionRef<SubscribableState>,\n\tpluginContext: Context.Context<never> = Context.empty(),\n\tdispatcher: ContributionDispatcher = noopContributionDispatcher,\n\tcommandHandler?: SupervisorCommandHandler,\n\tpostAcquireHook?: SupervisorPostAcquireHook,\n): Effect.Effect<SupervisorHandle, SupervisorError, Scope.Scope> =>\n\tEffect.gen(function* () {\n\t\tconst startup = yield* startSupervisor(\n\t\t\tstack,\n\t\t\tidentity,\n\t\t\tstate,\n\t\t\tpluginContext,\n\t\t\tdispatcher,\n\t\t\tcommandHandler,\n\t\t\tpostAcquireHook,\n\t\t\t{ commandLoop: true },\n\t\t);\n\t\tyield* startup.runInitialAcquire;\n\t\treturn startup.handle;\n\t});\n\n/**\n * Block until the supervisor's shutdown latch fires OR the surrounding\n * scope is interrupted (signal-driven exit). Wraps `awaitShutdown` for\n * the common \"boot then wait\" shape.\n *\n * Returns the final supervisor state so callers (CLI, programmable\n * API) can inspect the projection after shutdown.\n */\nexport const runToShutdown = (\n\thandle: SupervisorHandle,\n): Effect.Effect<SubscribableState, never, never> =>\n\tEffect.gen(function* () {\n\t\tyield* handle.awaitShutdown;\n\t\treturn yield* SubscriptionRef.get(handle.state);\n\t});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"start-supervisor.mjs","names":["Logger"],"sources":["../../../../src/substrate/runtime/supervisor/start-supervisor.ts"],"sourcesContent":["// Top-level supervisor entry — composes every per-concern module.\n//\n// Responsibilities (in order):\n// 1. Seed projection identity + booting slice so early-mounted\n// renderers see a complete baseline.\n// 2. Resolve the dep graph + boot the registry.\n// 3. Build the substrate-wiring bag: logger overlay, runtime root, and\n// the closed contribution dispatcher.\n// 4. Compose the `SupervisorState` record (one bag passed to every\n// module).\n// 5. Build `runInitialAcquire` — the deferred initial-acquire body.\n// 6. Build the watch index + `notifyWatchFire`.\n// 7. Fork the command loop + signal handler.\n// 8. Install the scope-close finalizer (reverse-dep teardown).\n// 9. Return the handle.\n\nimport {\n\tContext,\n\tDeferred,\n\tEffect,\n\tExit,\n\ttype Fiber,\n\tQueue,\n\tRef,\n\tScope,\n\tSubscriptionRef,\n} from 'effect';\n\nimport type { PluginKey } from '../../brand.ts';\nimport type { EngineCommand, EngineEvent } from '../../events.ts';\nimport type { Identity } from '../../identity.ts';\nimport type { LifecycleStatus } from '../../lifecycle.ts';\nimport type { AccountProjection, SubscribableState } from '../../projection.ts';\nimport {\n\tnoopContributionDispatcher,\n\ttype ContributionDispatcher,\n} from './contribution-dispatcher.ts';\nimport {\n\tLogger,\n\tLogStore,\n\tmakeLogStore,\n\ttype LoggerShape,\n\ttype LogStoreConfig,\n} from '../observability/index.ts';\nimport { ControlPlaneService } from '../control-plane/service.ts';\nimport { controlPlaneDomainFromContext } from '../control-plane/domain.ts';\nimport { RuntimeRoot } from '../paths.ts';\nimport { declareAccount, setIdentity } from '../projection/update.ts';\nimport {\n\tattribute,\n\tbuildWatchIndex,\n\tglobMatch,\n\tinstallSignalHandler,\n\tresolveGraph,\n\ttype PluginRegistry,\n\ttype ResolvedGraph,\n\ttype UnknownDependency,\n\ttype WatchEntry,\n} from '../lifecycle/index.ts';\nimport { acquireFullGraph, buildRegistry } from './acquire-node.ts';\nimport { commandLoop } from './command-loop.ts';\nimport { runPostAcquireHook } from './background-tasks.ts';\nimport {\n\tSupervisorBootError,\n\ttype SupervisorError,\n\ttype SupervisorPostAcquireFailed,\n} from './errors.ts';\nimport {\n\tallReadyOrTerminal,\n\ttype BackgroundTaskSlot,\n\ttype QueuedCommand,\n\ttype SupervisorCommandHandler,\n\ttype SupervisorPostAcquireHook,\n\ttype SupervisorState,\n} from './state.ts';\nimport type { SupervisedStack } from './types.ts';\nimport { teardownKeys } from './teardown.ts';\nimport { plan } from '../reconcile/graph.ts';\nimport {\n\tbuildTransitionEmitter,\n\tnoopLogger,\n\tOptionalService,\n\tsetCyclePhase,\n\twithEventPublishingLogger,\n} from './wiring.ts';\n\nconst loggerAccess = OptionalService(Logger);\nconst runtimeRootAccess = OptionalService(RuntimeRoot);\n\nexport interface SupervisorHandle {\n\treadonly identity: Identity;\n\treadonly graph: ResolvedGraph;\n\treadonly registry: PluginRegistry;\n\treadonly events: Queue.Dequeue<EngineEvent>;\n\treadonly commands: Queue.Enqueue<EngineCommand>;\n\treadonly runCommand: (command: EngineCommand) => Effect.Effect<void, unknown>;\n\treadonly state: SubscriptionRef.SubscriptionRef<SubscribableState>;\n\treadonly watchIndex: ReadonlyArray<WatchEntry>;\n\t/** Fire a watch event for the given path. Substrate-level glue:\n\t * the thick L0 watcher calls this; tests call it directly. */\n\treadonly notifyWatchFire: (path: string) => Effect.Effect<void>;\n\t/** Block until the supervisor's command loop sets the shutdown\n\t * latch. The outer runtime then closes the supervisor scope, which\n\t * tears down every plugin in reverse-dep order. */\n\treadonly awaitShutdown: Effect.Effect<void>;\n}\n\nexport interface SupervisorStartup {\n\treadonly handle: SupervisorHandle;\n\treadonly runInitialAcquire: Effect.Effect<void, SupervisorPostAcquireFailed, never>;\n}\n\nexport interface SupervisorStartupOptions {\n\treadonly commandLoop?: boolean;\n\treadonly devstackVersion?: string;\n\t/** Per-service log-store tuning. Absent fields fall back to the\n\t * `DEVSTACK_DASHBOARD_LOG_*` env vars, then the module defaults\n\t * (2000 records/service, 256 services). Threaded into the\n\t * process-scoped `makeLogStore` below. */\n\treadonly logStore?: LogStoreConfig;\n}\n\nconst pendingAccountProjection = (\n\trowKey: PluginKey,\n\tresourceId: string,\n\tupdatedAt: number,\n): AccountProjection | null => {\n\tif (!resourceId.startsWith('account/')) return null;\n\tconst name = resourceId.slice('account/'.length);\n\tif (name.length === 0) return null;\n\treturn {\n\t\tkey: resourceId as `account/${string}`,\n\t\trowKey,\n\t\tname,\n\t\taddress: null,\n\t\tscheme: null,\n\t\tsource: null,\n\t\tfunding: { status: 'pending', balanceMist: null, requestedMist: null, entries: [] },\n\t\twalletVisible: false,\n\t\tupdatedAt,\n\t};\n};\n\n/**\n * Prepare a supervisor for `stack` without running the initial acquire.\n * The returned `SupervisorHandle` is Scope-managed; the supervisor's\n * lifecycle is the surrounding Scope's lifecycle. Signal handlers,\n * the command loop, and every plugin's scope are children of the\n * supervisor scope. Callers that mount renderers can subscribe to the\n * returned state before invoking `runInitialAcquire`.\n *\n * `pluginContext` carries the substrate-context services available to\n * each plugin's `acquire` body (`IdentityContext`,\n * `ContainerRuntimeService`, `RuntimeRoot`, `StackPathsService`, etc.).\n * Plugins declare what they need by yielding the corresponding\n * `Context.Service` tag from within `Effect.gen`; the supervisor\n * provides this context before running the acquire effect so the\n * plugin's R-channel narrows to `Scope.Scope` (then to `never` after\n * the per-plugin Scope is provided).\n *\n * Contribution dispatch: the supervisor replays each plugin's\n * buffered ctx contributions through the closed `dispatcher`\n * (snapshotable/routable/codegenable/projection/strategy-contributor).\n * Production callers pass `buildProductionContributionDispatcher(...)`;\n * bare smoke tests omit it and get the no-op dispatcher.\n */\nexport const startSupervisor = (\n\tstack: SupervisedStack,\n\tidentity: Identity,\n\tstate: SubscriptionRef.SubscriptionRef<SubscribableState>,\n\tpluginContext: Context.Context<never> = Context.empty(),\n\tdispatcher: ContributionDispatcher = noopContributionDispatcher,\n\tcommandHandler?: SupervisorCommandHandler,\n\tpostAcquireHook?: SupervisorPostAcquireHook,\n\toptions: SupervisorStartupOptions = {},\n): Effect.Effect<SupervisorStartup, SupervisorBootError | UnknownDependency, Scope.Scope> =>\n\tEffect.gen(function* () {\n\t\t// Optional Logger service — pull from the plugin context if the\n\t\t// caller layered it in (CLI / e2e do). Bare smoke tests get the\n\t\t// no-op fallback so they remain log-free.\n\t\tconst baseLogger: LoggerShape = loggerAccess.read(pluginContext, noopLogger);\n\n\t\tyield* baseLogger.log('supervisor', null, {\n\t\t\tlevel: 'debug',\n\t\t\tmessage: 'supervisor boot start',\n\t\t\tfields: {\n\t\t\t\tapp: identity.app,\n\t\t\t\tstack: identity.stack,\n\t\t\t\tnetwork: identity.network,\n\t\t\t\tmemberCount: stack.members.length,\n\t\t\t},\n\t\t});\n\n\t\t// Seed the projection's identity and booting slices before\n\t\t// acquire starts so renderers mounting early have a complete\n\t\t// baseline.\n\t\tyield* SubscriptionRef.update(\n\t\t\tstate,\n\t\t\t(s) =>\n\t\t\t\t({\n\t\t\t\t\t...setIdentity(s, {\n\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t\tnetwork: identity.network,\n\t\t\t\t\t}),\n\t\t\t\t\tcycle: {\n\t\t\t\t\t\t...s.cycle,\n\t\t\t\t\t\tstartedAt: s.cycle.startedAt === 0 ? Date.now() : s.cycle.startedAt,\n\t\t\t\t\t\tphase: 'booting',\n\t\t\t\t\t},\n\t\t\t\t}) satisfies SubscribableState,\n\t\t);\n\n\t\t// Resolve the dep graph.\n\t\tconst graph = yield* resolveGraph(stack.members).pipe(\n\t\t\tEffect.mapError((cause) => new SupervisorBootError({ cause })),\n\t\t);\n\n\t\t// Event hub + command channel.\n\t\tconst hub = yield* Queue.unbounded<EngineEvent>();\n\t\tconst commands = yield* Queue.unbounded<EngineCommand>();\n\t\tconst queuedCommands = yield* Queue.unbounded<QueuedCommand>();\n\t\t// Background-task fiber slot: the live stack-restart fiber (or `null`\n\t\t// when idle). The fiber IS the running state — see `BackgroundTaskSlot`.\n\t\t// Forked into `supervisorScope` (below) via `Effect.forkIn`, interrupted\n\t\t// via `Fiber.interrupt`. (Snapshot capture/restore run inline in the\n\t\t// command loop now — the bounce — so they have no forked slot.)\n\t\tconst stackRestartTask: BackgroundTaskSlot = yield* Ref.make<Fiber.Fiber<void, never> | null>(\n\t\t\tnull,\n\t\t);\n\t\tconst shutdownLatch = yield* Ref.make(false);\n\t\tconst shutdownComplete = yield* Deferred.make<void>();\n\t\tconst initialAcquireStarted = yield* Ref.make(false);\n\n\t\t// Observability store (process-scoped, like `state`/`hub`): a\n\t\t// cross-service queryable log ring fed off the SAME logger path that\n\t\t// feeds the projection's per-row tail. Survives `stack.restart`\n\t\t// because it lives in this closure (only `cycle.id` bumps on restart).\n\t\t// The dashboard reads it via the control-plane `domain`.\n\t\tconst logStore = yield* makeLogStore(options.logStore ?? {});\n\t\tconst logger = withEventPublishingLogger(baseLogger, state, hub, logStore);\n\n\t\t// The control-plane command verbs, both offering onto the SAME\n\t\t// `queuedCommands` seam the single command-loop consumer drains —\n\t\t// distinguished only by the QueuedCommand kind:\n\t\t// - `publishCommand` — fire-and-forget; offers a `fire-and-forget`\n\t\t// QueuedCommand and returns immediately.\n\t\t// - `submitCommand` — offers a `submitted` QueuedCommand carrying a\n\t\t// completion deferred, then AWAITs the real exit. So a destructive\n\t\t// command like `snapshot.restore` (which removes live managed\n\t\t// containers then re-acquires) runs in-band with the loop, never\n\t\t// racing the live supervisor out-of-band. Re-fails with the\n\t\t// handler's cause.\n\t\t// (Both feed `queuedCommands`, NOT the public `commands` queue + bridge —\n\t\t// that queue stays for the signal handler and cross-process callers.)\n\t\tconst publishCommand = (command: EngineCommand): Effect.Effect<void> =>\n\t\t\tEffect.asVoid(Queue.offer(queuedCommands, { kind: 'fire-and-forget', command }));\n\t\tconst submitCommand = (command: EngineCommand): Effect.Effect<void, unknown> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst completion = yield* Deferred.make<Exit.Exit<void, unknown>>();\n\t\t\t\tyield* Queue.offer(queuedCommands, {\n\t\t\t\t\tkind: 'submitted',\n\t\t\t\t\tsubmission: { command, completion },\n\t\t\t\t});\n\t\t\t\tconst exit = yield* Deferred.await(completion);\n\t\t\t\tif (Exit.isFailure(exit)) {\n\t\t\t\t\treturn yield* Effect.failCause(exit.cause);\n\t\t\t\t}\n\t\t\t});\n\n\t\t// Per-plugin scopes parent off the supervisor scope.\n\t\tconst supervisorScope = yield* Effect.scope;\n\n\t\tconst emit = buildTransitionEmitter(state, hub);\n\t\tconst registry = yield* buildRegistry(graph, supervisorScope, emit);\n\n\t\t// Build the control-plane `domain` accessor surface from the data\n\t\t// the supervisor holds at wiring time: the resolved registry +\n\t\t// graph, plus the (optional) snapshot orchestrator / container\n\t\t// runtime / filesystem the caller may have layered into\n\t\t// `pluginContext`. The projection stays CLOSED — `domain` reads\n\t\t// resolved plugin VALUES via the name-blind registry seam, never\n\t\t// the projection. The registry is process-scoped, so the closure\n\t\t// stays valid across `stack.restart` (only `cycle.id` bumps).\n\t\tconst controlPlaneDomain = controlPlaneDomainFromContext({\n\t\t\tpluginContext,\n\t\t\tgraph,\n\t\t\tstackOptions: stack.options,\n\t\t\tdevstackVersion: options.devstackVersion ?? null,\n\t\t\tregistry,\n\t\t\tlogStore,\n\t\t});\n\n\t\tconst pluginRuntimeContext = pluginContext.pipe(\n\t\t\tContext.add(Logger, Logger.of(logger)),\n\t\t\t// Observability store. Exposed so the control-plane `domain` can\n\t\t\t// query the cross-service log ring.\n\t\t\tContext.add(LogStore, LogStore.of(logStore)),\n\t\t\t// Expose the control plane (live projection + fire-and-forget\n\t\t\t// command dispatch + the plugin-domain accessor surface) to\n\t\t\t// in-process surfaces like the dashboard plugin. Reads the same\n\t\t\t// `state` ref and `commands` queue the supervisor itself drives.\n\t\t\tContext.add(\n\t\t\t\tControlPlaneService,\n\t\t\t\tControlPlaneService.of({\n\t\t\t\t\tstate,\n\t\t\t\t\tpublishCommand,\n\t\t\t\t\tsubmitCommand,\n\t\t\t\t\tdomain: controlPlaneDomain,\n\t\t\t\t}),\n\t\t\t),\n\t\t) as Context.Context<never>;\n\n\t\t// Declare a row for every plugin so the projection's\n\t\t// `lifecycle.statusChanged` events have a row to attach to.\n\t\t// `section` is plugin-declared at `definePlugin({ section })`\n\t\t// time; we stamp it onto the row here so the TUI groups rows\n\t\t// without pattern-matching on plugin-name substrings.\n\t\tfor (const [key, node] of graph.nodes) {\n\t\t\tconst declaredAccount = pendingAccountProjection(key, node.member.id, Date.now());\n\t\t\tyield* SubscriptionRef.update(state, (s) => ({\n\t\t\t\t...(declaredAccount === null ? s : declareAccount(s, declaredAccount)),\n\t\t\t\trows: s.rows.some((r) => r.key === key)\n\t\t\t\t\t? s.rows\n\t\t\t\t\t: [\n\t\t\t\t\t\t\t...s.rows,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\t\trole: node.member.role,\n\t\t\t\t\t\t\t\tstatus: 'pending' as LifecycleStatus,\n\t\t\t\t\t\t\t\tphase: null,\n\t\t\t\t\t\t\t\tlastError: null,\n\t\t\t\t\t\t\t\tlogTail: { lines: [], level: 'info' as const, truncated: false },\n\t\t\t\t\t\t\t\tendpoints: [],\n\t\t\t\t\t\t\t\tselectiveRestartHighlight: false,\n\t\t\t\t\t\t\t\tsection: node.member.section,\n\t\t\t\t\t\t\t\tendpointSection: node.member.endpointSection ?? node.member.section,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t}));\n\t\t}\n\n\t\t// Extract `RuntimeRoot` from the plugin context — recorded on\n\t\t// `SupervisorState.runtimeRoot` and read by the post-acquire hook\n\t\t// + background tasks (NOT threaded into the acquire path, which is\n\t\t// name-blind). Fallback to '' for bare smoke tests that don't wire\n\t\t// RuntimeRoot; emit a logWarning when that path fires so\n\t\t// production-style misconfigurations are visible (plugins computing\n\t\t// `${runtimeRoot}/foo` would otherwise silently resolve to\n\t\t// host-filesystem root).\n\t\tconst runtimeRootResolved = runtimeRootAccess.read(pluginContext, { root: '' });\n\t\tif (runtimeRootResolved.root === '') {\n\t\t\tyield* Effect.logWarning(\n\t\t\t\t'supervisor: RuntimeRoot missing from pluginContext; falling back to empty root. ' +\n\t\t\t\t\t'Production wiring must layer `layerRuntimeRoot(...)` into pluginContext.',\n\t\t\t);\n\t\t}\n\t\tconst runtimeRoot = runtimeRootResolved.root;\n\n\t\tconst enableCommandLoop = options.commandLoop !== false;\n\t\tif (enableCommandLoop) {\n\t\t\tyield* Effect.forkScoped(installSignalHandler(commands));\n\t\t\tyield* Effect.forkScoped(\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\twhile (true) {\n\t\t\t\t\t\tconst command = yield* Queue.take(commands);\n\t\t\t\t\t\tyield* Queue.offer(queuedCommands, { kind: 'fire-and-forget', command });\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tconst supervisorState: SupervisorState = {\n\t\t\tgraph,\n\t\t\tregistry,\n\t\t\tref: state,\n\t\t\thub,\n\t\t\tqueuedCommands,\n\t\t\tsupervisorScope,\n\t\t\tstackRestartTask,\n\t\t\tshutdownLatch,\n\t\t\tshutdownComplete,\n\t\t\tpluginContext: pluginRuntimeContext,\n\t\t\tdispatcher,\n\t\t\tlogger,\n\t\t\tidentity,\n\t\t\truntimeRoot,\n\t\t\tcommandHandler,\n\t\t\tpostAcquireHook,\n\t\t};\n\n\t\tconst runInitialAcquire = Effect.gen(function* () {\n\t\t\tconst alreadyStarted = yield* Ref.modify(initialAcquireStarted, (started) => [started, true]);\n\t\t\tif (alreadyStarted) return;\n\t\t\tif (yield* Ref.get(shutdownLatch)) return;\n\t\t\tyield* acquireFullGraph(\n\t\t\t\tgraph,\n\t\t\t\tregistry,\n\t\t\t\tstate,\n\t\t\t\thub,\n\t\t\t\tpluginRuntimeContext,\n\t\t\t\tdispatcher,\n\t\t\t\tlogger,\n\t\t\t\tidentity,\n\t\t\t\tsupervisorScope,\n\t\t\t);\n\t\t\tif (yield* Ref.get(shutdownLatch)) return;\n\t\t\tconst initialReady = yield* allReadyOrTerminal(graph, registry);\n\t\t\tif (initialReady) {\n\t\t\t\tyield* runPostAcquireHook(supervisorState);\n\t\t\t\tif (!(yield* Ref.get(shutdownLatch))) {\n\t\t\t\t\tyield* setCyclePhase(state, 'running');\n\t\t\t\t}\n\t\t\t} else if (!(yield* Ref.get(shutdownLatch))) {\n\t\t\t\tyield* setCyclePhase(state, 'running');\n\t\t\t}\n\t\t});\n\n\t\t// Build the watch index up front; the supervisor exposes\n\t\t// `notifyWatchFire` so the L0 thick watcher can call into it.\n\t\tconst watchIndex = buildWatchIndex(graph.nodes);\n\n\t\tconst notifyWatchFire = (path: string): Effect.Effect<void> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\t// Glob-aware attribution: the declared paths are globs\n\t\t\t\t// (`<src>/**/*.move`), so a literal prefix test would never\n\t\t\t\t// match a fired file path. `globMatch` honors `**`/`*`/`?`.\n\t\t\t\tconst matched = attribute(watchIndex, path, globMatch);\n\t\t\t\tif (matched.size === 0) return;\n\t\t\t\t// One `selective-restart.requested` per matched plugin, in\n\t\t\t\t// match order. (Previously the head was offered separately\n\t\t\t\t// from the tail — identical behaviour, one fewer branch.)\n\t\t\t\tfor (const key of matched) {\n\t\t\t\t\tyield* Queue.offer(commands, {\n\t\t\t\t\t\ttag: 'selective-restart.requested',\n\t\t\t\t\t\tpluginKey: key,\n\t\t\t\t\t} satisfies EngineCommand);\n\t\t\t\t}\n\t\t\t});\n\n\t\t// Fork the command loop.\n\t\tif (enableCommandLoop) {\n\t\t\tyield* Effect.forkScoped(commandLoop(supervisorState));\n\t\t}\n\n\t\t// Tear-down finalizer: closes every plugin scope in reverse-dep\n\t\t// order. The supervisor scope itself closes when the surrounding\n\t\t// caller closes — `Scope.close` cascades to plugin scopes.\n\t\tyield* Effect.addFinalizer(() =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst fullDrain = plan(graph, {\n\t\t\t\t\tkind: 'graph-keys',\n\t\t\t\t\tkeys: [...graph.nodes.keys()],\n\t\t\t\t});\n\t\t\t\tyield* teardownKeys(graph, registry, fullDrain.teardownOrder).pipe(\n\t\t\t\t\tEffect.catch(() => Effect.void),\n\t\t\t\t);\n\t\t\t}),\n\t\t);\n\n\t\tconst awaitShutdown: Effect.Effect<void> = Deferred.await(shutdownComplete);\n\n\t\t// The programmable `runCommand` surface is exactly the submit-and-await\n\t\t// path the control plane uses; share the one implementation.\n\t\tconst runCommand = submitCommand;\n\n\t\tconst handle = {\n\t\t\tidentity,\n\t\t\tgraph,\n\t\t\tregistry,\n\t\t\tevents: hub,\n\t\t\tcommands,\n\t\t\trunCommand,\n\t\t\tstate,\n\t\t\twatchIndex,\n\t\t\tnotifyWatchFire,\n\t\t\tawaitShutdown,\n\t\t} satisfies SupervisorHandle;\n\t\treturn { handle, runInitialAcquire } satisfies SupervisorStartup;\n\t});\n\n/**\n * Boot a supervisor for `stack` and wait for the initial acquire to\n * finish before returning the handle. Existing callers that only need\n * a ready-or-failed handle keep this simpler entry point; live surfaces\n * that need startup display use `startSupervisor`.\n */\nexport const supervise = (\n\tstack: SupervisedStack,\n\tidentity: Identity,\n\tstate: SubscriptionRef.SubscriptionRef<SubscribableState>,\n\tpluginContext: Context.Context<never> = Context.empty(),\n\tdispatcher: ContributionDispatcher = noopContributionDispatcher,\n\tcommandHandler?: SupervisorCommandHandler,\n\tpostAcquireHook?: SupervisorPostAcquireHook,\n): Effect.Effect<SupervisorHandle, SupervisorError, Scope.Scope> =>\n\tEffect.gen(function* () {\n\t\tconst startup = yield* startSupervisor(\n\t\t\tstack,\n\t\t\tidentity,\n\t\t\tstate,\n\t\t\tpluginContext,\n\t\t\tdispatcher,\n\t\t\tcommandHandler,\n\t\t\tpostAcquireHook,\n\t\t\t{ commandLoop: true },\n\t\t);\n\t\tyield* startup.runInitialAcquire;\n\t\treturn startup.handle;\n\t});\n\n/**\n * Block until the supervisor's shutdown latch fires OR the surrounding\n * scope is interrupted (signal-driven exit). Wraps `awaitShutdown` for\n * the common \"boot then wait\" shape.\n *\n * Returns the final supervisor state so callers (CLI, programmable\n * API) can inspect the projection after shutdown.\n */\nexport const runToShutdown = (\n\thandle: SupervisorHandle,\n): Effect.Effect<SubscribableState, never, never> =>\n\tEffect.gen(function* () {\n\t\tyield* handle.awaitShutdown;\n\t\treturn yield* SubscriptionRef.get(handle.state);\n\t});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAsFA,MAAM,eAAe,gBAAgBA,QAAM;AAC3C,MAAM,oBAAoB,gBAAgB,WAAW;AAmCrD,MAAM,4BACL,QACA,YACA,cAC8B;CAC9B,IAAI,CAAC,WAAW,WAAW,UAAU,GAAG,OAAO;CAC/C,MAAM,OAAO,WAAW,MAAM,CAAiB;CAC/C,IAAI,KAAK,WAAW,GAAG,OAAO;CAC9B,OAAO;EACN,KAAK;EACL;EACA;EACA,SAAS;EACT,QAAQ;EACR,QAAQ;EACR,SAAS;GAAE,QAAQ;GAAW,aAAa;GAAM,eAAe;GAAM,SAAS,CAAC;EAAE;EAClF,eAAe;EACf;CACD;AACD;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,MAAa,mBACZ,OACA,UACA,OACA,gBAAwC,QAAQ,MAAM,GACtD,aAAqC,4BACrC,gBACA,iBACA,UAAoC,CAAC,MAErC,OAAO,IAAI,aAAa;CAIvB,MAAM,aAA0B,aAAa,KAAK,eAAe,UAAU;CAE3E,OAAO,WAAW,IAAI,cAAc,MAAM;EACzC,OAAO;EACP,SAAS;EACT,QAAQ;GACP,KAAK,SAAS;GACd,OAAO,SAAS;GAChB,SAAS,SAAS;GAClB,aAAa,MAAM,QAAQ;EAC5B;CACD,CAAC;CAKD,OAAO,gBAAgB,OACtB,QACC,OACC;EACA,GAAG,YAAY,GAAG;GACjB,KAAK,SAAS;GACd,OAAO,SAAS;GAChB,SAAS,SAAS;EACnB,CAAC;EACD,OAAO;GACN,GAAG,EAAE;GACL,WAAW,EAAE,MAAM,cAAc,IAAI,KAAK,IAAI,IAAI,EAAE,MAAM;GAC1D,OAAO;EACR;CACD,EACF;CAGA,MAAM,QAAQ,OAAO,aAAa,MAAM,OAAO,CAAC,CAAC,KAChD,OAAO,UAAU,UAAU,IAAI,oBAAoB,EAAE,MAAM,CAAC,CAAC,CAC9D;CAGA,MAAM,MAAM,OAAO,MAAM,UAAuB;CAChD,MAAM,WAAW,OAAO,MAAM,UAAyB;CACvD,MAAM,iBAAiB,OAAO,MAAM,UAAyB;CAM7D,MAAM,mBAAuC,OAAO,IAAI,KACvD,IACD;CACA,MAAM,gBAAgB,OAAO,IAAI,KAAK,KAAK;CAC3C,MAAM,mBAAmB,OAAO,SAAS,KAAW;CACpD,MAAM,wBAAwB,OAAO,IAAI,KAAK,KAAK;CAOnD,MAAM,WAAW,OAAO,aAAa,QAAQ,YAAY,CAAC,CAAC;CAC3D,MAAM,SAAS,0BAA0B,YAAY,OAAO,KAAK,QAAQ;CAezE,MAAM,kBAAkB,YACvB,OAAO,OAAO,MAAM,MAAM,gBAAgB;EAAE,MAAM;EAAmB;CAAQ,CAAC,CAAC;CAChF,MAAM,iBAAiB,YACtB,OAAO,IAAI,aAAa;EACvB,MAAM,aAAa,OAAO,SAAS,KAA+B;EAClE,OAAO,MAAM,MAAM,gBAAgB;GAClC,MAAM;GACN,YAAY;IAAE;IAAS;GAAW;EACnC,CAAC;EACD,MAAM,OAAO,OAAO,SAAS,MAAM,UAAU;EAC7C,IAAI,KAAK,UAAU,IAAI,GACtB,OAAO,OAAO,OAAO,UAAU,KAAK,KAAK;CAE3C,CAAC;CAGF,MAAM,kBAAkB,OAAO,OAAO;CAGtC,MAAM,WAAW,OAAO,cAAc,OAAO,iBADhC,uBAAuB,OAAO,GACsB,CAAC;CAUlE,MAAM,qBAAqB,8BAA8B;EACxD;EACA;EACA,cAAc,MAAM;EACpB,iBAAiB,QAAQ,mBAAmB;EAC5C;EACA;CACD,CAAC;CAED,MAAM,uBAAuB,cAAc,KAC1C,QAAQ,IAAIA,UAAQA,SAAO,GAAG,MAAM,CAAC,GAGrC,QAAQ,IAAI,UAAU,SAAS,GAAG,QAAQ,CAAC,GAK3C,QAAQ,IACP,qBACA,oBAAoB,GAAG;EACtB;EACA;EACA;EACA,QAAQ;CACT,CAAC,CACF,CACD;CAOA,KAAK,MAAM,CAAC,KAAK,SAAS,MAAM,OAAO;EACtC,MAAM,kBAAkB,yBAAyB,KAAK,KAAK,OAAO,IAAI,KAAK,IAAI,CAAC;EAChF,OAAO,gBAAgB,OAAO,QAAQ,OAAO;GAC5C,GAAI,oBAAoB,OAAO,IAAI,eAAe,GAAG,eAAe;GACpE,MAAM,EAAE,KAAK,MAAM,MAAM,EAAE,QAAQ,GAAG,IACnC,EAAE,OACF,CACA,GAAG,EAAE,MACL;IACC;IACA,MAAM,KAAK,OAAO;IAClB,QAAQ;IACR,OAAO;IACP,WAAW;IACX,SAAS;KAAE,OAAO,CAAC;KAAG,OAAO;KAAiB,WAAW;IAAM;IAC/D,WAAW,CAAC;IACZ,2BAA2B;IAC3B,SAAS,KAAK,OAAO;IACrB,iBAAiB,KAAK,OAAO,mBAAmB,KAAK,OAAO;GAC7D,CACD;EACH,EAAE;CACH;CAUA,MAAM,sBAAsB,kBAAkB,KAAK,eAAe,EAAE,MAAM,GAAG,CAAC;CAC9E,IAAI,oBAAoB,SAAS,IAChC,OAAO,OAAO,WACb,0JAED;CAED,MAAM,cAAc,oBAAoB;CAExC,MAAM,oBAAoB,QAAQ,gBAAgB;CAClD,IAAI,mBAAmB;EACtB,OAAO,OAAO,WAAW,qBAAqB,QAAQ,CAAC;EACvD,OAAO,OAAO,WACb,OAAO,IAAI,aAAa;GACvB,OAAO,MAAM;IACZ,MAAM,UAAU,OAAO,MAAM,KAAK,QAAQ;IAC1C,OAAO,MAAM,MAAM,gBAAgB;KAAE,MAAM;KAAmB;IAAQ,CAAC;GACxE;EACD,CAAC,CACF;CACD;CAEA,MAAM,kBAAmC;EACxC;EACA;EACA,KAAK;EACL;EACA;EACA;EACA;EACA;EACA;EACA,eAAe;EACf;EACA;EACA;EACA;EACA;EACA;CACD;CAEA,MAAM,oBAAoB,OAAO,IAAI,aAAa;EAEjD,IAAI,OAD0B,IAAI,OAAO,wBAAwB,YAAY,CAAC,SAAS,IAAI,CAAC,GACxE;EACpB,IAAI,OAAO,IAAI,IAAI,aAAa,GAAG;EACnC,OAAO,iBACN,OACA,UACA,OACA,KACA,sBACA,YACA,QACA,UACA,eACD;EACA,IAAI,OAAO,IAAI,IAAI,aAAa,GAAG;EAEnC,IAAI,OADwB,mBAAmB,OAAO,QAAQ,GAC5C;GACjB,OAAO,mBAAmB,eAAe;GACzC,IAAI,EAAE,OAAO,IAAI,IAAI,aAAa,IACjC,OAAO,cAAc,OAAO,SAAS;EAEvC,OAAO,IAAI,EAAE,OAAO,IAAI,IAAI,aAAa,IACxC,OAAO,cAAc,OAAO,SAAS;CAEvC,CAAC;CAID,MAAM,aAAa,gBAAgB,MAAM,KAAK;CAE9C,MAAM,mBAAmB,SACxB,OAAO,IAAI,aAAa;EAIvB,MAAM,UAAU,UAAU,YAAY,MAAM,SAAS;EACrD,IAAI,QAAQ,SAAS,GAAG;EAIxB,KAAK,MAAM,OAAO,SACjB,OAAO,MAAM,MAAM,UAAU;GAC5B,KAAK;GACL,WAAW;EACZ,CAAyB;CAE3B,CAAC;CAGF,IAAI,mBACH,OAAO,OAAO,WAAW,YAAY,eAAe,CAAC;CAMtD,OAAO,OAAO,mBACb,OAAO,IAAI,aAAa;EAKvB,OAAO,aAAa,OAAO,UAJT,KAAK,OAAO;GAC7B,MAAM;GACN,MAAM,CAAC,GAAG,MAAM,MAAM,KAAK,CAAC;EAC7B,CAC6C,CAAC,CAAC,aAAa,CAAC,CAAC,KAC7D,OAAO,YAAY,OAAO,IAAI,CAC/B;CACD,CAAC,CACF;CAoBA,OAAO;EAAE,QAAA;GAXR;GACA;GACA;GACA,QAAQ;GACR;GACA,YAAA;GACA;GACA;GACA;GACA,eAhB0C,SAAS,MAAM,gBAgB7C;EAEC;EAAG;CAAkB;AACpC,CAAC"}
|