Skip to content

Commit 5677ecd

Browse files
committed
feat(json-render): support native RPC contexts in browser surfaces
Tracked in dvcol/devkit-extension#12. Preserve existing renderer context defaults, expose the prebuilt browser renderer and portable view publication, and clean up failed mounts. Scoped lint, type checks, existing renderer tests and live extension proof pass.
1 parent fe0fb62 commit 5677ecd

16 files changed

Lines changed: 115 additions & 25 deletions

File tree

‎alias.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export const alias = {
7878
'@devframes/vite': r('vite/src/index.ts'),
7979
'@devframes/json-render/core': r('json-render/src/core.ts'),
8080
'@devframes/json-render/hub': r('json-render/src/hub.ts'),
81+
'@devframes/json-render/view': r('json-render/src/view.ts'),
8182
'@devframes/json-render/node': r('json-render/src/node/index.ts'),
8283
'@devframes/json-render': r('json-render/src/index.ts'),
8384
'@devframes/json-render-ui/hub': r('json-render-ui/src/hub.ts'),

‎docs/content/1.guide/21.build-your-own-json-render-frontend.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Resolve the entry's `view`:
2929
it as the live spec, re-render on `'updated'`. **Unsubscribe in `dispose`.**
3030
- `{ spec }`: render the embedded spec directly.
3131

32-
Detect static output via `context.rpc.connectionMeta.backend === 'static'`,
32+
Detect static output via `context.rpc.connectionMeta?.backend === 'static'`,
3333
disabling action dispatch there.
3434

3535
## Behavior expectations

‎docs/content/1.guide/8.json-render.md‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,24 @@ for a browser-synthesized [client-only dock](/guide/client-context#client-only-d
167167
reference implementation.
168168
See [Build your own JSON-Render frontend](/guide/build-your-own-json-render-frontend)
169169
and the [`json-render` example](https://github.com/devframes/devframe/tree/main/examples/json-render).
170+
171+
## Rendering with a custom RPC channel
172+
173+
A host page can import the reference browser bundle directly. It includes its renderer and styles:
174+
175+
```ts
176+
import renderer from '@devframes/json-render-ui/renderer'
177+
178+
const mounted = await renderer({
179+
entry,
180+
container,
181+
context: { rpc: { call, sharedState } },
182+
})
183+
184+
// When the surface closes:
185+
mounted.dispose?.()
186+
```
187+
188+
`call` and `sharedState` are native RPC members. An optional `connectionMeta.backend` marks static output. The reference implementation accepts this smaller `JsonRenderRpcContext`; a full hub client context also satisfies it. Custom `JsonRenderDockRenderer` implementations retain the full client context by default and can declare their own context type through its generic parameter.
189+
190+
For view publication in a worker, import `createJsonRenderView` from `@devframes/json-render/view` and pass `{ rpc: { sharedState } }`. Its native publishing state must support `get(key, { sharedState })`. The node-side import and scoped node contexts continue to use the same implementation.

‎packages/json-render-ui/package.json‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
"exports": {
2323
"./hub": "./dist/hub.mjs",
2424
"./spa": "./dist/spa.mjs",
25+
"./renderer": {
26+
"types": "./dist/renderer.d.mts",
27+
"default": "./dist/renderer/json-render.mjs"
28+
},
2529
"./package.json": "./package.json"
2630
},
2731
"files": [

‎packages/json-render-ui/src/dock-renderer.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { JsonRenderViewRef, Spec } from '@devframes/json-render'
2-
import type { JsonRenderDockRenderer } from '@devframes/json-render/hub'
2+
import type { JsonRenderDockRenderer, JsonRenderRpcContext } from '@devframes/json-render/hub'
33
import type { ComponentRegistry } from '@json-render/vue'
44
import type { ActionBridgeRpc } from './action-bridge'
55
import { createApp, h, shallowRef } from 'vue'
@@ -33,7 +33,7 @@ export interface JsonRenderDockRendererOptions {
3333
*/
3434
export function createJsonRenderDockRenderer(
3535
options: JsonRenderDockRendererOptions = {},
36-
): JsonRenderDockRenderer {
36+
): JsonRenderDockRenderer<JsonRenderRpcContext> {
3737
const registry = options.registry ?? baseRegistry
3838
return async ({ entry, container, context }) => {
3939
const view: JsonRenderViewRef = entry.view
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import type { JsonRenderDockRenderer, JsonRenderRpcContext } from '@devframes/json-render/hub'
2+
3+
/** Self-styling browser renderer, also served by `jsonRenderUiRenderer()`. */
4+
declare const renderer: JsonRenderDockRenderer<JsonRenderRpcContext>
5+
export default renderer

‎packages/json-render-ui/src/renderer-module/index.ts‎

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import type { JsonRenderDockRenderer } from '@devframes/json-render/hub'
1+
import type { DockRendererInstance } from '@devframes/hub/client'
2+
import type { JsonRenderDockRenderer, JsonRenderRpcContext } from '@devframes/json-render/hub'
23
import css from '../.generated/css'
34
import { createJsonRenderDockRenderer } from '../dock-renderer'
45

@@ -32,7 +33,7 @@ const inner = createJsonRenderDockRenderer()
3233
* there, fully styled in a light-DOM host page and inside a viewer's shadow
3334
* root alike, without leaking the reset or any global rule into the page.
3435
*/
35-
const jsonRenderDockRenderer: JsonRenderDockRenderer = async ({ entry, container, context }) => {
36+
const jsonRenderDockRenderer: JsonRenderDockRenderer<JsonRenderRpcContext> = async ({ entry, container, context }) => {
3637
const shadow = container.shadowRoot ?? container.attachShadow({ mode: 'open' })
3738
if (!shadow.querySelector(`style[${STYLE_MARKER}]`)) {
3839
const style = document.createElement('style')
@@ -66,7 +67,15 @@ const jsonRenderDockRenderer: JsonRenderDockRenderer = async ({ entry, container
6667
colorSchemeRoot.append(root)
6768
shadow.append(colorSchemeRoot)
6869

69-
const instance = await inner({ entry, container: root, context })
70+
let instance: DockRendererInstance
71+
try {
72+
instance = await inner({ entry, container: root, context })
73+
}
74+
catch (error) {
75+
observer.disconnect()
76+
colorSchemeRoot.remove()
77+
throw error
78+
}
7079
return {
7180
dispose() {
7281
observer.disconnect()

‎packages/json-render-ui/tsdown.config.ts‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { defineConfig } from 'tsdown'
1414
* emitted `.d.mts` references the packages instead of inlining their whole
1515
* type graph.
1616
*/
17-
export default defineConfig({
17+
export default defineConfig([{
1818
entry: {
1919
/**
2020
* Node-safe entry: the prebuilt SPA path + a devframe wiring helper.
@@ -46,4 +46,11 @@ export default defineConfig({
4646
'@devframes/json-render/core',
4747
],
4848
},
49-
})
49+
}, {
50+
entry: { renderer: 'src/renderer-export.ts' },
51+
clean: false,
52+
tsconfig: '../../tsconfig.base.json',
53+
dts: { emitDtsOnly: true },
54+
outExtensions: () => ({ dts: '.d.mts' }),
55+
deps: { neverBundle: ['@devframes/json-render/hub'] },
56+
}])

‎packages/json-render/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"./core": "./dist/core.mjs",
2424
"./hub": "./dist/hub.mjs",
2525
"./node": "./dist/node/index.mjs",
26+
"./view": "./dist/view.mjs",
2627
"./package.json": "./package.json"
2728
},
2829
"types": "./dist/index.d.mts",

‎packages/json-render/src/hub.ts‎

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import type { DockRenderer, DockRendererMountOptions } from '@devframes/hub/client'
1+
import type { DevframeClientContext, DockRendererInstance, DockRendererMountOptions } from '@devframes/hub/client'
22
import type { DevframeDockEntryBase } from '@devframes/hub/types'
3+
import type { DevframeRpcClient } from 'devframe/client'
4+
import type { ConnectionMeta } from 'devframe/types'
35
import type { JsonRenderView } from './types'
46
import type { JsonRenderViewRef } from './view-ref'
57

@@ -26,12 +28,22 @@ declare module '@devframes/hub/types' {
2628

2729
/**
2830
* The mount options a hub viewer hands a json-render dock renderer: the
29-
* hub's `DockRendererMountOptions` narrowed to the `'json-render'` entry.
31+
* hub's `DockRendererMountOptions` narrowed to the `'json-render'` entry
32+
* and an optional context type. Existing renderers keep the full client context.
3033
* This protocol package owns the renderer contract so every frontend
3134
* (`@devframes/json-render-ui`, a community renderer, a host page's own)
3235
* implements one shared shape instead of re-declaring it.
3336
*/
34-
export type JsonRenderDockMountOptions = DockRendererMountOptions<DevframeJsonRenderDockEntry>
37+
export type JsonRenderDockMountOptions<Context = DevframeClientContext> = Omit<DockRendererMountOptions<DevframeJsonRenderDockEntry>, 'context'> & {
38+
context: Context
39+
}
40+
41+
/** Native RPC calls and shared state consumed by the reference JSON renderer. */
42+
export interface JsonRenderRpcContext {
43+
rpc: Pick<DevframeRpcClient, 'call' | 'sharedState'> & {
44+
connectionMeta?: Pick<ConnectionMeta, 'backend'>
45+
}
46+
}
3547

3648
/**
3749
* The renderer contract for `'json-render'` docks: a hub `DockRenderer`
@@ -41,7 +53,9 @@ export type JsonRenderDockMountOptions = DockRendererMountOptions<DevframeJsonRe
4153
* module registered through the hub's renderer manifest
4254
* (`initHub({ renderers })`).
4355
*/
44-
export type JsonRenderDockRenderer = DockRenderer<DevframeJsonRenderDockEntry>
56+
export type JsonRenderDockRenderer<Context = DevframeClientContext> = (
57+
options: JsonRenderDockMountOptions<Context>,
58+
) => DockRendererInstance | Promise<DockRendererInstance>
4559

4660
/**
4761
* Build a `json-render` dock entry from a {@link JsonRenderView} and the dock

0 commit comments

Comments
 (0)