Client Assets

A devframe's UI is a built SPA; clientAssets says where it lives: a local directory or published npm package.

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:

  1. Locally installed package: resolved from resolveFrom (default importMetaUrl); served with no network.
  2. On-disk cache: files already fetched, under the project's storage directory.
  3. 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-assets

When 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.