Client Assets
A devframe's UI is a built SPA; clientAssets says where it lives: a local directory or published npm package.
Mounting a local build
Point clientAssets at your SPA build directory, resolved from the module:
import { fileURLToPath } from 'node:url'
import { defineDevframe } from 'devframe'
import pkg from '../package.json' with { type: 'json' }
export default defineDevframe({
id: 'my-tool',
version: pkg.version,
packageName: pkg.name,
clientAssets: fileURLToPath(new URL('../dist/spa', import.meta.url)),
setup(ctx) {
// …
},
})devframe serves it with SPA fallback (unknown paths → index.html) and no-store dev caching. Build the SPA with a relative base (vite: { base: './' }); it reads its runtime base from document.baseURI.
The dev, build, and Vite adapters share clientAssets; the deprecated cli.distDir is a fallback when it's unset.
Programmatic hosting from setup
To host assets beyond the primary UI, use ctx.views.hostStatic:
export default defineDevframe({
id: 'my-tool',
version: pkg.version,
packageName: pkg.name,
importMetaUrl: import.meta.url,
clientAssets: fileURLToPath(new URL('../dist/spa', import.meta.url)),
setup(ctx) {
// Serve an extra static bundle at a sibling base.
ctx.views.hostStatic(
'/docs/',
fileURLToPath(new URL('../dist/docs', import.meta.url)),
)
// A remote source works here too, same shape as `clientAssets`.
ctx.views.hostStatic('/legacy/', {
package: '@acme/my-tool-legacy-ui',
version: pkg.version,
})
},
})hostStatic(baseUrl, source, defaultResolveFrom?) takes clientAssets's StaticAssetsSource; dev registers middleware live, build copies into the static output.
Remote assets
Give clientAssets a RemoteAssets object naming a published npm package and exact version:
import type { RemoteAssets } from 'devframe'
import { defineDevframe } from 'devframe'
import pkg from '../package.json' with { type: 'json' }
const clientAssets: RemoteAssets = {
package: '@acme/my-tool-assets',
version: pkg.version,
}
export default defineDevframe({
id: 'my-tool',
version: pkg.version,
packageName: pkg.name,
importMetaUrl: import.meta.url,
clientAssets,
setup(ctx) {
// …
},
})The definition's importMetaUrl is the resolution base.
How assets resolve
Per request, resolution tries in order:
- Locally installed package: resolved from
resolveFrom(defaultimportMetaUrl); served with no network. - On-disk cache: files already fetched, under the project's storage directory.
- CDN back-proxy: jsDelivr by default; exact-version URLs are immutable, so caches never stale.
Options
package and version (exact) name the published assets; resolveFrom, path, provider, and offline tune resolution; every field is in the Node-Side API reference.
An invalid npm name or non-exact version throws DF0065.
Offline and air-gapped use
Install the assets package explicitly; step 1 serves it locally. Set offline: true to never contact the CDN, or point provider at a mirror:
npm install @acme/my-tool-assetsWhen the assets can't be reached
A file absent from local install and cache, with the provider unreachable, raises DF0060; an HTML navigation gets a self-contained error page.
It also posts the failure to window.parent (DEVFRAME_REMOTE_ASSETS_ERROR_MESSAGE_TYPE from devframe/constants, payload RemoteAssetsErrorMessage) for an embedding hub UI provider like @devframes/hub-ui.
Custom provider
A custom provider supplies the file URL, optionally a listing:
const clientAssets: RemoteAssets = {
package: '@acme/my-tool-assets',
version: pkg.version,
provider: {
fileUrl: (name, version, file) =>
`https://npm.internal.acme.com/${name}@${version}/${file}`,
},
}Publishing the assets
An ordinary npm package ships the built UI under path (default dist) and exposes its package.json for the resolver:
{
"name": "@acme/my-tool-assets",
"version": "1.0.0",
"exports": { "./package.json": "./package.json" },
"files": ["dist"]
}Keep its version in lockstep with the tool.
Streaming
Streaming channels push chunk-style data from the node side to the browser side over the RPC socket.
Scoped Context
A scoped context is a namespaced view of the context: it auto-prefixes every RPC id, shared-state key, and streaming channel with your tool's id, and adds a typed, persisted settings store, used from a single tool's code.