|
| 1 | +--- |
| 2 | +outline: deep |
| 3 | +--- |
| 4 | + |
| 5 | +# Remote Assets |
| 6 | + |
| 7 | +Anywhere a devframe serves a built SPA — `cli.distDir` — you can point at a **published npm package** instead of a local directory. The assets are then fetched on demand and cached locally, so the node package doesn't have to bundle its UI. |
| 8 | + |
| 9 | +This keeps the installed footprint small: a plugin's prebuilt SPA is usually the bulk of its tarball, and with remote assets it's served on first use rather than shipped to every consumer up front. |
| 10 | + |
| 11 | +## Basic example |
| 12 | + |
| 13 | +Give `cli.distDir` a `RemoteAssets` object naming the package and exact version that holds the built UI: |
| 14 | + |
| 15 | +```ts |
| 16 | +import type { RemoteAssets } from 'devframe' |
| 17 | +import { defineDevframe } from 'devframe' |
| 18 | +import pkg from '../package.json' with { type: 'json' } |
| 19 | + |
| 20 | +const distDir: RemoteAssets = { |
| 21 | + package: '@acme/my-tool-assets', |
| 22 | + version: pkg.version, |
| 23 | + resolveFrom: import.meta.url, |
| 24 | +} |
| 25 | + |
| 26 | +export default defineDevframe({ |
| 27 | + id: 'my-tool', |
| 28 | + version: pkg.version, |
| 29 | + packageName: pkg.name, |
| 30 | + cli: { distDir }, |
| 31 | + setup(ctx) { |
| 32 | + // … |
| 33 | + }, |
| 34 | +}) |
| 35 | +``` |
| 36 | + |
| 37 | +That's it — the UI mounts as usual. The first request for each file is streamed from a CDN and written to a local cache; subsequent requests are served from disk. |
| 38 | + |
| 39 | +## How assets resolve |
| 40 | + |
| 41 | +For each request the source resolves in order: |
| 42 | + |
| 43 | +1. **Locally installed package** — resolved from `resolveFrom` (`import.meta.url`). If `@acme/my-tool-assets` is installed next to your tool, it's served directly with no network. This is the offline path. |
| 44 | +2. **On-disk cache** — files already fetched, under the project's storage directory. |
| 45 | +3. **CDN back-proxy** — [jsDelivr](https://www.jsdelivr.com/) by default, mirroring npm. Each file streams to the browser and is cached on the way past. |
| 46 | + |
| 47 | +Exact-version URLs are immutable, so a cached file never goes stale. |
| 48 | + |
| 49 | +## Options |
| 50 | + |
| 51 | +| Field | Purpose | |
| 52 | +|-------|---------| |
| 53 | +| `package` | npm package holding the built assets. | |
| 54 | +| `version` | Exact version to serve — usually your tool's own `pkg.version`. | |
| 55 | +| `resolveFrom` | `import.meta.url` of the declaring module; enables the zero-network path from a locally installed copy. Omit to skip straight to cache + CDN. | |
| 56 | +| `path` | Subpath inside the package the assets live under. Defaults to `dist`. | |
| 57 | +| `provider` | `'jsdelivr'` (default), `'unpkg'`, or a custom provider for an internal mirror. | |
| 58 | +| `offline` | `true` serves only from a local install or the cache — never the network. | |
| 59 | + |
| 60 | +`package` and `version` are interpolated into the CDN URL and cache path, so they're validated: an invalid npm name or non-exact version throws [`DF0065`](../errors/DF0065). |
| 61 | + |
| 62 | +## Offline and air-gapped use |
| 63 | + |
| 64 | +Remote assets are a convenience, not a hard network dependency. To run with no network, install the assets package explicitly — resolution step 1 then serves it locally: |
| 65 | + |
| 66 | +```sh |
| 67 | +npm install @acme/my-tool-assets |
| 68 | +``` |
| 69 | + |
| 70 | +Set `offline: true` to guarantee the CDN is never contacted, or point `provider` at an internal npm mirror. |
| 71 | + |
| 72 | +## Custom provider |
| 73 | + |
| 74 | +A custom provider supplies the file URL, and optionally a file listing (used for correct 404s, SPA fallback, and static builds): |
| 75 | + |
| 76 | +```ts |
| 77 | +const distDir: RemoteAssets = { |
| 78 | + package: '@acme/my-tool-assets', |
| 79 | + version: pkg.version, |
| 80 | + resolveFrom: import.meta.url, |
| 81 | + provider: { |
| 82 | + fileUrl: (name, version, file) => |
| 83 | + `https://npm.internal.acme.com/${name}@${version}/${file}`, |
| 84 | + }, |
| 85 | +} |
| 86 | +``` |
| 87 | + |
| 88 | +## Publishing the assets |
| 89 | + |
| 90 | +The assets package is an ordinary npm package that ships the built UI under `path` (default `dist`) and exposes its `package.json` so `resolveFrom` can locate it: |
| 91 | + |
| 92 | +```json |
| 93 | +{ |
| 94 | + "name": "@acme/my-tool-assets", |
| 95 | + "version": "1.0.0", |
| 96 | + "exports": { "./package.json": "./package.json" }, |
| 97 | + "files": ["dist"] |
| 98 | +} |
| 99 | +``` |
| 100 | + |
| 101 | +Keep its version in lockstep with the tool that declares it, so `version: pkg.version` always points at matching UI. |
0 commit comments