@devframes/hub 0.9.8 → 0.9.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.d.mts +64 -50
- package/dist/client/index.mjs +78 -42
- package/dist/{constants-D1zicoJ4.mjs → constants-BUd6oMci.mjs} +4 -4
- package/dist/{constants-DJrPlLi0.d.mts → constants-Coo1p3ja.d.mts} +5 -5
- package/dist/constants.d.mts +1 -1
- package/dist/constants.mjs +1 -1
- package/dist/{context-DYUkd3il.mjs → context-D4L966cN.mjs} +141 -120
- package/dist/{context-BJFGlcf8.d.mts → context-Di-fhMKP.d.mts} +17 -17
- package/dist/{index-OpSX0dK5.d.mts → index-faD2d5iX.d.mts} +2 -2
- package/dist/index.d.mts +3 -3
- package/dist/node/index.d.mts +28 -19
- package/dist/node/index.mjs +1 -1
- package/dist/node/initiate.d.mts +35 -30
- package/dist/node/initiate.mjs +80 -53
- package/dist/{remote-url-Bgc7gtsP.mjs → remote-url-UClKLQ5_.mjs} +34 -39
- package/dist/{settings-DwxCyeI6.d.mts → settings-CWHPzCWS.d.mts} +59 -38
- package/dist/types/index.d.mts +3 -3
- package/package.json +3 -3
package/dist/client/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { C as DevframeMessageEntryInput, O as DevframeMessagesClient } from "../context-
|
|
2
|
-
import { A as DevframeViewIframe, C as DevframeDockUserEntry, F as RemoteConnectionInfo, P as NavTarget, S as DevframeDockPanelState, _ as DevframeDockEntry, a as DevframeCommandEntry, b as DevframeDockEntryIcon, g as DevframeDockEntriesGrouped, n as DevframeClientCommand, p as ClientScriptEntry, s as DevframeCommandKeybinding, t as DevframeDocksUserSettings } from "../settings-
|
|
3
|
-
import "../index-
|
|
1
|
+
import { C as DevframeMessageEntryInput, O as DevframeMessagesClient } from "../context-Di-fhMKP.mjs";
|
|
2
|
+
import { A as DevframeViewIframe, C as DevframeDockUserEntry, F as RemoteConnectionInfo, P as NavTarget, S as DevframeDockPanelState, _ as DevframeDockEntry, a as DevframeCommandEntry, b as DevframeDockEntryIcon, g as DevframeDockEntriesGrouped, n as DevframeClientCommand, p as ClientScriptEntry, s as DevframeCommandKeybinding, t as DevframeDocksUserSettings } from "../settings-CWHPzCWS.mjs";
|
|
3
|
+
import "../index-faD2d5iX.mjs";
|
|
4
4
|
import { DevframeClientRpcHost, DevframeConnection, DevframeConnectionStatus, DevframeRpcClient, DevframeRpcClientOptions, DevframeRpcContext, RpcClientEvents, RpcClientEvents as RpcClientEvents$1 } from "devframe/client";
|
|
5
5
|
import { EventEmitter } from "devframe/types";
|
|
6
6
|
import { SharedState } from "devframe/utils/shared-state";
|
|
@@ -12,10 +12,10 @@ export * from "devframe/client";
|
|
|
12
12
|
* specifiers pass through untouched; a **bare** specifier resolves through
|
|
13
13
|
* the explicit `resolveClientModule` callback (a viewer's own policy), then
|
|
14
14
|
* the host-advertised `template` (`ConnectionMeta.configs.dock.clientModuleResolution`,
|
|
15
|
-
* its `{specifier}` token replaced
|
|
15
|
+
* its `{specifier}` token replaced, e.g. Vite's `'/@id/{specifier}'`),
|
|
16
16
|
* resolved against `metaBaseUrl` so it targets the host's origin from any
|
|
17
|
-
* viewer. With neither, it stays unchanged
|
|
18
|
-
* cover it.
|
|
17
|
+
* viewer. With neither, it stays unchanged, since a page import map may
|
|
18
|
+
* still cover it.
|
|
19
19
|
*/
|
|
20
20
|
declare function resolveClientModuleSpecifier(specifier: string, options?: {
|
|
21
21
|
resolveClientModule?: (specifier: string) => string | undefined;
|
|
@@ -55,21 +55,21 @@ interface DockRendererInstance {
|
|
|
55
55
|
dispose?: () => void;
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
|
-
* A renderer for a dock `type`. The headless hub is renderer-agnostic
|
|
58
|
+
* A renderer for a dock `type`. The headless hub is renderer-agnostic: a
|
|
59
59
|
* host registers renderers at boot (e.g. injecting `@devframes/json-render-ui`
|
|
60
60
|
* for the `'json-render'` type) or serves them as prebuilt modules through the
|
|
61
61
|
* hub's renderer manifest (`initHub({ renderers })`). The renderer owns its
|
|
62
62
|
* framework (Vue, React, …); the hub only routes a dock type to it and
|
|
63
63
|
* disposes it on deactivation.
|
|
64
64
|
*
|
|
65
|
-
* Integration packages narrow `Entry` to export a precisely-typed contract
|
|
65
|
+
* Integration packages narrow `Entry` to export a precisely-typed contract,
|
|
66
66
|
* e.g. `@devframes/json-render/hub` exports
|
|
67
67
|
* `JsonRenderDockRenderer = DockRenderer<DevframeJsonRenderDockEntry>`.
|
|
68
68
|
*/
|
|
69
69
|
type DockRenderer<Entry extends DevframeDockEntry = DevframeDockEntry> = (options: DockRendererMountOptions<Entry>) => DockRendererInstance | Promise<DockRendererInstance>;
|
|
70
70
|
/**
|
|
71
71
|
* The outcome of {@link DockRenderersContext.mount}. A missing renderer is an
|
|
72
|
-
* expected, non-exceptional state
|
|
72
|
+
* expected, non-exceptional state: a viewer renders its "no renderer for this
|
|
73
73
|
* dock type" fallback from `missing-renderer`, and its error variant (with a
|
|
74
74
|
* retry affordance) from `load-error`.
|
|
75
75
|
*/
|
|
@@ -85,8 +85,8 @@ type DockRendererMountResult = {
|
|
|
85
85
|
/**
|
|
86
86
|
* The dock-renderer registry surfaced on the client host context. A viewer
|
|
87
87
|
* calls {@link DockRenderersContext.mount} to render a dock whose `type` has a
|
|
88
|
-
* renderer
|
|
89
|
-
* hub's renderer manifest
|
|
88
|
+
* renderer (registered locally at boot, or served as a prebuilt module by the
|
|
89
|
+
* hub's renderer manifest) into a container it owns; the host tracks the
|
|
90
90
|
* instance and disposes it when the entry deactivates.
|
|
91
91
|
*/
|
|
92
92
|
interface DockRenderersContext {
|
|
@@ -94,13 +94,13 @@ interface DockRenderersContext {
|
|
|
94
94
|
* Register a renderer for a dock `type`. Returns an unregister function.
|
|
95
95
|
* Accepts a renderer narrowed to any specific entry variant (e.g. a
|
|
96
96
|
* {@link DockRenderer}<DevframeJsonRenderDockEntry> from an integration
|
|
97
|
-
* package)
|
|
97
|
+
* package); the type routes only its own entries to it.
|
|
98
98
|
*/
|
|
99
99
|
register: (type: string, renderer: DockRenderer<any>) => () => void;
|
|
100
100
|
/** Look up the locally-registered renderer for a dock `type`, if any. */
|
|
101
101
|
get: (type: string) => DockRenderer | undefined;
|
|
102
102
|
/**
|
|
103
|
-
* Whether a renderer is available for a dock `type
|
|
103
|
+
* Whether a renderer is available for a dock `type`, registered locally
|
|
104
104
|
* **or** provided by the hub's renderer manifest. A viewer checks this
|
|
105
105
|
* before mounting to render its missing-renderer fallback declaratively.
|
|
106
106
|
*/
|
|
@@ -112,14 +112,14 @@ interface DockRenderersContext {
|
|
|
112
112
|
* registered. The mounted instance is also disposed automatically when the
|
|
113
113
|
* entry deactivates. Resolves `missing-renderer` (with a `console.warn`)
|
|
114
114
|
* when neither source has the type, and `load-error` when the module import
|
|
115
|
-
* or the renderer itself fails
|
|
115
|
+
* or the renderer itself fails. A failed import is not cached, so a retry
|
|
116
116
|
* re-imports.
|
|
117
117
|
*/
|
|
118
118
|
mount: (entry: DevframeDockEntry, container: HTMLElement) => Promise<DockRendererMountResult>;
|
|
119
119
|
}
|
|
120
120
|
/**
|
|
121
121
|
* The renderer manifest published by the hub at the
|
|
122
|
-
* `devframe:dock-renderers` shared-state slot
|
|
122
|
+
* `devframe:dock-renderers` shared-state slot: one {@link ClientScriptEntry}
|
|
123
123
|
* per dock `type`, whose `importFrom` is a URL path the hub serves
|
|
124
124
|
* (`<base>__renderers/<type>.mjs`). Mirrors the dock client-script
|
|
125
125
|
* convention: the module's `importName` export (default `'default'`) is a
|
|
@@ -130,20 +130,20 @@ type DockRendererManifest = Record<string, ClientScriptEntry>;
|
|
|
130
130
|
interface CreateDockRenderersContextOptions {
|
|
131
131
|
/** The assembled client context handed to renderers at mount. */
|
|
132
132
|
context: () => DevframeClientContext;
|
|
133
|
-
/** Renderers registered locally at boot
|
|
133
|
+
/** Renderers registered locally at boot; these win over manifest modules. */
|
|
134
134
|
local?: Record<string, DockRenderer<any>>;
|
|
135
135
|
/** The current {@link DockRendererManifest} (live getter). */
|
|
136
136
|
manifest?: () => DockRendererManifest;
|
|
137
137
|
/**
|
|
138
138
|
* Called with each successful mount's disposer so the caller can tie
|
|
139
139
|
* disposal to its own lifecycle (entry deactivation, host teardown). An
|
|
140
|
-
* optional returned cleanup runs exactly once when the mount is disposed
|
|
140
|
+
* optional returned cleanup runs exactly once when the mount is disposed,
|
|
141
141
|
* whichever side (caller or viewer) triggers it first.
|
|
142
142
|
*/
|
|
143
143
|
onMounted?: (dispose: () => void, entry: DevframeDockEntry) => (() => void) | void;
|
|
144
144
|
}
|
|
145
145
|
/**
|
|
146
|
-
* Build the {@link DockRenderersContext} shared by every hub-aware client
|
|
146
|
+
* Build the {@link DockRenderersContext} shared by every hub-aware client.
|
|
147
147
|
* `createDevframeClientRuntime` and hub UI providers that assemble their own context
|
|
148
148
|
* (`@devframes/hub-ui`) both delegate here so local-first resolution, lazy
|
|
149
149
|
* manifest imports, and the typed mount result behave identically everywhere.
|
|
@@ -164,7 +164,7 @@ interface DockPanelStorage {
|
|
|
164
164
|
* Per-tab UI state of the dock panel, distinct from the browser-shared geometry
|
|
165
165
|
* in {@link DockPanelStorage}. A hub UI provider persists this to `sessionStorage` so a
|
|
166
166
|
* reload (or the RPC auth handshake that follows one) restores the panel to
|
|
167
|
-
* exactly where the developer left it
|
|
167
|
+
* exactly where the developer left it: which dock was open, and the
|
|
168
168
|
* address-bar route of that dock's iframe. Kept `sessionStorage`-scoped (not
|
|
169
169
|
* `localStorage`) because it is per-tab navigation state: two tabs open against
|
|
170
170
|
* the same dev server each keep their own selection rather than fighting over a
|
|
@@ -183,7 +183,7 @@ interface DockSessionStorage {
|
|
|
183
183
|
selectedDockRoute: string | null;
|
|
184
184
|
/**
|
|
185
185
|
* The dock entry most recently selected from beyond the float bar's inline
|
|
186
|
-
* capacity
|
|
186
|
+
* capacity, whether from the overflow popover or from inside a dock group. A hub UI
|
|
187
187
|
* provider raises this entry into a dedicated slot between the bar's visible
|
|
188
188
|
* items and the overflow button, so deselecting it (or selecting a visible
|
|
189
189
|
* neighbour) keeps it one click away instead of folding it straight back
|
|
@@ -247,7 +247,7 @@ interface DocksConnectionContext {
|
|
|
247
247
|
/** The most recent connection-level error, or `null` when healthy. */
|
|
248
248
|
readonly error: Error | null;
|
|
249
249
|
/**
|
|
250
|
-
* The client's event emitter
|
|
250
|
+
* The client's event emitter. Subscribe to `connection:status`,
|
|
251
251
|
* `connection:error`, and `rpc:error` to react to changes.
|
|
252
252
|
*/
|
|
253
253
|
readonly events: EventEmitter<RpcClientEvents$1>;
|
|
@@ -270,7 +270,7 @@ interface DocksPanelContext {
|
|
|
270
270
|
readonly events: EventEmitter<DocksPanelEvents>;
|
|
271
271
|
store: DockPanelStorage;
|
|
272
272
|
/**
|
|
273
|
-
* Per-tab session UI state
|
|
273
|
+
* Per-tab session UI state: whether the panel is open, which dock is
|
|
274
274
|
* selected, and that dock's iframe route. Restored across reloads (and the
|
|
275
275
|
* auth handshake that follows one) by a hub UI provider that persists it to
|
|
276
276
|
* `sessionStorage`.
|
|
@@ -282,7 +282,7 @@ interface DocksPanelContext {
|
|
|
282
282
|
/**
|
|
283
283
|
* Claim the persisted {@link DockSessionStorage.selectedDockRoute boot route} for a dock
|
|
284
284
|
* id, once. Returns the saved address-bar URL only for the dock that was
|
|
285
|
-
* selected before the last reload
|
|
285
|
+
* selected before the last reload (and only on the first call for it), so a
|
|
286
286
|
* restored iframe boots deep-linked while a later switch to a different dock
|
|
287
287
|
* (whose live route isn't reflected in {@link DockSessionStorage.selectedDockRoute} yet)
|
|
288
288
|
* doesn't reuse the stale value. Returns `null` otherwise.
|
|
@@ -297,11 +297,11 @@ interface DocksEntriesContext {
|
|
|
297
297
|
entryToStateMap: Map<string, DockEntryState>;
|
|
298
298
|
groupedEntries: DevframeDockEntriesGrouped;
|
|
299
299
|
/**
|
|
300
|
-
* The resolved top-level category ordering
|
|
300
|
+
* The resolved top-level category ordering: `DEFAULT_CATEGORIES_ORDER`,
|
|
301
301
|
* overridden by every installed devframe's own `dock.categoryOrder`
|
|
302
302
|
* (`ConnectionMeta.configs.dock.categoryOrder`), overridden again by the
|
|
303
303
|
* host page's own `createDevframeClientRuntime({ categoryOrder })`. Fixed
|
|
304
|
-
* for the life of the session
|
|
304
|
+
* for the life of the session, resolved once at boot.
|
|
305
305
|
*/
|
|
306
306
|
readonly categoryOrder: Record<string, number>;
|
|
307
307
|
settings: SharedState<DevframeDocksUserSettings>;
|
|
@@ -327,7 +327,7 @@ interface DocksEntriesContext {
|
|
|
327
327
|
* {@link entries}. Unlike a dock registered on the node
|
|
328
328
|
* {@link import('../types/docks').DevframeDocksHost}, it never flows into
|
|
329
329
|
* shared state, so it stays local to this client instead of syncing to the
|
|
330
|
-
* hub or other hub UI providers
|
|
330
|
+
* hub or other hub UI providers, suiting a view the client runtime synthesizes itself.
|
|
331
331
|
*
|
|
332
332
|
* Throws when `id` already names a client dock, unless `force` is set. A
|
|
333
333
|
* client dock sharing an id with a server dock overrides it in the local
|
|
@@ -343,7 +343,7 @@ interface DocksEntriesContext {
|
|
|
343
343
|
}
|
|
344
344
|
interface DockRegistration<T extends DevframeDockEntry = DevframeDockEntry> {
|
|
345
345
|
/**
|
|
346
|
-
* Patch the registered client dock in place. The `id` is immutable
|
|
346
|
+
* Patch the registered client dock in place. The `id` is immutable, so passing
|
|
347
347
|
* a differing `id` throws.
|
|
348
348
|
*/
|
|
349
349
|
update: (patch: Partial<T>) => void;
|
|
@@ -395,6 +395,20 @@ interface CommandsContext {
|
|
|
395
395
|
* Whether the command palette is open
|
|
396
396
|
*/
|
|
397
397
|
paletteOpen: boolean;
|
|
398
|
+
/**
|
|
399
|
+
* The command whose children the palette should open drilled into, or `null`
|
|
400
|
+
* for the root list. Set by {@link CommandsContext.openPalette}; the palette
|
|
401
|
+
* reads it when it opens and clears it when it closes.
|
|
402
|
+
*/
|
|
403
|
+
paletteScopeId: string | null;
|
|
404
|
+
/**
|
|
405
|
+
* Open the command palette, optionally scoped to one command's children:
|
|
406
|
+
* e.g. a dock group with no `defaultChildId` opens the palette listing just
|
|
407
|
+
* that group's members instead of guessing one for the user.
|
|
408
|
+
*
|
|
409
|
+
* Passing no id opens the root list, the same as setting `paletteOpen` true.
|
|
410
|
+
*/
|
|
411
|
+
openPalette: (atCommandId?: string) => void;
|
|
398
412
|
}
|
|
399
413
|
//#endregion
|
|
400
414
|
//#region src/client/client-script.d.ts
|
|
@@ -407,7 +421,7 @@ interface DockClientScriptContext extends DocksContext {
|
|
|
407
421
|
*/
|
|
408
422
|
current: DockEntryState;
|
|
409
423
|
/**
|
|
410
|
-
* Messages client scoped to this dock entry
|
|
424
|
+
* Messages client scoped to this dock entry. Messages it adds default
|
|
411
425
|
* their `category` to the entry id, so the feed can attribute them.
|
|
412
426
|
*/
|
|
413
427
|
messages: DevframeMessagesClient;
|
|
@@ -440,8 +454,8 @@ declare function resolveDockIcon(icon: DevframeDockEntryIcon, connection: Devfra
|
|
|
440
454
|
/**
|
|
441
455
|
* Live location tracking for an iframe dock.
|
|
442
456
|
*
|
|
443
|
-
* An iframe dock's address bar
|
|
444
|
-
* `DockSessionStorage.selectedDockRoute`
|
|
457
|
+
* An iframe dock's address bar (and the route persisted from it into
|
|
458
|
+
* `DockSessionStorage.selectedDockRoute`) has to follow wherever the embedded
|
|
445
459
|
* app actually goes. The `load` event only covers whole-document navigations, so
|
|
446
460
|
* an SPA router moving between routes with `history.pushState()` leaves both
|
|
447
461
|
* showing the URL the frame booted with.
|
|
@@ -449,21 +463,21 @@ declare function resolveDockIcon(icon: DevframeDockEntryIcon, connection: Devfra
|
|
|
449
463
|
* {@link watchFrameLocation} closes that gap for a same-origin frame, reporting
|
|
450
464
|
* `location.href` on every navigation it can observe:
|
|
451
465
|
*
|
|
452
|
-
* - `popstate` / `hashchange
|
|
466
|
+
* - `popstate` / `hashchange`: back/forward and hash routing;
|
|
453
467
|
* - `history.pushState`/`replaceState`, wrapped in place (and restored on
|
|
454
468
|
* dispose) because those two fire no event of their own;
|
|
455
469
|
* - the [Navigation API](https://developer.mozilla.org/en-US/docs/Web/API/Navigation_API)'s
|
|
456
470
|
* `currententrychange` where it exists, which reports post-commit and so also
|
|
457
|
-
* catches what a wrapper structurally cannot
|
|
471
|
+
* catches what a wrapper structurally cannot: a router holding a reference to
|
|
458
472
|
* `pushState` captured before this watch attached;
|
|
459
473
|
* - `load`, which also re-subscribes: a document navigation swaps the frame's
|
|
460
474
|
* `history`/`navigation` objects, so the previous subscription dies with them.
|
|
461
475
|
*
|
|
462
476
|
* These sources overlap deliberately rather than being chosen between, since a
|
|
463
|
-
* report is deduped against the last href
|
|
477
|
+
* report is deduped against the last href, so the cost of hearing the same
|
|
464
478
|
* navigation twice is nothing, and the cost of missing one is a stale route.
|
|
465
479
|
*
|
|
466
|
-
* A cross-origin frame reports nothing
|
|
480
|
+
* A cross-origin frame reports nothing, because its location is unreadable by design,
|
|
467
481
|
* so the caller keeps the last URL it knew.
|
|
468
482
|
*/
|
|
469
483
|
/** The minimal `history` surface the watcher wraps. Injectable for tests. */
|
|
@@ -511,7 +525,7 @@ declare function watchFrameLocation(options: WatchFrameLocationOptions): () => v
|
|
|
511
525
|
//#endregion
|
|
512
526
|
//#region src/client/frame-nav.d.ts
|
|
513
527
|
/**
|
|
514
|
-
* Shared-iframe soft navigation
|
|
528
|
+
* Shared-iframe soft navigation: the hub-UI-provider-side half of a host-page↔iframe
|
|
515
529
|
* `postMessage` protocol.
|
|
516
530
|
*
|
|
517
531
|
* An {@link DevframeViewIframe.subTabs anchor} iframe dock owns one live iframe
|
|
@@ -527,7 +541,7 @@ declare function watchFrameLocation(options: WatchFrameLocationOptions): () => v
|
|
|
527
541
|
* highlight to match.
|
|
528
542
|
*
|
|
529
543
|
* The protocol is server-free (works cross-origin and in static builds) and
|
|
530
|
-
* decoupled
|
|
544
|
+
* decoupled, so the embedded app takes no hub/RPC dependency, only the shim.
|
|
531
545
|
*/
|
|
532
546
|
/** `postMessage` channel tag shared by both halves of the protocol. */
|
|
533
547
|
declare const FRAME_NAV_CHANNEL: string;
|
|
@@ -600,11 +614,11 @@ interface FrameNavListenTarget {
|
|
|
600
614
|
interface FrameNavClientOptions {
|
|
601
615
|
/** The shared frame id (defaults to the anchor's, else the anchor id). */
|
|
602
616
|
frameId: string;
|
|
603
|
-
/** The anchor iframe dock entry
|
|
617
|
+
/** The anchor iframe dock entry, supplying url/icon/groupId defaults. */
|
|
604
618
|
anchor: DevframeViewIframe;
|
|
605
619
|
/** The live iframe element hosting the embedded app. */
|
|
606
620
|
iframe: Pick<HTMLIFrameElement, 'contentWindow' | 'src'>;
|
|
607
|
-
/** Client docks context
|
|
621
|
+
/** Client docks context, used to register members and drive selection. */
|
|
608
622
|
docks: Pick<DocksEntriesContext, 'register' | 'switchEntry' | 'getStateById'>;
|
|
609
623
|
/**
|
|
610
624
|
* Window to receive the frame's `message` events on (the host page window).
|
|
@@ -634,7 +648,7 @@ declare function attachFrameNavClient(options: FrameNavClientOptions): FrameNavC
|
|
|
634
648
|
interface DevframeClientRuntimeOptions {
|
|
635
649
|
/**
|
|
636
650
|
* An already-connected RPC client. When omitted, one is created via
|
|
637
|
-
* `connectDevframe(connect)
|
|
651
|
+
* `connectDevframe(connect)`, so pass `connect.baseURL` to point at the hub's
|
|
638
652
|
* connection-meta mount (e.g. `/__hub/`).
|
|
639
653
|
*/
|
|
640
654
|
rpc?: DevframeRpcClient;
|
|
@@ -642,8 +656,8 @@ interface DevframeClientRuntimeOptions {
|
|
|
642
656
|
connect?: DevframeRpcClientOptions;
|
|
643
657
|
/**
|
|
644
658
|
* Environment the host runs in.
|
|
645
|
-
* - `'standalone'` (default)
|
|
646
|
-
* - `'embedded'
|
|
659
|
+
* - `'standalone'` (default): the runtime owns the whole page (a hub UI).
|
|
660
|
+
* - `'embedded'`: the runtime lives inside a user app alongside a panel.
|
|
647
661
|
*/
|
|
648
662
|
clientType?: DockClientType;
|
|
649
663
|
/**
|
|
@@ -653,7 +667,7 @@ interface DevframeClientRuntimeOptions {
|
|
|
653
667
|
loadClientScripts?: boolean;
|
|
654
668
|
/**
|
|
655
669
|
* Resolve a **bare-specifier** client script (`importFrom` naming an npm
|
|
656
|
-
* module) to a URL this page can import
|
|
670
|
+
* module) to a URL this page can import, a hub UI provider's own policy that wins
|
|
657
671
|
* over the host-advertised `ConnectionMeta.configs.dock.clientModuleResolution`
|
|
658
672
|
* template (return `undefined` to fall through to it). URL specifiers never
|
|
659
673
|
* reach this hook. E.g. on a Vite-served page:
|
|
@@ -667,11 +681,11 @@ interface DevframeClientRuntimeOptions {
|
|
|
667
681
|
* `@devframes/json-render-ui`). The hub ships none by default.
|
|
668
682
|
*
|
|
669
683
|
* Local registrations take precedence over the hub's renderer manifest
|
|
670
|
-
* (`initHub({ renderers })`)
|
|
684
|
+
* (`initHub({ renderers })`), since explicit local code beats wire config.
|
|
671
685
|
*/
|
|
672
686
|
renderers?: Record<string, DockRenderer<any>>;
|
|
673
687
|
/**
|
|
674
|
-
* Hub-wide override of the top-level dock-bar category ordering
|
|
688
|
+
* Hub-wide override of the top-level dock-bar category ordering: a map of
|
|
675
689
|
* category id → ordering weight (lower sorts earlier), mirroring the shape
|
|
676
690
|
* of {@link import('../constants').DEFAULT_CATEGORIES_ORDER}.
|
|
677
691
|
*
|
|
@@ -703,24 +717,24 @@ interface DevframeClientRuntime {
|
|
|
703
717
|
* Boot the client runtime: connect RPC, assemble the full
|
|
704
718
|
* {@link DevframeClientContext} (panel, docks, commands, when) from the hub's
|
|
705
719
|
* shared state, publish it at `__DEVFRAME_HUB_CLIENT_CONTEXT__`, and load every
|
|
706
|
-
* dock entry's client script into this page
|
|
720
|
+
* dock entry's client script into this page, the devframe equivalent of the
|
|
707
721
|
* runtime `@vitejs/devtools-kit` injects into a host app.
|
|
708
722
|
*
|
|
709
723
|
* A hub UI provider keeps rendering its own dock UI (reading the same shared state); this
|
|
710
724
|
* runtime is what gives plugin client scripts a live host context to run in.
|
|
711
725
|
*/
|
|
712
726
|
declare function createDevframeClientRuntime(options?: DevframeClientRuntimeOptions): Promise<DevframeClientRuntime>;
|
|
713
|
-
/** @deprecated Renamed
|
|
727
|
+
/** @deprecated Renamed; use {@link DevframeClientRuntimeOptions}. */
|
|
714
728
|
type DevframeClientHostOptions = DevframeClientRuntimeOptions;
|
|
715
|
-
/** @deprecated Renamed
|
|
729
|
+
/** @deprecated Renamed; use {@link DevframeClientRuntime}. */
|
|
716
730
|
type DevframeClientHost = DevframeClientRuntime;
|
|
717
|
-
/** @deprecated Renamed
|
|
731
|
+
/** @deprecated Renamed; use {@link createDevframeClientRuntime}. */
|
|
718
732
|
declare const createDevframeClientHost: typeof createDevframeClientRuntime;
|
|
719
733
|
//#endregion
|
|
720
734
|
//#region src/client/messages.d.ts
|
|
721
735
|
interface MessagesClientOptions {
|
|
722
736
|
/**
|
|
723
|
-
* Default fields merged beneath every `add()` input
|
|
737
|
+
* Default fields merged beneath every `add()` input. The client host passes
|
|
724
738
|
* `{ category: entry.id }` to scope a dock client script's messages to its
|
|
725
739
|
* entry. Fields set on the input itself win.
|
|
726
740
|
*/
|
|
@@ -759,7 +773,7 @@ declare function parseRemoteConnection(input?: string): RemoteConnectionInfo | n
|
|
|
759
773
|
* the current URL and returns a connected {@link DevframeRpcClient}.
|
|
760
774
|
*
|
|
761
775
|
* Pairs with `remote: true` on a `DevframeViewIframe` registered on the node
|
|
762
|
-
* side
|
|
776
|
+
* side, where the hub injects the descriptor into the iframe URL.
|
|
763
777
|
*
|
|
764
778
|
* @throws if no descriptor is present in the URL.
|
|
765
779
|
*/
|
package/dist/client/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as DOCK_RENDERERS_STATE_KEY, n as DEFAULT_STATE_USER_SETTINGS, o as HUB_EVENTS, t as DEFAULT_CATEGORIES_ORDER } from "../constants-
|
|
2
|
-
import { a as resolveClientModuleSpecifier, n as stripRemoteConnectionFromUrl, r as clientScriptFailureHint, t as buildRemoteConnectionUrl } from "../remote-url-
|
|
1
|
+
import { i as DOCK_RENDERERS_STATE_KEY, n as DEFAULT_STATE_USER_SETTINGS, o as HUB_EVENTS, t as DEFAULT_CATEGORIES_ORDER } from "../constants-BUd6oMci.mjs";
|
|
2
|
+
import { a as resolveClientModuleSpecifier, n as stripRemoteConnectionFromUrl, r as clientScriptFailureHint, t as buildRemoteConnectionUrl } from "../remote-url-UClKLQ5_.mjs";
|
|
3
3
|
import { REMOTE_CONNECTION_KEY } from "devframe/constants";
|
|
4
4
|
import { connectDevframe, getDevframeRpcClient, resolveWsUrl } from "devframe/client";
|
|
5
5
|
import { createEventEmitter } from "devframe/utils/events";
|
|
@@ -82,7 +82,7 @@ function watchFrameLocation(options) {
|
|
|
82
82
|
let disposed = false;
|
|
83
83
|
/**
|
|
84
84
|
* The frame's window, or `null` when its location can't be read. Touching
|
|
85
|
-
* `location.href` is the cross-origin probe
|
|
85
|
+
* `location.href` is the cross-origin probe, since it throws for a foreign
|
|
86
86
|
* document, where there is nothing to observe.
|
|
87
87
|
*/
|
|
88
88
|
function readableWindow() {
|
|
@@ -153,7 +153,7 @@ function watchFrameLocation(options) {
|
|
|
153
153
|
//#endregion
|
|
154
154
|
//#region src/client/frame-nav.ts
|
|
155
155
|
/**
|
|
156
|
-
* Shared-iframe soft navigation
|
|
156
|
+
* Shared-iframe soft navigation: the hub-UI-provider-side half of a host-page↔iframe
|
|
157
157
|
* `postMessage` protocol.
|
|
158
158
|
*
|
|
159
159
|
* An {@link DevframeViewIframe.subTabs anchor} iframe dock owns one live iframe
|
|
@@ -169,7 +169,7 @@ function watchFrameLocation(options) {
|
|
|
169
169
|
* highlight to match.
|
|
170
170
|
*
|
|
171
171
|
* The protocol is server-free (works cross-origin and in static builds) and
|
|
172
|
-
* decoupled
|
|
172
|
+
* decoupled, so the embedded app takes no hub/RPC dependency, only the shim.
|
|
173
173
|
*/
|
|
174
174
|
/** `postMessage` channel tag shared by both halves of the protocol. */
|
|
175
175
|
const FRAME_NAV_CHANNEL = HUB_EVENTS.postMessage.frameNav;
|
|
@@ -341,17 +341,18 @@ function deriveFallbackUrl(base, navTarget) {
|
|
|
341
341
|
}
|
|
342
342
|
const path = navTarget.path.startsWith("#") ? navTarget.path.slice(1) : navTarget.path;
|
|
343
343
|
let hash = `#${path}`;
|
|
344
|
-
const
|
|
345
|
-
if (
|
|
346
|
-
const params = new URLSearchParams();
|
|
347
|
-
for (const [key, value] of Object.entries(query)) if (typeof value === "string") params.set(key, value);
|
|
348
|
-
else for (const v of value) params.append(key, v);
|
|
349
|
-
const qs = params.toString();
|
|
350
|
-
if (qs) hash += (path.includes("?") ? "&" : "?") + qs;
|
|
351
|
-
}
|
|
344
|
+
const qs = navTarget.query ? serializeNavQuery(navTarget.query) : "";
|
|
345
|
+
if (qs) hash += (path.includes("?") ? "&" : "?") + qs;
|
|
352
346
|
url.hash = hash;
|
|
353
347
|
return url.toString();
|
|
354
348
|
}
|
|
349
|
+
/** Serialize a {@link NavTarget.query} into a URL query string. */
|
|
350
|
+
function serializeNavQuery(query) {
|
|
351
|
+
const params = new URLSearchParams();
|
|
352
|
+
for (const [key, value] of Object.entries(query)) if (typeof value === "string") params.set(key, value);
|
|
353
|
+
else for (const v of value) params.append(key, v);
|
|
354
|
+
return params.toString();
|
|
355
|
+
}
|
|
355
356
|
//#endregion
|
|
356
357
|
//#region src/client/messages.ts
|
|
357
358
|
/**
|
|
@@ -406,7 +407,7 @@ function createMessagesClient(rpc, options = {}) {
|
|
|
406
407
|
//#endregion
|
|
407
408
|
//#region src/client/renderers.ts
|
|
408
409
|
/**
|
|
409
|
-
* Build the {@link DockRenderersContext} shared by every hub-aware client
|
|
410
|
+
* Build the {@link DockRenderersContext} shared by every hub-aware client.
|
|
410
411
|
* `createDevframeClientRuntime` and hub UI providers that assemble their own context
|
|
411
412
|
* (`@devframes/hub-ui`) both delegate here so local-first resolution, lazy
|
|
412
413
|
* manifest imports, and the typed mount result behave identically everywhere.
|
|
@@ -504,7 +505,7 @@ const DOCKS_ACTIVATE_EVENT = HUB_EVENTS.broadcast.docksActivate;
|
|
|
504
505
|
* Boot the client runtime: connect RPC, assemble the full
|
|
505
506
|
* {@link DevframeClientContext} (panel, docks, commands, when) from the hub's
|
|
506
507
|
* shared state, publish it at `__DEVFRAME_HUB_CLIENT_CONTEXT__`, and load every
|
|
507
|
-
* dock entry's client script into this page
|
|
508
|
+
* dock entry's client script into this page, the devframe equivalent of the
|
|
508
509
|
* runtime `@vitejs/devtools-kit` injects into a host app.
|
|
509
510
|
*
|
|
510
511
|
* A hub UI provider keeps rendering its own dock UI (reading the same shared state); this
|
|
@@ -575,7 +576,7 @@ async function createDevframeClientRuntime(options = {}) {
|
|
|
575
576
|
type: "action",
|
|
576
577
|
handler: (activation) => activateHandler(activation)
|
|
577
578
|
});
|
|
578
|
-
if (getDevframeClientContext()) console.warn("[@devframes/hub] A client context is already published on this page
|
|
579
|
+
if (getDevframeClientContext()) console.warn("[@devframes/hub] A client context is already published on this page; replacing it. Boot createDevframeClientRuntime() once per page (e.g. HTML injection combined with a manual import boots it twice).");
|
|
579
580
|
setDevframeClientContext(context);
|
|
580
581
|
const loadedScripts = /* @__PURE__ */ new Set();
|
|
581
582
|
if (loadScriptsEnabled) {
|
|
@@ -678,6 +679,10 @@ async function createDevframeClientRuntime(options = {}) {
|
|
|
678
679
|
set selectedId(id) {
|
|
679
680
|
switchEntry(id);
|
|
680
681
|
},
|
|
682
|
+
/**
|
|
683
|
+
* A mirror of the session field, so a persisting host reads and writes the
|
|
684
|
+
* selected iframe's route through the same context every hub UI provider uses.
|
|
685
|
+
*/
|
|
681
686
|
get selectedRoute() {
|
|
682
687
|
return panel.session.selectedDockRoute;
|
|
683
688
|
},
|
|
@@ -696,14 +701,14 @@ async function createDevframeClientRuntime(options = {}) {
|
|
|
696
701
|
switchEntry,
|
|
697
702
|
toggleEntry: (id) => selectedId === id ? switchEntry(null) : switchEntry(id),
|
|
698
703
|
register(entry, force) {
|
|
699
|
-
if (clientDocks.has(entry.id) && !force) throw new Error(`[@devframes/hub] a client dock "${entry.id}" is already registered
|
|
704
|
+
if (clientDocks.has(entry.id) && !force) throw new Error(`[@devframes/hub] a client dock "${entry.id}" is already registered; pass force to overwrite`);
|
|
700
705
|
clientDocks.set(entry.id, entry);
|
|
701
706
|
refreshEntries();
|
|
702
707
|
return {
|
|
703
708
|
update: (patch) => {
|
|
704
709
|
if (patch.id && patch.id !== entry.id) throw new Error(`[@devframes/hub] cannot change a dock id ("${entry.id}" → "${patch.id}")`);
|
|
705
710
|
const existing = clientDocks.get(entry.id);
|
|
706
|
-
if (!existing) throw new Error(`[@devframes/hub] client dock "${entry.id}" was removed
|
|
711
|
+
if (!existing) throw new Error(`[@devframes/hub] client dock "${entry.id}" was removed; register it again to update`);
|
|
707
712
|
clientDocks.set(entry.id, {
|
|
708
713
|
...existing,
|
|
709
714
|
...patch
|
|
@@ -716,7 +721,7 @@ async function createDevframeClientRuntime(options = {}) {
|
|
|
716
721
|
};
|
|
717
722
|
},
|
|
718
723
|
update(entry) {
|
|
719
|
-
if (!clientDocks.has(entry.id)) throw new Error(`[@devframes/hub] no client dock "${entry.id}" to update
|
|
724
|
+
if (!clientDocks.has(entry.id)) throw new Error(`[@devframes/hub] no client dock "${entry.id}" to update; register it first`);
|
|
720
725
|
clientDocks.set(entry.id, entry);
|
|
721
726
|
refreshEntries();
|
|
722
727
|
}
|
|
@@ -739,7 +744,14 @@ async function createDevframeClientRuntime(options = {}) {
|
|
|
739
744
|
function allCommands() {
|
|
740
745
|
return [...commandsState.value(), ...clientCommands.values()];
|
|
741
746
|
}
|
|
742
|
-
|
|
747
|
+
function findCommand(commands, id) {
|
|
748
|
+
for (const command of commands) {
|
|
749
|
+
if (command.id === id) return command;
|
|
750
|
+
const child = findCommand(command.children ?? [], id);
|
|
751
|
+
if (child) return child;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
const ctx = {
|
|
743
755
|
get commands() {
|
|
744
756
|
return allCommands();
|
|
745
757
|
},
|
|
@@ -754,18 +766,24 @@ async function createDevframeClientRuntime(options = {}) {
|
|
|
754
766
|
};
|
|
755
767
|
},
|
|
756
768
|
async execute(id, ...args) {
|
|
757
|
-
const client = clientCommands.
|
|
758
|
-
if (client?.action) return client.action(...args);
|
|
769
|
+
const client = findCommand(clientCommands.values(), id);
|
|
770
|
+
if (client?.source === "client" && client.action) return client.action(...args);
|
|
759
771
|
return rpc.call("hub:commands:execute", id, ...args);
|
|
760
772
|
},
|
|
761
773
|
getKeybindings(id) {
|
|
762
774
|
const override = settings.value().commandShortcuts?.[id];
|
|
763
775
|
if (override) return override;
|
|
764
|
-
return allCommands()
|
|
776
|
+
return findCommand(allCommands(), id)?.keybindings ?? [];
|
|
765
777
|
},
|
|
766
778
|
settings,
|
|
767
|
-
paletteOpen: false
|
|
779
|
+
paletteOpen: false,
|
|
780
|
+
paletteScopeId: null,
|
|
781
|
+
openPalette(atCommandId) {
|
|
782
|
+
ctx.paletteScopeId = atCommandId ?? null;
|
|
783
|
+
ctx.paletteOpen = true;
|
|
784
|
+
}
|
|
768
785
|
};
|
|
786
|
+
return ctx;
|
|
769
787
|
}
|
|
770
788
|
function createRenderersContext() {
|
|
771
789
|
const mountedDisposers = /* @__PURE__ */ new Set();
|
|
@@ -902,7 +920,7 @@ function groupByCategory(entries, categoryOrder) {
|
|
|
902
920
|
}
|
|
903
921
|
return [...groups.entries()].sort(([a], [b]) => (categoryOrder[a] ?? 0) - (categoryOrder[b] ?? 0));
|
|
904
922
|
}
|
|
905
|
-
/** @deprecated Renamed
|
|
923
|
+
/** @deprecated Renamed; use {@link createDevframeClientRuntime}. */
|
|
906
924
|
const createDevframeClientHost = createDevframeClientRuntime;
|
|
907
925
|
//#endregion
|
|
908
926
|
//#region src/client/remote.ts
|
|
@@ -962,22 +980,9 @@ function extractKeyFromQuery(search) {
|
|
|
962
980
|
* Throws if the descriptor is malformed or its schema version is unsupported.
|
|
963
981
|
*/
|
|
964
982
|
function parseRemoteConnection(input) {
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
if (typeof location === "undefined") return null;
|
|
969
|
-
hash = location.hash;
|
|
970
|
-
search = location.search;
|
|
971
|
-
} else try {
|
|
972
|
-
const parsed = new URL(input, "http://_");
|
|
973
|
-
hash = parsed.hash;
|
|
974
|
-
search = parsed.search;
|
|
975
|
-
} catch {
|
|
976
|
-
if (input.startsWith("#")) hash = input;
|
|
977
|
-
else if (input.startsWith("?")) search = input;
|
|
978
|
-
else return null;
|
|
979
|
-
}
|
|
980
|
-
const encoded = extractKeyFromFragment(hash) ?? extractKeyFromQuery(search);
|
|
983
|
+
const parts = resolveUrlParts(input);
|
|
984
|
+
if (!parts) return null;
|
|
985
|
+
const encoded = extractKeyFromFragment(parts.hash) ?? extractKeyFromQuery(parts.search);
|
|
981
986
|
if (!encoded) return null;
|
|
982
987
|
let payload;
|
|
983
988
|
try {
|
|
@@ -985,6 +990,37 @@ function parseRemoteConnection(input) {
|
|
|
985
990
|
} catch (cause) {
|
|
986
991
|
throw new Error("[@devframes/hub] Failed to decode remote connection descriptor.", { cause });
|
|
987
992
|
}
|
|
993
|
+
return validateConnectionDescriptor(payload);
|
|
994
|
+
}
|
|
995
|
+
/** Resolve the URL fragment/query to inspect, from an explicit input or `location`. */
|
|
996
|
+
function resolveUrlParts(input) {
|
|
997
|
+
if (input === void 0) {
|
|
998
|
+
if (typeof location === "undefined") return null;
|
|
999
|
+
return {
|
|
1000
|
+
hash: location.hash,
|
|
1001
|
+
search: location.search
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
try {
|
|
1005
|
+
const parsed = new URL(input, "http://_");
|
|
1006
|
+
return {
|
|
1007
|
+
hash: parsed.hash,
|
|
1008
|
+
search: parsed.search
|
|
1009
|
+
};
|
|
1010
|
+
} catch {
|
|
1011
|
+
if (input.startsWith("#")) return {
|
|
1012
|
+
hash: input,
|
|
1013
|
+
search: ""
|
|
1014
|
+
};
|
|
1015
|
+
if (input.startsWith("?")) return {
|
|
1016
|
+
hash: "",
|
|
1017
|
+
search: input
|
|
1018
|
+
};
|
|
1019
|
+
return null;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
/** Validate a decoded payload as a {@link RemoteConnectionInfo}, throwing on any mismatch. */
|
|
1023
|
+
function validateConnectionDescriptor(payload) {
|
|
988
1024
|
if (!payload || typeof payload !== "object") throw new Error("[@devframes/hub] Remote connection descriptor must be an object.");
|
|
989
1025
|
const info = payload;
|
|
990
1026
|
if (info.v !== 1) throw new Error(`[@devframes/hub] Unsupported remote connection descriptor version: ${String(info.v)}`);
|
|
@@ -998,7 +1034,7 @@ function parseRemoteConnection(input) {
|
|
|
998
1034
|
* the current URL and returns a connected {@link DevframeRpcClient}.
|
|
999
1035
|
*
|
|
1000
1036
|
* Pairs with `remote: true` on a `DevframeViewIframe` registered on the node
|
|
1001
|
-
* side
|
|
1037
|
+
* side, where the hub injects the descriptor into the iframe URL.
|
|
1002
1038
|
*
|
|
1003
1039
|
* @throws if no descriptor is present in the URL.
|
|
1004
1040
|
*/
|
|
@@ -2,7 +2,7 @@ import { cleanDoubleSlashes, withLeadingSlash, withTrailingSlash } from "ufo";
|
|
|
2
2
|
//#region src/events.ts
|
|
3
3
|
/**
|
|
4
4
|
* Centralized registry of every event, broadcast, RPC method, shared-state
|
|
5
|
-
* key, and channel name the hub uses
|
|
5
|
+
* key, and channel name the hub uses: the single source of truth that keeps
|
|
6
6
|
* these names out of scattered string literals.
|
|
7
7
|
*
|
|
8
8
|
* **Keep this in sync with [`docs/content/8.references/3.events.md`](../../../docs/content/8.references/3.events.md)**
|
|
@@ -69,7 +69,7 @@ const HUB_EVENTS = {
|
|
|
69
69
|
};
|
|
70
70
|
//#endregion
|
|
71
71
|
//#region src/constants.ts
|
|
72
|
-
/** Default mount base for a hub instance
|
|
72
|
+
/** Default mount base for a hub instance: one namespace, one catch-all. */
|
|
73
73
|
const DEVFRAMES_HUB_BASE = "/__devframes/";
|
|
74
74
|
/**
|
|
75
75
|
* Normalize a hub mount base to an absolute path with leading and trailing
|
|
@@ -82,7 +82,7 @@ function normalizeHubBase(base) {
|
|
|
82
82
|
return cleanDoubleSlashes(withTrailingSlash(withLeadingSlash(base)));
|
|
83
83
|
}
|
|
84
84
|
/**
|
|
85
|
-
* The default ordering weight for each known dock category
|
|
85
|
+
* The default ordering weight for each known dock category, where lower sorts
|
|
86
86
|
* earlier. Downstream viewers (e.g. `@vitejs/devtools-kit`) import this as the
|
|
87
87
|
* single source of truth so the hub and its viewers agree on category order.
|
|
88
88
|
* `framework` sorts first; `~builtin` (the viewer's own built-in views) last.
|
|
@@ -104,7 +104,7 @@ const DEFAULT_CATEGORIES_ORDER = {
|
|
|
104
104
|
"~builtin": 1e3
|
|
105
105
|
};
|
|
106
106
|
/**
|
|
107
|
-
* Shared-state slot carrying the hub's renderer manifest
|
|
107
|
+
* Shared-state slot carrying the hub's renderer manifest: one
|
|
108
108
|
* {@link import('./client/renderers').DockRendererManifest} entry per dock
|
|
109
109
|
* `type`, published by `initHub({ renderers })` and consumed by every
|
|
110
110
|
* hub-aware client (the headless client host and viewers alike).
|