devframe 0.9.0-beta.4 → 0.9.0-beta.5
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/{_shared-CGPVmXUM.mjs → _shared-CKZPvSrN.mjs} +2 -1
- package/dist/{_shared-Bm8p4-fD.d.mts → _shared-QkI6ljJS.d.mts} +2 -2
- package/dist/adapters/build.d.mts +10 -12
- package/dist/adapters/build.mjs +23 -27
- package/dist/adapters/cac.d.mts +1 -1
- package/dist/adapters/cac.mjs +3 -4
- package/dist/adapters/dev.d.mts +19 -5
- package/dist/adapters/dev.mjs +2 -2
- package/dist/adapters/embedded.d.mts +1 -1
- package/dist/adapters/initiate.d.mts +4 -3
- package/dist/adapters/initiate.mjs +1 -1
- package/dist/adapters/mcp.d.mts +1 -1
- package/dist/adapters/mcp.mjs +1 -1
- package/dist/cli/main.mjs +2 -2
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.mjs +2 -1175
- package/dist/constants.d.mts +1 -2
- package/dist/constants.mjs +1 -2
- package/dist/{context-DV4uWieM.d.mts → context-CQkRTbAc.d.mts} +1 -1
- package/dist/{context-b6iSomtv.mjs → context-D0HD0F7C.mjs} +19 -7
- package/dist/{context-CMSryXs-.mjs → context-D0jErmlx.mjs} +1 -1
- package/dist/{dev-4xjiynUD.mjs → dev-QEuT8SOF.mjs} +15 -9
- package/dist/{devframe-5oblos7I.d.mts → devframe-Cfx6doi4.d.mts} +41 -64
- package/dist/{diagnostics-C7IGWppM.mjs → diagnostics-DEuH3P5O.mjs} +32 -0
- package/dist/{host-agent-BBkKT27Q.mjs → host-agent-C5HjzfBz.mjs} +41 -3
- package/dist/{host-h3-CzCZwtuV.mjs → host-h3-Crkzm42q.mjs} +2 -2
- package/dist/{http-Z0w9baEe.mjs → http-CVwj7b4g.mjs} +2 -2
- package/dist/{index-BzDl02lt.d.mts → index-MYi1SZVL.d.mts} +2 -2
- package/dist/index.d.mts +3 -2
- package/dist/{instance-registry-Du_hbx-j.mjs → instance-registry-M2p-z6xE.mjs} +1 -1
- package/dist/{instance-shell-B0TwOxCl.mjs → instance-shell-DJ8s-MFw.mjs} +5 -5
- package/dist/{instance-shell-B-BSVp37.d.mts → instance-shell-D_UA0Idz.d.mts} +2 -2
- package/dist/internal/index.d.mts +72 -10
- package/dist/internal/index.mjs +7 -7
- package/dist/node/auth.d.mts +2 -2
- package/dist/node/auth.mjs +1 -1
- package/dist/node/hub-internals.d.mts +2 -2
- package/dist/node/hub-internals.mjs +2 -2
- package/dist/node/index.d.mts +1 -1
- package/dist/node/index.mjs +2 -2
- package/dist/recipes/common-rpc-functions.d.mts +1 -1
- package/dist/recipes/interactive-auth.d.mts +2 -2
- package/dist/recipes/interactive-auth.mjs +2 -2
- package/dist/remote-assets-DclsYyoL.d.mts +106 -0
- package/dist/rpc/transports/ws-bun.d.mts +1 -1
- package/dist/{rpc-core-B4Qm66Qt.d.mts → rpc-core-C8JQ3p7A.d.mts} +2 -2
- package/dist/{rpc-core-Cva-0pw0.mjs → rpc-core-IhcWl_xn.mjs} +1 -1
- package/dist/state-Cc1IeSmQ.mjs +1291 -0
- package/dist/storage-BVuipbGS.mjs +137 -0
- package/dist/types/index.d.mts +3 -2
- package/dist/utils/events.d.mts +1 -1
- package/dist/utils/open.d.mts +2 -10
- package/dist/utils/open.mjs +56 -1
- package/dist/utils/remote-assets.d.mts +15 -0
- package/dist/utils/remote-assets.mjs +309 -0
- package/dist/utils/serve-static.d.mts +13 -5
- package/dist/utils/serve-static.mjs +103 -9
- package/dist/utils/shared-state.d.mts +1 -1
- package/dist/utils/streaming-channel.d.mts +1 -1
- package/package.json +2 -3
- package/skills/devframe/SKILL.md +4 -5
- package/dist/open-Dz4V4irZ.mjs +0 -547
- package/dist/state-BeUHDDjk.mjs +0 -108
- package/dist/storage-CaUVrBrG.mjs +0 -1122
- package/skills/devframe/templates/spa-devframe.ts +0 -38
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createReadStream } from "node:fs";
|
|
2
|
-
import { extname, join, normalize, resolve, sep } from "pathe";
|
|
3
2
|
import { stat } from "node:fs/promises";
|
|
4
|
-
import { H3, defineHandler } from "h3";
|
|
5
3
|
import { Readable } from "node:stream";
|
|
6
4
|
import { lookup } from "mrmime";
|
|
5
|
+
import { extname, join, normalize, resolve, sep } from "pathe";
|
|
6
|
+
import { H3, defineHandler } from "h3";
|
|
7
7
|
//#region src/utils/serve-static.ts
|
|
8
8
|
const HTML_EXTENSIONS = [".html", ".htm"];
|
|
9
9
|
async function statFile(abs) {
|
|
@@ -74,7 +74,43 @@ function normalizeOptions(options) {
|
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
77
|
-
*
|
|
77
|
+
* Drive one request through a {@link RemoteAssetsStore}: the store's
|
|
78
|
+
* `Response` on a hit, a 404 on a miss, or a 502 (styled error page for HTML
|
|
79
|
+
* navigations) on provider failure — shared between the h3 and connect flavors.
|
|
80
|
+
*/
|
|
81
|
+
async function remoteResponse(store, urlPath, accept) {
|
|
82
|
+
try {
|
|
83
|
+
return await store.serve(urlPath) ?? new Response(null, { status: 404 });
|
|
84
|
+
} catch (error) {
|
|
85
|
+
if (typeof accept === "string" && accept.includes("text/html")) return new Response(remoteErrorPage(store.assets.package, store.assets.version, error instanceof Error ? error.message : String(error)), {
|
|
86
|
+
status: 502,
|
|
87
|
+
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
88
|
+
});
|
|
89
|
+
return new Response(null, { status: 502 });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function serveRemoteAssetsHandler(store) {
|
|
93
|
+
return defineHandler(async (event) => {
|
|
94
|
+
const method = event.req.method;
|
|
95
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
96
|
+
event.res.status = 405;
|
|
97
|
+
event.res.headers.set("Allow", "GET, HEAD");
|
|
98
|
+
return "";
|
|
99
|
+
}
|
|
100
|
+
const res = await remoteResponse(store, event.url.pathname, event.req.headers.get("accept"));
|
|
101
|
+
event.res.status = res.status;
|
|
102
|
+
res.headers.forEach((v, k) => event.res.headers.set(k, v));
|
|
103
|
+
if (method === "HEAD") {
|
|
104
|
+
await res.body?.cancel().catch(() => {});
|
|
105
|
+
return "";
|
|
106
|
+
}
|
|
107
|
+
return res.body ?? "";
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* h3 event handler that serves files from `source` with SPA fallback — a
|
|
112
|
+
* local directory, or a {@link RemoteAssetsStore} whose files stream through
|
|
113
|
+
* the CDN back-proxy into the local cache.
|
|
78
114
|
*
|
|
79
115
|
* Drop-in replacement for `fromNodeMiddleware(sirv(dir, { dev: true, single: true }))`
|
|
80
116
|
* when the surrounding server is an h3 app — no `Cache-Control` beyond
|
|
@@ -82,8 +118,9 @@ function normalizeOptions(options) {
|
|
|
82
118
|
* file extension falls back to `<dir>/index.html` so client-side routing
|
|
83
119
|
* works.
|
|
84
120
|
*/
|
|
85
|
-
function serveStaticHandler(
|
|
86
|
-
|
|
121
|
+
function serveStaticHandler(source, options) {
|
|
122
|
+
if (typeof source !== "string") return serveRemoteAssetsHandler(source);
|
|
123
|
+
const absDir = resolve(source);
|
|
87
124
|
const opts = normalizeOptions(options);
|
|
88
125
|
return defineHandler(async (event) => {
|
|
89
126
|
const method = event.req.method;
|
|
@@ -109,9 +146,9 @@ function serveStaticHandler(dir, options) {
|
|
|
109
146
|
* from `event.url.pathname`, so the file resolver sees paths relative to
|
|
110
147
|
* `dir`.
|
|
111
148
|
*/
|
|
112
|
-
function mountStaticHandler(app, base,
|
|
149
|
+
function mountStaticHandler(app, base, source, options) {
|
|
113
150
|
const staticApp = new H3();
|
|
114
|
-
staticApp.use(serveStaticHandler(
|
|
151
|
+
staticApp.use(serveStaticHandler(source, options));
|
|
115
152
|
app.mount(base.replace(/\/$/, ""), staticApp);
|
|
116
153
|
}
|
|
117
154
|
/**
|
|
@@ -121,8 +158,8 @@ function mountStaticHandler(app, base, dir, options) {
|
|
|
121
158
|
* Connect stack — avoids forcing the host package to depend on h3 just to
|
|
122
159
|
* adapt an event handler back into Node middleware.
|
|
123
160
|
*/
|
|
124
|
-
function serveStaticNodeMiddleware(
|
|
125
|
-
const absDir = resolve(
|
|
161
|
+
function serveStaticNodeMiddleware(source, options) {
|
|
162
|
+
const absDir = typeof source === "string" ? resolve(source) : void 0;
|
|
126
163
|
const opts = normalizeOptions(options);
|
|
127
164
|
return (req, res, next) => {
|
|
128
165
|
(async () => {
|
|
@@ -138,6 +175,22 @@ function serveStaticNodeMiddleware(dir, options) {
|
|
|
138
175
|
return;
|
|
139
176
|
}
|
|
140
177
|
const url = req.url ?? "/";
|
|
178
|
+
if (absDir === void 0) {
|
|
179
|
+
const response = await remoteResponse(source, url, req.headers.accept);
|
|
180
|
+
if (response.status === 404 && next) {
|
|
181
|
+
next();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
res.statusCode = response.status;
|
|
185
|
+
response.headers.forEach((v, k) => res.setHeader(k, v));
|
|
186
|
+
if (method === "HEAD" || !response.body) {
|
|
187
|
+
await response.body?.cancel().catch(() => {});
|
|
188
|
+
res.end();
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
Readable.fromWeb(response.body).pipe(res);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
141
194
|
const file = await resolveTarget(absDir, url, opts.indexNames, opts.single);
|
|
142
195
|
if (!file) {
|
|
143
196
|
if (next) {
|
|
@@ -164,5 +217,46 @@ function serveStaticNodeMiddleware(dir, options) {
|
|
|
164
217
|
});
|
|
165
218
|
};
|
|
166
219
|
}
|
|
220
|
+
/**
|
|
221
|
+
* Minimal, dependency-free HTML shown when a remote-assets request cannot
|
|
222
|
+
* be satisfied (no installed package, no cache, provider unreachable).
|
|
223
|
+
*/
|
|
224
|
+
function remoteErrorPage(pkg, version, reason) {
|
|
225
|
+
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
226
|
+
const name = esc(pkg);
|
|
227
|
+
const ver = esc(version);
|
|
228
|
+
return `<!doctype html>
|
|
229
|
+
<html lang="en">
|
|
230
|
+
<head>
|
|
231
|
+
<meta charset="utf-8">
|
|
232
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
233
|
+
<title>Client assets unavailable</title>
|
|
234
|
+
<style>
|
|
235
|
+
:root { color-scheme: light dark; }
|
|
236
|
+
body { margin: 0; min-height: 100vh; display: grid; place-items: center; font: 14px/1.6 ui-sans-serif, system-ui, sans-serif; background: #fafafa; color: #27272a; }
|
|
237
|
+
main { max-width: 32rem; padding: 2rem; }
|
|
238
|
+
h1 { font-size: 1rem; font-weight: 600; margin: 0 0 .5rem; }
|
|
239
|
+
p { margin: .5rem 0; opacity: .8; }
|
|
240
|
+
pre { font: 12px/1.6 ui-monospace, monospace; background: rgb(125 125 125 / .12); border-radius: 6px; padding: .75rem 1rem; overflow-x: auto; }
|
|
241
|
+
button { font: inherit; padding: .35rem 1rem; border-radius: 6px; border: 1px solid rgb(125 125 125 / .3); background: transparent; color: inherit; cursor: pointer; }
|
|
242
|
+
button:hover { background: rgb(125 125 125 / .08); }
|
|
243
|
+
.muted { opacity: .55; font-size: 12px; }
|
|
244
|
+
@media (prefers-color-scheme: dark) { body { background: #111; color: #d4d4d8; } }
|
|
245
|
+
</style>
|
|
246
|
+
</head>
|
|
247
|
+
<body>
|
|
248
|
+
<main>
|
|
249
|
+
<h1>Client assets unavailable</h1>
|
|
250
|
+
<p>The UI for this tool is served from <code>${name}@${ver}</code>, which could not be reached.</p>
|
|
251
|
+
<pre>${esc(reason)}</pre>
|
|
252
|
+
<p>To use it without network access, install the assets package locally:</p>
|
|
253
|
+
<pre>npm install ${name}@${ver}</pre>
|
|
254
|
+
<button onclick="location.reload()">Retry</button>
|
|
255
|
+
<p class="muted">devframe remote assets</p>
|
|
256
|
+
</main>
|
|
257
|
+
</body>
|
|
258
|
+
</html>
|
|
259
|
+
`;
|
|
260
|
+
}
|
|
167
261
|
//#endregion
|
|
168
262
|
export { mountStaticHandler, serveStaticHandler, serveStaticNodeMiddleware };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { at as Immutable, ct as ImmutableObject, dt as SharedStateEvents, ft as SharedStateOptions, lt as ImmutableSet, mt as createSharedState, ot as ImmutableArray, pt as SharedStatePatch, st as ImmutableMap, ut as SharedState } from "../devframe-Cfx6doi4.mjs";
|
|
2
2
|
export { Immutable, ImmutableArray, ImmutableMap, ImmutableObject, ImmutableSet, SharedState, SharedStateEvents, SharedStateOptions, SharedStatePatch, createSharedState };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $ as StreamErrorPayload, Q as CreateStreamSinkOptions, X as BufferedChunk, Z as CreateStreamReaderOptions, et as StreamReader, it as createStreamSink, nt as StreamSinkEvents, rt as createStreamReader, tt as StreamSink } from "../devframe-Cfx6doi4.mjs";
|
|
2
2
|
export { BufferedChunk, CreateStreamReaderOptions, CreateStreamSinkOptions, StreamErrorPayload, StreamReader, StreamSink, StreamSinkEvents, createStreamReader, createStreamSink };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devframe",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.9.0-beta.
|
|
4
|
+
"version": "0.9.0-beta.5",
|
|
5
5
|
"description": "Framework for building one portable devtool integration that runs in any viewer.",
|
|
6
6
|
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"./utils/launch-editor": "./dist/utils/launch-editor.mjs",
|
|
53
53
|
"./utils/nanoid": "./dist/utils/nanoid.mjs",
|
|
54
54
|
"./utils/open": "./dist/utils/open.mjs",
|
|
55
|
+
"./utils/remote-assets": "./dist/utils/remote-assets.mjs",
|
|
55
56
|
"./utils/simple-schema": "./dist/utils/simple-schema.mjs",
|
|
56
57
|
"./utils/serve-static": "./dist/utils/serve-static.mjs",
|
|
57
58
|
"./utils/shared-state": "./dist/utils/shared-state.mjs",
|
|
@@ -89,7 +90,6 @@
|
|
|
89
90
|
"@standard-schema/spec": "^1.1.0",
|
|
90
91
|
"birpc": "^4.1.0",
|
|
91
92
|
"crossws": "^0.4.10",
|
|
92
|
-
"destr": "^2.0.5",
|
|
93
93
|
"h3": "^2.0.1-rc.26",
|
|
94
94
|
"mrmime": "^2.0.1",
|
|
95
95
|
"nostics": "^1.2.0",
|
|
@@ -106,7 +106,6 @@
|
|
|
106
106
|
"mlly": "^1.8.2",
|
|
107
107
|
"obug": "^2.1.4",
|
|
108
108
|
"ohash": "^2.0.11",
|
|
109
|
-
"open": "^11.0.0",
|
|
110
109
|
"p-limit": "^7.3.1",
|
|
111
110
|
"perfect-debounce": "^2.1.0",
|
|
112
111
|
"structured-clone-es": "^2.0.1",
|
package/skills/devframe/SKILL.md
CHANGED
|
@@ -5,7 +5,7 @@ description: >
|
|
|
5
5
|
framework- and build-tool-agnostic foundation for defining a
|
|
6
6
|
devtool once and serving it in many places. Covers
|
|
7
7
|
DevframeDefinition, picking the right deployment adapter
|
|
8
|
-
(cli / build /
|
|
8
|
+
(cli / build / vite / embedded / mcp), designing RPC
|
|
9
9
|
contracts, exposing an agent-native surface over MCP, and
|
|
10
10
|
wiring the author's SPA client. For host-level features (docks,
|
|
11
11
|
terminals, palette, etc.), the devframe can be mounted into a
|
|
@@ -85,7 +85,7 @@ export default defineDevframe({
|
|
|
85
85
|
|
|
86
86
|
`setup(ctx)` registers RPC functions, shared state, diagnostics, and any other devframe-level wiring. Host adapters can augment `ctx` with extra surfaces - for example, mounting into Vite DevTools via `createPluginFromDevframe(d)` exposes `docks`, `terminals`, `messages`, and `commands` on the augmented context, and the kit auto-derives an iframe dock entry from `id` / `name` / `icon` / `basePath`. For richer host-side behaviour (custom-render, terminals, palette commands) pass `options.setup` to `createPluginFromDevframe`.
|
|
87
87
|
|
|
88
|
-
See `templates/counter-devframe.ts` for a runnable counter example
|
|
88
|
+
See `templates/counter-devframe.ts` for a runnable counter example and `templates/vite-client.ts` for the author's client entry.
|
|
89
89
|
|
|
90
90
|
## Scoped context (preferred)
|
|
91
91
|
|
|
@@ -504,7 +504,7 @@ const data = await my.rpc.call('get-stats', { limit: 10 })
|
|
|
504
504
|
`connectDevframe` auto-detects the backend via `/.devframe/.connection.json`:
|
|
505
505
|
|
|
506
506
|
- **websocket** (dev mode) - full read/write, requires auth handshake. Listen for token updates on the `devframe-auth` BroadcastChannel.
|
|
507
|
-
- **static** (build
|
|
507
|
+
- **static** (build output) - read-only, resolves calls from the baked RPC dump.
|
|
508
508
|
|
|
509
509
|
Use `my.rpc.sharedState(key)` for observable state, `my.rpc.register(defineRpcFunction(...))` to receive server broadcasts, `my.rpc.callOptional(...)` when a missing handler should resolve to `undefined` instead of throwing, and `my.settings.{project,global}` for persisted per-workspace / per-user settings synced from the server.
|
|
510
510
|
|
|
@@ -530,13 +530,12 @@ At runtime, static clients look up the argument hash in the dump; misses resolve
|
|
|
530
530
|
|
|
531
531
|
## CLI adapter subcommands
|
|
532
532
|
|
|
533
|
-
`createCac(devframe).parse()` gives the tool
|
|
533
|
+
`createCac(devframe).parse()` gives the tool three subcommands out of the box:
|
|
534
534
|
|
|
535
535
|
| Subcommand | Action |
|
|
536
536
|
|------------|--------|
|
|
537
537
|
| *(default)* | Dev server on port 9999 (or `--port`) - WebSocket RPC, `cli.distDir` served at `/.devframe/` |
|
|
538
538
|
| `build` | Static snapshot → `./dist-static/` (configurable via `--out-dir`) |
|
|
539
|
-
| `spa` | Deployable SPA → `./dist-spa/` |
|
|
540
539
|
| `mcp` | stdio MCP server |
|
|
541
540
|
|
|
542
541
|
**Bring your own CLI framework?** `createCac` (`devframe/adapters/cac`) is just a cac wrapper around three peer factories - `createDevServer` (`devframe/adapters/dev`), `createBuild` (`devframe/adapters/build`), and `createMcpServer` (`devframe/adapters/mcp`). Use them directly with commander/yargs/oclif when `createCac`'s baked-in command structure doesn't fit. `cac` is an optional peer dependency pulled in only through `devframe/adapters/cac`, so bring-your-own-CLI tools run without installing it. `createDevServer` returns a `StartedServer` handle (`origin`, `port`, `app`, `wss`, `close()`) so you can wire SIGINT / hot-reload teardown into the surrounding program. `parseCliFlags(schema, raw)` and `defineCliFlags(...)` (both from `devframe/adapters/cac`) validate an arbitrary flag bag against a `CliFlagsSchema` - the helpers are framework-agnostic.
|