@devframes/hub 0.9.8 → 0.9.10

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.
@@ -1,4 +1,4 @@
1
- import { A as DevframeViewIframe, T as DevframeDocksHost, b as DevframeDockEntryIcon, l as DevframeCommandsHost, m as DevframeDockActivation } from "./settings-DwxCyeI6.mjs";
1
+ import { A as DevframeViewIframe, T as DevframeDocksHost, b as DevframeDockEntryIcon, l as DevframeCommandsHost, m as DevframeDockActivation } from "./settings-CWHPzCWS.mjs";
2
2
  import { CreateHostContextOptions } from "devframe/node";
3
3
  import { DevframeDefinition, DevframeHost, DevframeNodeContext, EventEmitter } from "devframe/types";
4
4
  import { ChildProcess } from "node:child_process";
@@ -154,13 +154,13 @@ interface DevframeMessageHandle {
154
154
  dismiss: () => Promise<void>;
155
155
  }
156
156
  /**
157
- * Extra fields accepted by the per-level message shortcuts —
157
+ * Extra fields accepted by the per-level message shortcuts:
158
158
  * everything on {@link DevframeMessageEntryInput} except the
159
159
  * `message` and `level` the shortcut itself provides.
160
160
  */
161
161
  type DevframeMessageShortcutInput = Omit<DevframeMessageEntryInput, 'message' | 'level'>;
162
162
  /**
163
- * Per-level shortcuts shared by the client and the node host —
163
+ * Per-level shortcuts shared by the client and the node host:
164
164
  * `messages.info('...')` is `messages.add({ message: '...', level: 'info' })`.
165
165
  */
166
166
  interface DevframeMessagesLevelShortcuts {
@@ -197,7 +197,7 @@ interface DevframeMessagesListDelta {
197
197
  entries: DevframeMessageEntry[];
198
198
  /** Ids removed since the cursor (empty when `full`) */
199
199
  removedIds: string[];
200
- /** The version cursor — pass back as `since` on the next call */
200
+ /** The version cursor; pass back as `since` on the next call */
201
201
  version: number;
202
202
  /**
203
203
  * When `true`, `entries` is the complete snapshot and any locally cached
@@ -262,7 +262,7 @@ interface DevframeTerminalsHost {
262
262
  * {@link DevframePtyTerminalSession.resize}, TUI-capable. The session is
263
263
  * marked `interactive`, so a hub-aware terminal UI (e.g. the terminals
264
264
  * plugin) surfaces it as writable rather than read-only. Powered by
265
- * `zigpty` — where its native bindings can't load, it degrades to
265
+ * `zigpty`; where its native bindings can't load, it degrades to
266
266
  * pipe-based terminal emulation.
267
267
  */
268
268
  startPtySession: (executeOptions: DevframePtyExecuteOptions, terminal: Omit<DevframeTerminalSessionBase, 'status'>) => Promise<DevframePtyTerminalSession>;
@@ -284,7 +284,7 @@ interface DevframeTerminalSessionBase {
284
284
  /**
285
285
  * Whether the session may be restarted in place (re-running its command).
286
286
  * Defaults to `true`. Set `false` for sessions whose lifecycle is owned
287
- * elsewhere — e.g. a one-shot build, or a server (like code-server) that
287
+ * elsewhere, e.g. a one-shot build, or a server (like code-server) that
288
288
  * should be restarted through its own controls rather than by re-spawning
289
289
  * the raw process. A hub-aware terminal UI hides its restart affordance for
290
290
  * these, and `hub:terminals:restart` rejects them.
@@ -302,7 +302,7 @@ interface DevframeChildProcessExecuteOptions {
302
302
  env?: Record<string, string>;
303
303
  }
304
304
  /**
305
- * The settled outcome of a {@link DevframeChildProcessTerminalSession} run —
305
+ * The settled outcome of a {@link DevframeChildProcessTerminalSession} run:
306
306
  * stdout/stderr captured separately (unlike the session's merged display
307
307
  * `stream`), plus the process's exit code (`undefined` if it was killed by a
308
308
  * signal before exiting).
@@ -313,7 +313,7 @@ interface DevframeChildProcessOutput {
313
313
  exitCode: number | undefined;
314
314
  }
315
315
  /**
316
- * A live handle on a child process's outcome — mirrors the ergonomics of
316
+ * A live handle on a child process's outcome, mirroring the ergonomics of
317
317
  * `tinyexec`'s `Result` (a promise-like paired with synchronous accessors) so
318
318
  * callers migrating from a `tinyexec`/`execa`-based subprocess API (e.g.
319
319
  * Nuxt DevTools' `startSubprocess().getResult()`) can adopt
@@ -334,11 +334,11 @@ interface DevframeChildProcessTerminalSession extends DevframeTerminalSession {
334
334
  getChildProcess: () => ChildProcess | undefined;
335
335
  /**
336
336
  * Get a live handle on the current run's outcome. Reflects the most recent
337
- * `restart()` — call it again after restarting to track the new run.
337
+ * `restart()`; call it again after restarting to track the new run.
338
338
  */
339
339
  getResult: () => DevframeChildProcessResult;
340
340
  terminate: () => Promise<void>;
341
- /** Throws `DF8206` once the session's output stream has closed (after a natural exit or `terminate()`) — drop it with `ctx.terminals.remove(session)` and start a fresh session instead. */
341
+ /** Throws `DF8206` once the session's output stream has closed (after a natural exit or `terminate()`); drop it with `ctx.terminals.remove(session)` and start a fresh session instead. */
342
342
  restart: () => Promise<void>;
343
343
  }
344
344
  interface DevframePtyExecuteOptions {
@@ -384,7 +384,7 @@ interface DevframePtyTerminalSession extends DevframeTerminalSession {
384
384
  */
385
385
  getResult: () => DevframePtyResult;
386
386
  terminate: () => Promise<void>;
387
- /** Throws `DF8206` once the session's output stream has closed (after a natural exit or `terminate()`) — drop it with `ctx.terminals.remove(session)` and start a fresh session instead. */
387
+ /** Throws `DF8206` once the session's output stream has closed (after a natural exit or `terminate()`); drop it with `ctx.terminals.remove(session)` and start a fresh session instead. */
388
388
  restart: () => Promise<void>;
389
389
  }
390
390
  //#endregion
@@ -399,7 +399,7 @@ interface InstallDevframeOptions {
399
399
  * this to customize the entry's `category`, override the icon, hide it
400
400
  * via `when` (or only its dock-bar button via `visibility`), etc. Takes
401
401
  * precedence over the definition's own {@link DevframeDefinition.dock}
402
- * defaults. Cannot change `id`, `type`, or `url` — those are derived from
402
+ * defaults. Cannot change `id`, `type`, or `url`, which are derived from
403
403
  * the devframe definition.
404
404
  */
405
405
  dock?: Partial<Omit<DevframeViewIframe, 'id' | 'type' | 'url'>>;
@@ -441,7 +441,7 @@ declare module 'devframe/types' {
441
441
  * Ask the active viewer to switch its focused dock to `dockId`, optionally
442
442
  * carrying `params` for the target dock to interpret (e.g.
443
443
  * `{ sessionId }` for the terminals dock). Any connected client may call
444
- * it — a mounted devframe in its own iframe steers the host shell's dock
444
+ * it, so a mounted devframe in its own iframe steers the host shell's dock
445
445
  * selection. Handled by {@link import('./rpc-builtins').hubDocksActivate}.
446
446
  */
447
447
  'hub:docks:activate': (input: {
@@ -477,7 +477,7 @@ declare module 'devframe/types' {
477
477
  }
478
478
  }
479
479
  /**
480
- * Hub-augmented node context — extends devframe's framework-neutral
480
+ * Hub-augmented node context that extends devframe's framework-neutral
481
481
  * `DevframeNodeContext` with the hub-level subsystems (`docks`,
482
482
  * `terminals`, `messages`, `commands`).
483
483
  *
@@ -486,7 +486,7 @@ declare module 'devframe/types' {
486
486
  * filesystem reveal, etc.) ship as kit-registered RPC functions rather
487
487
  * than as part of this surface. JSON-render is an opt-in integration
488
488
  * (`@devframes/json-render`) that augments any devframe context and
489
- * contributes its own dock type — use `createJsonRenderView` from
489
+ * contributes its own dock type; use `createJsonRenderView` from
490
490
  * `@devframes/json-render/node`.
491
491
  */
492
492
  interface DevframeHubContext extends DevframeNodeContext {
@@ -499,13 +499,13 @@ interface DevframeHubContext extends DevframeNodeContext {
499
499
  * Install a {@link DevframeDefinition} into this hub: serve its SPA at the
500
500
  * resolved base, synthesize an iframe dock from its metadata, and run its
501
501
  * `setup(ctx)`. The imperative counterpart to `initHub`'s declarative
502
- * `devframes` list — call it from a hub host's `configure(ctx)`, or wherever
502
+ * `devframes` list. Call it from a hub host's `configure(ctx)`, or wherever
503
503
  * you hold the context, to plug an extra devframe in.
504
504
  */
505
505
  install: (devframe: DevframeDefinition, options?: InstallDevframeOptions) => Promise<void>;
506
506
  }
507
507
  /**
508
- * Options for {@link createHubContext} — devframe's
508
+ * Options for {@link createHubContext}: devframe's
509
509
  * {@link CreateHostContextOptions} plus any hub-level additions kits layer on
510
510
  * through declaration merging.
511
511
  */
@@ -1,5 +1,5 @@
1
- import "./context-BJFGlcf8.mjs";
2
- import "./settings-DwxCyeI6.mjs";
1
+ import "./context-Di-fhMKP.mjs";
2
+ import "./settings-CWHPzCWS.mjs";
3
3
  import { RpcDefinitionsFilter, RpcDefinitionsToFunctions } from "devframe/rpc";
4
4
  import { ConnectionMeta as ConnectionMeta$1, DevframeCapabilities, DevframeDiagnosticsHost, DevframeDiagnosticsLogger, DevframeHost as DevframeHost$1, DevframeNodeRpcSession, DevframeRpcClientFunctions, DevframeRpcServerFunctions, DevframeRpcSharedStates, DevframeViewHost, EventEmitter as EventEmitter$1, EventUnsubscribe, EventsMap, RpcBroadcastOptions, RpcFunctionsHost, RpcSharedStateGetOptions, RpcSharedStateHost, RpcStreamingChannel, RpcStreamingChannelOptions, RpcStreamingHost } from "devframe/types";
5
5
  export { RpcStreamingHost as S, RpcFunctionsHost as _, DevframeHost$1 as a, RpcStreamingChannel as b, DevframeRpcServerFunctions as c, EventEmitter$1 as d, EventUnsubscribe as f, RpcDefinitionsToFunctions as g, RpcDefinitionsFilter as h, DevframeDiagnosticsLogger as i, DevframeRpcSharedStates as l, RpcBroadcastOptions as m, DevframeCapabilities as n, DevframeNodeRpcSession as o, EventsMap as p, DevframeDiagnosticsHost as r, DevframeRpcClientFunctions as s, ConnectionMeta$1 as t, DevframeViewHost as u, RpcSharedStateGetOptions as v, RpcStreamingChannelOptions as x, RpcSharedStateHost as y };
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { A as DevframeMessagesLevelShortcuts, C as DevframeMessageEntryInput, D as DevframeMessageShortcutInput, E as DevframeMessageLevel, O as DevframeMessagesClient, S as DevframeMessageEntryFrom, T as DevframeMessageHandle, _ as DevframeMessageAction, a as DevframeChildProcessExecuteOptions, b as DevframeMessageElementPosition, c as DevframeChildProcessTerminalSession, d as DevframePtyResult, f as DevframePtyTerminalSession, g as DevframeTerminalsHost, h as DevframeTerminalStatus, j as DevframeMessagesListDelta, k as DevframeMessagesHost, l as DevframePtyExecuteOptions, m as DevframeTerminalSessionBase, n as DevframeHubContext, o as DevframeChildProcessOutput, p as DevframeTerminalSession, s as DevframeChildProcessResult, t as CreateHubContextOptions, u as DevframePtyOutput, v as DevframeMessageActivateAction, w as DevframeMessageFilePosition, x as DevframeMessageEntry, y as DevframeMessageCommandAction } from "./context-BJFGlcf8.mjs";
2
- import { A as DevframeViewIframe, C as DevframeDockUserEntry, D as DevframeViewBuiltin, E as DevframeViewAction, F as RemoteConnectionInfo, I as RemoteDockOptions, M as DevframeViewLauncherStatus, N as FrameSubTabsConfig, O as DevframeViewCustomRender, P as NavTarget, S as DevframeDockPanelState, T as DevframeDocksHost, _ as DevframeDockEntry, a as DevframeCommandEntry, b as DevframeDockEntryIcon, c as DevframeCommandShortcutOverrides, d as DevframeServerCommandEntry, f as DevframeServerCommandInput, g as DevframeDockEntriesGrouped, h as DevframeDockBadgeVariant, i as DevframeCommandBase, j as DevframeViewLauncher, k as DevframeViewGroup, l as DevframeCommandsHost, m as DevframeDockActivation, n as DevframeClientCommand, o as DevframeCommandHandle, p as ClientScriptEntry, r as DevframeCommandAgentOptions, s as DevframeCommandKeybinding, t as DevframeDocksUserSettings, u as DevframeCommandsHostEvents, v as DevframeDockEntryBase, w as DevframeDocksActiveState, x as DevframeDockEntryRegistry, y as DevframeDockEntryCategory } from "./settings-DwxCyeI6.mjs";
3
- import { S as RpcStreamingHost, _ as RpcFunctionsHost, a as DevframeHost, b as RpcStreamingChannel, c as DevframeRpcServerFunctions, d as EventEmitter, f as EventUnsubscribe, g as RpcDefinitionsToFunctions, h as RpcDefinitionsFilter, i as DevframeDiagnosticsLogger, l as DevframeRpcSharedStates, m as RpcBroadcastOptions, n as DevframeCapabilities, o as DevframeNodeRpcSession, p as EventsMap, r as DevframeDiagnosticsHost, s as DevframeRpcClientFunctions, t as ConnectionMeta, u as DevframeViewHost, v as RpcSharedStateGetOptions, x as RpcStreamingChannelOptions, y as RpcSharedStateHost } from "./index-OpSX0dK5.mjs";
1
+ import { A as DevframeMessagesLevelShortcuts, C as DevframeMessageEntryInput, D as DevframeMessageShortcutInput, E as DevframeMessageLevel, O as DevframeMessagesClient, S as DevframeMessageEntryFrom, T as DevframeMessageHandle, _ as DevframeMessageAction, a as DevframeChildProcessExecuteOptions, b as DevframeMessageElementPosition, c as DevframeChildProcessTerminalSession, d as DevframePtyResult, f as DevframePtyTerminalSession, g as DevframeTerminalsHost, h as DevframeTerminalStatus, j as DevframeMessagesListDelta, k as DevframeMessagesHost, l as DevframePtyExecuteOptions, m as DevframeTerminalSessionBase, n as DevframeHubContext, o as DevframeChildProcessOutput, p as DevframeTerminalSession, s as DevframeChildProcessResult, t as CreateHubContextOptions, u as DevframePtyOutput, v as DevframeMessageActivateAction, w as DevframeMessageFilePosition, x as DevframeMessageEntry, y as DevframeMessageCommandAction } from "./context-Di-fhMKP.mjs";
2
+ import { A as DevframeViewIframe, C as DevframeDockUserEntry, D as DevframeViewBuiltin, E as DevframeViewAction, F as RemoteConnectionInfo, I as RemoteDockOptions, M as DevframeViewLauncherStatus, N as FrameSubTabsConfig, O as DevframeViewCustomRender, P as NavTarget, S as DevframeDockPanelState, T as DevframeDocksHost, _ as DevframeDockEntry, a as DevframeCommandEntry, b as DevframeDockEntryIcon, c as DevframeCommandShortcutOverrides, d as DevframeServerCommandEntry, f as DevframeServerCommandInput, g as DevframeDockEntriesGrouped, h as DevframeDockBadgeVariant, i as DevframeCommandBase, j as DevframeViewLauncher, k as DevframeViewGroup, l as DevframeCommandsHost, m as DevframeDockActivation, n as DevframeClientCommand, o as DevframeCommandHandle, p as ClientScriptEntry, r as DevframeCommandAgentOptions, s as DevframeCommandKeybinding, t as DevframeDocksUserSettings, u as DevframeCommandsHostEvents, v as DevframeDockEntryBase, w as DevframeDocksActiveState, x as DevframeDockEntryRegistry, y as DevframeDockEntryCategory } from "./settings-CWHPzCWS.mjs";
3
+ import { S as RpcStreamingHost, _ as RpcFunctionsHost, a as DevframeHost, b as RpcStreamingChannel, c as DevframeRpcServerFunctions, d as EventEmitter, f as EventUnsubscribe, g as RpcDefinitionsToFunctions, h as RpcDefinitionsFilter, i as DevframeDiagnosticsLogger, l as DevframeRpcSharedStates, m as RpcBroadcastOptions, n as DevframeCapabilities, o as DevframeNodeRpcSession, p as EventsMap, r as DevframeDiagnosticsHost, s as DevframeRpcClientFunctions, t as ConnectionMeta, u as DevframeViewHost, v as RpcSharedStateGetOptions, x as RpcStreamingChannelOptions, y as RpcSharedStateHost } from "./index-faD2d5iX.mjs";
4
4
  import { WhenContext, WhenExpression } from "devframe/utils/when";
5
5
  //#region src/define.d.ts
6
6
  declare const defineHubRpcFunction: <NAME extends string, TYPE extends import("devframe/rpc").RpcFunctionType, ARGS extends any[], RETURN = void, const AS extends import("devframe/rpc").RpcArgsSchema | undefined = undefined, const RS extends import("devframe/rpc").RpcReturnSchema | undefined = undefined>(definition: import("devframe/rpc").RpcFunctionDefinition<NAME, TYPE, ARGS, RETURN, AS, RS, DevframeHubContext>) => import("devframe/rpc").RpcFunctionDefinition<NAME, TYPE, ARGS, RETURN, AS, RS, DevframeHubContext>;
@@ -0,0 +1,74 @@
1
+ import { n as DevframeHubContext, t as CreateHubContextOptions } from "../context-Di-fhMKP.mjs";
2
+ import { DevframeHubUi, DevframesInput, DockRendererRegistration } from "./initiate.mjs";
3
+ import { DevframeServiceInput, DevframeStorageScope } from "devframe/types";
4
+ //#region src/node/build.d.ts
5
+ interface BuildHubOptions {
6
+ /**
7
+ * Output directory the hub subtree is written into. It corresponds to the
8
+ * hub {@link BuildHubOptions.base} at serve time: building with
9
+ * `base: '/__devframes/'` into `dist/__devframes` makes the deployed app's
10
+ * `dist/` servable as-is by any static file server.
11
+ */
12
+ outDir: string;
13
+ /**
14
+ * Mount base the deployed hub answers under (baked into every absolute URL
15
+ * the build emits: page-script rewrites, renderer modules, per-frame meta
16
+ * pointers). Default: `/__devframes/`.
17
+ */
18
+ base?: string;
19
+ /** Devframes to bake as docks, same input as `initHub({ devframes })`. */
20
+ devframes?: DevframesInput;
21
+ /** Host-level wire services, same contract as `initHub({ services })`. */
22
+ services?: DevframeServiceInput[];
23
+ /** Extra RPC declarations registered at context creation. */
24
+ rpcDeclarations?: CreateHubContextOptions['builtinRpcDeclarations'];
25
+ /**
26
+ * Runs once the context exists and every `devframes` entry is mounted;
27
+ * register docks, commands, and messages surfaces here, exactly like
28
+ * `initHub({ configure })`. Everything it registers is baked into the
29
+ * static shared-state snapshot.
30
+ */
31
+ configure?: (ctx: DevframeHubContext) => void | Promise<void>;
32
+ /**
33
+ * The hub's UI slot: the viewer SPA is copied to the hub base, the
34
+ * `embedded.js` bootstrap next to it, and `setup(ctx)` runs so its static
35
+ * config (branding, dock preferences) is baked into the connection meta.
36
+ */
37
+ ui?: DevframeHubUi;
38
+ /** Prebuilt dock-renderer modules, copied to `<base>__renderers/<type>.mjs`. */
39
+ renderers?: readonly DockRendererRegistration[];
40
+ /** Name written into `<base>__index.json`. */
41
+ name?: string;
42
+ /** Version written into `<base>__index.json`. */
43
+ version?: string;
44
+ /** Working directory for the hub context. Default: `process.cwd()`. */
45
+ cwd?: string;
46
+ /** Override where persisted devframe state lives during the build. */
47
+ getStorageDir?: (scope: DevframeStorageScope) => string;
48
+ /** Pretty-print RPC dump JSON files. Default: `false` (minified shards). */
49
+ pretty?: boolean;
50
+ }
51
+ /**
52
+ * Produce a self-contained static deploy of a whole hub, the multi-devframe
53
+ * counterpart of devframe's `createBuild`:
54
+ *
55
+ * - Build a `mode: 'build'` hub context and mount every devframe: each
56
+ * SPA is copied to `<outDir>/<id>/`, an absolute-path page script to
57
+ * `<outDir>/<id>/__page-script/`, and its `setup(ctx)` runs.
58
+ * - Copy the UI slot's viewer SPA, `embedded.js`, and the renderer
59
+ * modules, and write the discovery documents (`__index.json`,
60
+ * `__client-imports.js`).
61
+ * - Write `__connection.json` (`{ backend: 'static' }`, carrying
62
+ * `ctx.staticConfig` as `configs`) at the hub base and at every frame
63
+ * base (each pointing back at the hub's own meta via `baseUrl`).
64
+ * - Bake the shared RPC dump under `<outDir>/__rpc-dump/`: every
65
+ * `static`/`snapshot` RPC plus a snapshot of each shared-state key
66
+ * (docks, commands, renderer manifest), so `createDevframeClientRuntime`
67
+ * and every frame SPA boot from the dump with no live server.
68
+ *
69
+ * Reads work from the baked dump; live writes (messages, terminals,
70
+ * commands execution) have no server and degrade to no-ops in the clients.
71
+ */
72
+ declare function buildHub(options: BuildHubOptions): Promise<void>;
73
+ //#endregion
74
+ export { BuildHubOptions, buildHub };
@@ -0,0 +1,167 @@
1
+ import { a as normalizeHubBase, i as DOCK_RENDERERS_STATE_KEY } from "../constants-Da23JxCZ.mjs";
2
+ import { b as diagnostics, t as createHubContext } from "../context-BWaW6N53.mjs";
3
+ import { i as resolveRendererRegistrations, n as renderClientImportsModule, r as resolveDevframesInput, t as mountDevframes } from "../assemble-Hj36CCzH.mjs";
4
+ import { joinURL } from "ufo";
5
+ import { DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DOCK_IMPORTS_FILENAME } from "devframe/constants";
6
+ import { existsSync } from "node:fs";
7
+ import fs from "node:fs/promises";
8
+ import process from "node:process";
9
+ import { createH3DevframeHost } from "devframe/internal";
10
+ import { collectStaticRpcDump, writeStaticRpcDump } from "devframe/rpc/dump";
11
+ import { colors } from "devframe/utils/colors";
12
+ import { dirname, resolve } from "pathe";
13
+ //#region src/node/build.ts
14
+ /**
15
+ * Produce a self-contained static deploy of a whole hub, the multi-devframe
16
+ * counterpart of devframe's `createBuild`:
17
+ *
18
+ * - Build a `mode: 'build'` hub context and mount every devframe: each
19
+ * SPA is copied to `<outDir>/<id>/`, an absolute-path page script to
20
+ * `<outDir>/<id>/__page-script/`, and its `setup(ctx)` runs.
21
+ * - Copy the UI slot's viewer SPA, `embedded.js`, and the renderer
22
+ * modules, and write the discovery documents (`__index.json`,
23
+ * `__client-imports.js`).
24
+ * - Write `__connection.json` (`{ backend: 'static' }`, carrying
25
+ * `ctx.staticConfig` as `configs`) at the hub base and at every frame
26
+ * base (each pointing back at the hub's own meta via `baseUrl`).
27
+ * - Bake the shared RPC dump under `<outDir>/__rpc-dump/`: every
28
+ * `static`/`snapshot` RPC plus a snapshot of each shared-state key
29
+ * (docks, commands, renderer manifest), so `createDevframeClientRuntime`
30
+ * and every frame SPA boot from the dump with no live server.
31
+ *
32
+ * Reads work from the baked dump; live writes (messages, terminals,
33
+ * commands execution) have no server and degrade to no-ops in the clients.
34
+ */
35
+ async function buildHub(options) {
36
+ const base = normalizeHubBase(options.base ?? "/__devframes/");
37
+ const cwd = options.cwd ?? process.cwd();
38
+ const outDir = resolve(cwd, options.outDir);
39
+ const rendererRegistrations = resolveRendererRegistrations(options.renderers ?? []);
40
+ if (existsSync(outDir)) await fs.rm(outDir, { recursive: true });
41
+ await fs.mkdir(outDir, { recursive: true });
42
+ /** Map a hub-base-relative URL base to its on-disk location under `outDir`. */
43
+ function resolveOutPath(urlBase) {
44
+ if (!urlBase.startsWith(base)) throw diagnostics.DF8006({
45
+ urlBase,
46
+ base
47
+ });
48
+ return resolve(outDir, urlBase.slice(base.length));
49
+ }
50
+ const frameMetaBases = [];
51
+ const host = {
52
+ ...createH3DevframeHost({
53
+ origin: "http://localhost",
54
+ appName: "devframes",
55
+ workspaceRoot: cwd,
56
+ /**
57
+ * A static build "serves" by copying: a local dist verbatim, a remote
58
+ * assets source by materializing every listed file.
59
+ */
60
+ mount: async (mountBase, source) => {
61
+ const target = resolveOutPath(mountBase);
62
+ await fs.mkdir(dirname(target), { recursive: true });
63
+ if (typeof source === "string") await fs.cp(source, target, { recursive: true });
64
+ else await source.materialize(target);
65
+ }
66
+ }),
67
+ ...options.getStorageDir ? { getStorageDir: options.getStorageDir } : {},
68
+ mountConnectionMeta: (frameBase) => {
69
+ resolveOutPath(frameBase);
70
+ frameMetaBases.push(frameBase);
71
+ }
72
+ };
73
+ const ctx = await createHubContext({
74
+ cwd,
75
+ workspaceRoot: cwd,
76
+ mode: "build",
77
+ host,
78
+ ...options.rpcDeclarations ? { builtinRpcDeclarations: options.rpcDeclarations } : {}
79
+ });
80
+ const devframes = await resolveDevframesInput(options.devframes ?? []);
81
+ for (const input of options.services ?? []) ctx.services.install(input);
82
+ const frames = [];
83
+ const setups = await mountDevframes(ctx, devframes, base, frames, false);
84
+ await ctx.services.ready();
85
+ for (const run of setups) await run();
86
+ await options.configure?.(ctx);
87
+ await options.ui?.setup?.(ctx);
88
+ await publishRendererManifest(ctx, rendererRegistrations, base, outDir);
89
+ await writeUiArtifacts(options.ui, outDir);
90
+ await fs.writeFile(resolve(outDir, DEVFRAME_DOCK_IMPORTS_FILENAME), renderClientImportsModule(ctx), "utf-8");
91
+ await fs.writeFile(resolve(outDir, "__index.json"), `${JSON.stringify({
92
+ name: options.name,
93
+ version: options.version,
94
+ base,
95
+ frames,
96
+ endpoints: {
97
+ connection: DEVFRAME_CONNECTION_META_FILENAME,
98
+ clientImports: DEVFRAME_DOCK_IMPORTS_FILENAME,
99
+ index: "__index.json",
100
+ ...options.ui?.embedded ? { embedded: "embedded.js" } : {}
101
+ }
102
+ }, null, 2)}\n`, "utf-8");
103
+ await writeConnectionMetas(ctx, base, outDir, frameMetaBases.map(resolveOutPath));
104
+ console.log(colors.cyan`[devframes-hub] writing RPC dump to ${resolve(outDir, "__rpc-dump")}`);
105
+ const dump = await collectStaticRpcDump(ctx.rpc.definitions.values(), ctx);
106
+ await writeStaticRpcDump(dump, outDir, { pretty: options.pretty });
107
+ console.log(colors.green`[devframes-hub] built ${frames.length} devframe${frames.length === 1 ? "" : "s"} -> ${outDir}`);
108
+ }
109
+ /**
110
+ * Publish the renderer manifest exactly like `initHub` does, so it is baked
111
+ * into the shared-state snapshot, and copy each prebuilt module to the URL
112
+ * the manifest advertises.
113
+ */
114
+ async function publishRendererManifest(ctx, registrations, base, outDir) {
115
+ const manifest = {};
116
+ for (const registration of registrations) {
117
+ manifest[registration.type] = {
118
+ importFrom: joinURL(base, "__renderers", `${registration.type}.mjs`),
119
+ ...registration.importName ? { importName: registration.importName } : {}
120
+ };
121
+ await fs.mkdir(resolve(outDir, "__renderers"), { recursive: true });
122
+ await fs.copyFile(registration.file, resolve(outDir, "__renderers", `${registration.type}.mjs`));
123
+ }
124
+ (await ctx.rpc.sharedState.get(DOCK_RENDERERS_STATE_KEY, { initialValue: {} })).mutate(() => manifest);
125
+ }
126
+ /**
127
+ * Copy the UI slot's artifacts: the viewer SPA owns the hub base (copied
128
+ * before the discovery documents, so those win over same-named files it
129
+ * ships), `embedded.js` next to it, plus any produced assets.
130
+ */
131
+ async function writeUiArtifacts(ui, outDir) {
132
+ if (ui?.viewer) await fs.cp(resolve(ui.viewer.distDir), outDir, { recursive: true });
133
+ if (ui?.embedded) await fs.copyFile(resolve(ui.embedded.entry), resolve(outDir, "embedded.js"));
134
+ for (const [key, produce] of Object.entries(ui?.assets ?? {})) {
135
+ const target = resolve(outDir, key);
136
+ await fs.mkdir(dirname(target), { recursive: true });
137
+ await fs.writeFile(target, produce());
138
+ }
139
+ }
140
+ /**
141
+ * Write the `backend: 'static'` connection meta at the hub base, and a copy
142
+ * at every frame base whose `baseUrl` points relative resolution (the RPC
143
+ * dump) back at the hub's own meta, so a frame SPA that fetched its
144
+ * per-frame copy (instead of inheriting the host page's connection) still
145
+ * finds the shared dump.
146
+ */
147
+ async function writeConnectionMetas(ctx, base, outDir, frameDirs) {
148
+ const jsonSerializableMethods = [];
149
+ for (const def of ctx.rpc.definitions.values()) if (def.jsonSerializable === true) jsonSerializableMethods.push(def.name);
150
+ const meta = {
151
+ backend: "static",
152
+ jsonSerializableMethods,
153
+ ...Object.keys(ctx.staticConfig).length > 0 ? { configs: ctx.staticConfig } : {}
154
+ };
155
+ await fs.writeFile(resolve(outDir, DEVFRAME_CONNECTION_META_FILENAME), JSON.stringify(meta, null, 2), "utf-8");
156
+ const frameMeta = {
157
+ ...meta,
158
+ baseUrl: joinURL(base, DEVFRAME_CONNECTION_META_FILENAME)
159
+ };
160
+ for (const frameDir of frameDirs) {
161
+ const target = resolve(frameDir, DEVFRAME_CONNECTION_META_FILENAME);
162
+ await fs.mkdir(dirname(target), { recursive: true });
163
+ await fs.writeFile(target, JSON.stringify(frameMeta, null, 2), "utf-8");
164
+ }
165
+ }
166
+ //#endregion
167
+ export { buildHub };
@@ -1,5 +1,5 @@
1
- import { C as DevframeMessageEntryInput, D as DevframeMessageShortcutInput, T as DevframeMessageHandle, a as DevframeChildProcessExecuteOptions, c as DevframeChildProcessTerminalSession, f as DevframePtyTerminalSession, g as DevframeTerminalsHost$1, i as InstallDevframeOptions, j as DevframeMessagesListDelta, k as DevframeMessagesHost$1, l as DevframePtyExecuteOptions, m as DevframeTerminalSessionBase, n as DevframeHubContext, p as DevframeTerminalSession, r as createHubContext, t as CreateHubContextOptions, x as DevframeMessageEntry } from "../context-BJFGlcf8.mjs";
2
- import { C as DevframeDockUserEntry, T as DevframeDocksHost$1, _ as DevframeDockEntry, d as DevframeServerCommandEntry, f as DevframeServerCommandInput, l as DevframeCommandsHost$1, o as DevframeCommandHandle, p as ClientScriptEntry, t as DevframeDocksUserSettings } from "../settings-DwxCyeI6.mjs";
1
+ import { C as DevframeMessageEntryInput, D as DevframeMessageShortcutInput, T as DevframeMessageHandle, a as DevframeChildProcessExecuteOptions, c as DevframeChildProcessTerminalSession, f as DevframePtyTerminalSession, g as DevframeTerminalsHost$1, i as InstallDevframeOptions, j as DevframeMessagesListDelta, k as DevframeMessagesHost$1, l as DevframePtyExecuteOptions, m as DevframeTerminalSessionBase, n as DevframeHubContext, p as DevframeTerminalSession, r as createHubContext, t as CreateHubContextOptions, x as DevframeMessageEntry } from "../context-Di-fhMKP.mjs";
2
+ import { C as DevframeDockUserEntry, T as DevframeDocksHost$1, _ as DevframeDockEntry, d as DevframeServerCommandEntry, f as DevframeServerCommandInput, l as DevframeCommandsHost$1, o as DevframeCommandHandle, p as ClientScriptEntry, t as DevframeDocksUserSettings } from "../settings-CWHPzCWS.mjs";
3
3
  import { RpcFunctionDefinitionAny } from "devframe/rpc";
4
4
  import { SharedState } from "devframe/utils/shared-state";
5
5
  //#region src/node/host-commands.d.ts
@@ -9,7 +9,7 @@ declare class DevframeCommandsHost implements DevframeCommandsHost$1 {
9
9
  readonly events: DevframeCommandsHost$1['events'];
10
10
  /**
11
11
  * Lazy agent projection: `ctx.agent` queries this provider at list/invoke
12
- * time, deriving tools from {@link commands} on demand — the commands map
12
+ * time, deriving tools from {@link commands} on demand, so the commands map
13
13
  * stays the single source of truth, nothing is mirrored or kept in sync.
14
14
  */
15
15
  private readonly agentProvider;
@@ -27,7 +27,7 @@ declare class DevframeCommandsHost implements DevframeCommandsHost$1 {
27
27
  * agent-flagged, handler-bearing command (children included) becomes a
28
28
  * callable tool. Queried lazily by the provider registered in the
29
29
  * constructor. `when` clauses evaluate client-side only and are not
30
- * enforced here — opting in a `when`-gated command is a deliberate author
30
+ * enforced here; opting in a `when`-gated command is a deliberate author
31
31
  * decision (documented on `DevframeCommandAgentOptions`).
32
32
  */
33
33
  private collectAgentTools;
@@ -52,7 +52,7 @@ declare class DevframeDocksHost implements DevframeDocksHost$1 {
52
52
  update(view: DevframeDockUserEntry): void;
53
53
  activate(dockId: string, params?: Record<string, unknown>): void;
54
54
  /**
55
- * Warn (don't throw — a client-runtime `resolveClientModule` override may still cover
55
+ * Warn (don't throw, since a client-runtime `resolveClientModule` override may still cover
56
56
  * it) when a dock declares a **bare-specifier** client script on a host
57
57
  * that advertises no `staticConfig.dock.clientModuleResolution`: the
58
58
  * browser cannot resolve a bare npm specifier natively, so the script is
@@ -79,7 +79,7 @@ declare class DevframeMessagesHost implements DevframeMessagesHost$1 {
79
79
  private _clock;
80
80
  /**
81
81
  * The tick of the newest removal record dropped from the capped
82
- * `removals` log — cursors older than this can't get a reliable delta
82
+ * `removals` log; cursors older than this can't get a reliable delta
83
83
  * and fall back to a full snapshot in {@link listSince}.
84
84
  */
85
85
  private _removalsTrimmedAt;
@@ -113,14 +113,23 @@ declare class DevframeTerminalsHost implements DevframeTerminalsHost$1 {
113
113
  private _channel?;
114
114
  constructor(context: DevframeHubContext);
115
115
  /**
116
- * Lazily acquire the streaming channel — `context.rpc` isn't assigned
116
+ * Lazily acquire the streaming channel, since `context.rpc` isn't assigned
117
117
  * until after every host is constructed, so we can't grab it in the
118
118
  * constructor.
119
119
  */
120
120
  private getStreamingChannel;
121
+ /**
122
+ * Per-run stream lifecycle shared by the child-process and PTY paths: guards
123
+ * the single-close/error transition and keeps the session's `status` in step
124
+ * with the process lifecycle (a hub-aware client sees `running` →
125
+ * `stopped`/`error` instead of a value frozen at spawn time).
126
+ */
127
+ private createStreamLifecycle;
121
128
  register(session: DevframeTerminalSession): DevframeTerminalSession;
122
129
  update(patch: PartialWithoutId<DevframeTerminalSession>): void;
123
130
  remove(session: DevframeTerminalSession): void;
131
+ /** Drain a session's output stream into its scrollback buffer and streaming sink. */
132
+ private pumpStream;
124
133
  private bindStream;
125
134
  startChildProcess(executeOptions: DevframeChildProcessExecuteOptions, terminal: Omit<DevframeTerminalSessionBase, 'status'>): Promise<DevframeChildProcessTerminalSession>;
126
135
  startPtySession(executeOptions: DevframePtyExecuteOptions, terminal: Omit<DevframeTerminalSessionBase, 'status'>): Promise<DevframePtyTerminalSession>;
@@ -128,7 +137,7 @@ declare class DevframeTerminalsHost implements DevframeTerminalsHost$1 {
128
137
  //#endregion
129
138
  //#region src/node/rpc-builtins.d.ts
130
139
  /**
131
- * `hub:commands:execute` — Invoke a registered server command by id. The
140
+ * `hub:commands:execute`: Invoke a registered server command by id. The
132
141
  * arguments after `id` are forwarded to the command's `handler(...)`.
133
142
  * Returns whatever the handler returns.
134
143
  *
@@ -151,7 +160,7 @@ declare const hubCommandsExecute: {
151
160
  __promise?: import("devframe/rpc").Thenable<import("devframe/rpc").RpcFunctionSetupResult<[id: string, ...args: any[]], Promise<unknown>>> | undefined;
152
161
  };
153
162
  /**
154
- * `hub:messages:add` — Add a message from a browser client into the hub's
163
+ * `hub:messages:add`: Add a message from a browser client into the hub's
155
164
  * messages subsystem. Marked `from: 'browser'`. Returns the serializable
156
165
  * entry (the mutation handle stays server-side).
157
166
  *
@@ -174,7 +183,7 @@ declare const hubMessagesAdd: {
174
183
  __cache?: WeakMap<object, import("devframe/rpc").Thenable<import("devframe/rpc").RpcFunctionSetupResult<[input: DevframeMessageEntryInput], Promise<DevframeMessageEntry>>>> | undefined;
175
184
  __promise?: import("devframe/rpc").Thenable<import("devframe/rpc").RpcFunctionSetupResult<[input: DevframeMessageEntryInput], Promise<DevframeMessageEntry>>> | undefined;
176
185
  };
177
- /** `hub:messages:update` — Patch a message by id; returns the updated entry (or `undefined`). */
186
+ /** `hub:messages:update`: Patch a message by id; returns the updated entry (or `undefined`). */
178
187
  declare const hubMessagesUpdate: {
179
188
  name: "hub:messages:update";
180
189
  type?: "action" | undefined;
@@ -190,7 +199,7 @@ declare const hubMessagesUpdate: {
190
199
  __cache?: WeakMap<object, import("devframe/rpc").Thenable<import("devframe/rpc").RpcFunctionSetupResult<[id: string, patch: Partial<DevframeMessageEntryInput>], Promise<DevframeMessageEntry | undefined>>>> | undefined;
191
200
  __promise?: import("devframe/rpc").Thenable<import("devframe/rpc").RpcFunctionSetupResult<[id: string, patch: Partial<DevframeMessageEntryInput>], Promise<DevframeMessageEntry | undefined>>> | undefined;
192
201
  };
193
- /** `hub:messages:remove` — Remove a message by id. */
202
+ /** `hub:messages:remove`: Remove a message by id. */
194
203
  declare const hubMessagesRemove: {
195
204
  name: "hub:messages:remove";
196
205
  type?: "action" | undefined;
@@ -206,7 +215,7 @@ declare const hubMessagesRemove: {
206
215
  __cache?: WeakMap<object, import("devframe/rpc").Thenable<import("devframe/rpc").RpcFunctionSetupResult<[id: string], Promise<void>>>> | undefined;
207
216
  __promise?: import("devframe/rpc").Thenable<import("devframe/rpc").RpcFunctionSetupResult<[id: string], Promise<void>>> | undefined;
208
217
  };
209
- /** `hub:messages:clear` — Remove every message. */
218
+ /** `hub:messages:clear`: Remove every message. */
210
219
  declare const hubMessagesClear: {
211
220
  name: "hub:messages:clear";
212
221
  type?: "action" | undefined;
@@ -223,7 +232,7 @@ declare const hubMessagesClear: {
223
232
  __promise?: import("devframe/rpc").Thenable<import("devframe/rpc").RpcFunctionSetupResult<[], Promise<void>>> | undefined;
224
233
  };
225
234
  /**
226
- * `hub:terminals:write` — Send input to an interactive PTY session spawned
235
+ * `hub:terminals:write`: Send input to an interactive PTY session spawned
227
236
  * via `ctx.terminals.startPtySession`. Lets a hub-aware terminal UI (e.g. the
228
237
  * terminals plugin) drive a session owned by another plugin.
229
238
  */
@@ -242,7 +251,7 @@ declare const hubTerminalsWrite: {
242
251
  __cache?: WeakMap<object, import("devframe/rpc").Thenable<import("devframe/rpc").RpcFunctionSetupResult<[id: string, data: string], Promise<void>>>> | undefined;
243
252
  __promise?: import("devframe/rpc").Thenable<import("devframe/rpc").RpcFunctionSetupResult<[id: string, data: string], Promise<void>>> | undefined;
244
253
  };
245
- /** `hub:terminals:resize` — Resize an interactive PTY session by id. */
254
+ /** `hub:terminals:resize`: Resize an interactive PTY session by id. */
246
255
  declare const hubTerminalsResize: {
247
256
  name: "hub:terminals:resize";
248
257
  type?: "action" | undefined;
@@ -259,7 +268,7 @@ declare const hubTerminalsResize: {
259
268
  __promise?: import("devframe/rpc").Thenable<import("devframe/rpc").RpcFunctionSetupResult<[id: string, cols: number, rows: number], Promise<void>>> | undefined;
260
269
  };
261
270
  /**
262
- * `hub:terminals:terminate` — Kill a session's process while keeping the
271
+ * `hub:terminals:terminate`: Kill a session's process while keeping the
263
272
  * session registered (its output/scrollback stays). Works for both read-only
264
273
  * child-process and interactive PTY sessions, letting a hub-aware terminal UI
265
274
  * force-kill a session owned by another plugin.
@@ -280,7 +289,7 @@ declare const hubTerminalsTerminate: {
280
289
  __promise?: import("devframe/rpc").Thenable<import("devframe/rpc").RpcFunctionSetupResult<[id: string], Promise<void>>> | undefined;
281
290
  };
282
291
  /**
283
- * `hub:terminals:restart` — Re-run a session's command in place. Rejected for
292
+ * `hub:terminals:restart`: Re-run a session's command in place. Rejected for
284
293
  * sessions registered with `restartable: false`, whose lifecycle is owned
285
294
  * elsewhere.
286
295
  */
@@ -300,7 +309,7 @@ declare const hubTerminalsRestart: {
300
309
  __promise?: import("devframe/rpc").Thenable<import("devframe/rpc").RpcFunctionSetupResult<[id: string], Promise<void>>> | undefined;
301
310
  };
302
311
  /**
303
- * `hub:terminals:remove` — Kill a session's process (when it still owns one)
312
+ * `hub:terminals:remove`: Kill a session's process (when it still owns one)
304
313
  * and drop it from the registry, disposing its output stream. Lets a hub-aware
305
314
  * terminal UI discard a stopped aggregated session.
306
315
  */
@@ -320,13 +329,13 @@ declare const hubTerminalsRemove: {
320
329
  __promise?: import("devframe/rpc").Thenable<import("devframe/rpc").RpcFunctionSetupResult<[id: string], Promise<void>>> | undefined;
321
330
  };
322
331
  /**
323
- * `hub:docks:activate` — Ask the active viewer to switch its focused dock to
332
+ * `hub:docks:activate`: Ask the active viewer to switch its focused dock to
324
333
  * `dockId`, optionally carrying `params` for the target dock to interpret
325
334
  * (e.g. `{ sessionId }` for the terminals dock to focus a session).
326
335
  *
327
336
  * Any connected client may call it, which is the point: a mounted devframe
328
337
  * running in its own iframe (on its own RPC client) can steer the host shell's
329
- * dock selection — client-local state it otherwise can't reach. The hub
338
+ * dock selection, client-local state it otherwise can't reach. The hub
330
339
  * broadcasts the request live to connected clients (the host shell switches)
331
340
  * and mirrors it into the `devframe:docks:active` shared state (a dock that
332
341
  * mounts in response still converges on it).
@@ -1,8 +1,8 @@
1
- import { _ as DevframeDocksHost, a as hubMessagesAdd, c as hubMessagesUpdate, d as hubTerminalsRestart, f as hubTerminalsTerminate, g as DevframeMessagesHost, h as DevframeTerminalsHost, i as hubDocksActivate, l as hubTerminalsRemove, n as builtinHubRpcDeclarations, o as hubMessagesClear, p as hubTerminalsWrite, r as hubCommandsExecute, s as hubMessagesRemove, t as createHubContext, u as hubTerminalsResize, v as DevframeCommandsHost } from "../context-DYUkd3il.mjs";
1
+ import { _ as DevframeCommandsHost, a as hubMessagesAdd, c as hubMessagesUpdate, d as hubTerminalsRestart, f as hubTerminalsTerminate, g as DevframeDocksHost, h as DevframeMessagesHost, i as hubDocksActivate, l as hubTerminalsRemove, m as DevframeTerminalsHost, n as builtinHubRpcDeclarations, o as hubMessagesClear, p as hubTerminalsWrite, r as hubCommandsExecute, s as hubMessagesRemove, t as createHubContext, u as hubTerminalsResize } from "../context-BWaW6N53.mjs";
2
2
  import "ufo";
3
- import "pathe";
4
- import "node:process";
5
3
  import "node:fs";
4
+ import "node:process";
5
+ import "pathe";
6
6
  import { builtinModules } from "node:module";
7
7
  import "node:url";
8
8
  import assert from "node:assert";