diff --git a/docs/guide/build-your-own-hub-ui.md b/docs/guide/build-your-own-hub-ui.md index 83258683..f1b582ed 100644 --- a/docs/guide/build-your-own-hub-ui.md +++ b/docs/guide/build-your-own-hub-ui.md @@ -16,6 +16,7 @@ interface DevframeHubUi { viewer?: { distDir: string } // a standalone SPA served at the hub base embedded?: { entry: string } // a self-contained bootstrap at embedded.js assets?: Record string | Uint8Array> // extra UI-owned files + setup?: (ctx) => void | Promise // publish static config via ctx.staticConfig } ``` @@ -24,6 +25,14 @@ prebuilt assets: the viewer SPA is built with relative asset paths, and the embedded entry is one self-contained ES module that mounts your dock into any host page. +`setup(ctx)` runs once during hub init — write your static, boot-time config +to `ctx.staticConfig`, which is serialized into `ConnectionMeta.configs` and +read by the client from the one connection handshake it already performs. The +reference UI's `createUi({ branding })` uses it to set +`ctx.staticConfig.ui = { branding, … }`; the hub never interprets what you +write. It's the structured, read-only counterpart to `assets` (arbitrary +served files). + ## The client contracts A viewer renders from the hub's shared state and drives it through diff --git a/docs/guide/devframe-definition.md b/docs/guide/devframe-definition.md index e389d7e4..f9500deb 100644 --- a/docs/guide/devframe-definition.md +++ b/docs/guide/devframe-definition.md @@ -113,6 +113,7 @@ interface DevframeNodeContext { diagnostics: DevframeDiagnosticsHost agent: DevframeAgentHost // experimental services: DevframeServicesHost // typed cross-plugin service registry + staticConfig: Partial // this context's own ConnectionMeta.configs scope: (id) => DevframeScopedNodeContext // namespaced view (preferred) } @@ -130,6 +131,22 @@ ctx.services.whenAvailable('my-plugin:sources', (sources) => { }) ``` +### Static connection configs + +`ctx.staticConfig` is this context's own `ConnectionMeta.configs` — static, boot-time data delivered once through the connection handshake every client already performs, and read-only from the browser. It's a plain, **non-reactive** object: write it during `setup(ctx)`, never during the session (it's serialized once, after setup). Contrast it with `ctx.scope(id).settings`, which is mutable and synced bidirectionally over shared-state RPC for the life of the session. + +```ts +declare module 'devframe/types' { + interface DevframeConnectionConfigsRegistry { + 'my-plugin': { featureFlag: boolean } + } +} + +ctx.staticConfig['my-plugin'] = { featureFlag: true } +``` + +`updater` receives whatever's been contributed to that key so far (or `undefined` on the first contribution), so multiple contributors sharing a key — a hub aggregating each installed devframe's own preference, for example — own their own merge semantics (overwrite, shallow-merge a record, …) rather than the host imposing one. + ### Storage scopes `ctx.host.getStorageDir(scope)` places persisted state in one of three classes: @@ -152,6 +169,7 @@ Each devframe-level host has a dedicated page: - [Shared State](./shared-state) — `ctx.rpc.sharedState` - [Diagnostics](./diagnostics) — `ctx.diagnostics` - [Agent-Native](./agent-native) — `ctx.agent` +- [Cross-Plugin Services](./services) — `ctx.services` ## Browser setup diff --git a/docs/guide/hub-initiate.md b/docs/guide/hub-initiate.md index 2ae4ab67..9499a080 100644 --- a/docs/guide/hub-initiate.md +++ b/docs/guide/hub-initiate.md @@ -56,10 +56,27 @@ The hub is headless — `DevframeHubUi` is pure data, and whoever fills it decid interface DevframeHubUi { viewer?: { distDir: string } // a standalone SPA served at the namespace root embedded?: { entry: string } // a prebuilt bootstrap served at embedded.js + assets?: Record string | Uint8Array> // extra UI-owned files + setup?: (ctx) => void | Promise // publish static config via ctx.staticConfig } ``` -`@devframes/hub-ui`'s `createUi()` is the reference implementation: a standalone viewer plus the floating dock — one `