devframe 0.4.1 → 0.5.1
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/README.md +1 -1
- package/dist/adapters/build.d.mts +2 -2
- package/dist/adapters/build.mjs +9 -9
- package/dist/adapters/cli.d.mts +1 -1
- package/dist/adapters/cli.mjs +1 -1
- package/dist/adapters/dev.d.mts +2 -2
- package/dist/adapters/dev.mjs +1 -1
- package/dist/adapters/embedded.d.mts +2 -2
- package/dist/adapters/mcp.d.mts +1 -1
- package/dist/adapters/mcp.mjs +3 -3
- package/dist/client/index.d.mts +28 -28
- package/dist/client/index.mjs +27 -23
- package/dist/constants.d.mts +10 -10
- package/dist/constants.mjs +10 -10
- package/dist/{context-C9oW88gr.d.mts → context-DTRcO_UH.d.mts} +6 -6
- package/dist/{storage-CZVTPKsw.mjs → context-DaKmhhHY.mjs} +48 -3
- package/dist/{dev-C2wjXjGB.mjs → dev-Cv43GfqM.mjs} +5 -5
- package/dist/{devframe-K4IxpM4a.d.mts → devframe-BuR6n9ZD.d.mts} +44 -44
- package/dist/{dump-B1wd4u68.mjs → dump-9lKIJTLh.mjs} +15 -11
- package/dist/helpers/vite.d.mts +1 -1
- package/dist/helpers/vite.mjs +4 -4
- package/dist/{host-h3-Div5Vp8R.mjs → host-h3-Dgpgr1Ul.mjs} +16 -16
- package/dist/{index-CBSBvT9K.d.mts → index-C7M1hnvL.d.mts} +2 -2
- package/dist/{index-BhtRKN7X.d.mts → index-DH2sBIwd.d.mts} +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/node/auth.d.mts +5 -5
- package/dist/node/auth.mjs +1 -1
- package/dist/node/{internal.d.mts → hub-internals.d.mts} +3 -3
- package/dist/node/hub-internals.mjs +3 -0
- package/dist/node/index.d.mts +34 -34
- package/dist/node/index.mjs +4 -4
- package/dist/recipes/open-helpers.d.mts +5 -5
- package/dist/{human-id-Dq-qgtES.mjs → revoke-CL0LSAN9.mjs} +35 -35
- package/dist/rpc/dump.d.mts +1 -1
- package/dist/rpc/dump.mjs +1 -1
- package/dist/rpc/index.d.mts +2 -2
- package/dist/rpc/index.mjs +1 -1
- package/dist/rpc/transports/ws-client.d.mts +1 -1
- package/dist/rpc/transports/ws-client.mjs +1 -1
- package/dist/rpc/transports/ws-server.d.mts +2 -2
- package/dist/{server-BO8IDVsJ.mjs → server-BBaBJaUL.mjs} +12 -5
- package/dist/{server-KzCqriO9.d.mts → server-wHlpcdZ9.d.mts} +3 -3
- package/dist/{shared-state-CasqoUba.mjs → shared-state-BlBNYziY.mjs} +1 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/{types-dNW3UmMl.d.mts → types-BkkQ0Txg.d.mts} +4 -4
- package/dist/utils/events.d.mts +1 -1
- package/dist/utils/shared-state.d.mts +1 -1
- package/dist/utils/streaming-channel.d.mts +1 -1
- package/dist/{ws-client-BZptK_Mf.d.mts → ws-client-CVYX9niP.d.mts} +1 -1
- package/dist/{ws-server-CTeMCO1r.d.mts → ws-server-C1LjmRnp.d.mts} +5 -5
- package/package.json +5 -5
- package/skills/devframe/SKILL.md +82 -5
- package/dist/node/internal.mjs +0 -49
package/skills/devframe/SKILL.md
CHANGED
|
@@ -66,6 +66,83 @@ export default defineDevframe({
|
|
|
66
66
|
|
|
67
67
|
See `templates/counter-devframe.ts` for a runnable counter example, `templates/spa-devframe.ts` for an SPA-ready shape, and `templates/vite-client.ts` for the author's client entry.
|
|
68
68
|
|
|
69
|
+
## Project layout
|
|
70
|
+
|
|
71
|
+
Once a devframe grows past a handful of RPC functions, split them out — one file per function under `src/rpc/functions/`, with `src/rpc/index.ts` as the barrel. The `functions/` subdirectory leaves room for sibling files like `src/rpc/utils.ts` (helpers, type aliases) as the surface grows. Each function file exports a named const; the barrel collects them into a `const serverFunctions = [...] as const` that feeds the type-safe client registry recipe (`RpcDefinitionsToFunctions<typeof serverFunctions>`).
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
// src/rpc/functions/list-files.ts
|
|
75
|
+
import { defineRpcFunction } from 'devframe'
|
|
76
|
+
import { getMyToolContext } from '../../context'
|
|
77
|
+
|
|
78
|
+
export const listFiles = defineRpcFunction({
|
|
79
|
+
name: 'my-tool:list-files',
|
|
80
|
+
type: 'query',
|
|
81
|
+
jsonSerializable: true,
|
|
82
|
+
setup: (ctx) => {
|
|
83
|
+
const { loaders } = getMyToolContext(ctx)
|
|
84
|
+
return { handler: () => loaders.list() }
|
|
85
|
+
},
|
|
86
|
+
})
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
// src/rpc/index.ts
|
|
91
|
+
import { getCwd } from './functions/get-cwd'
|
|
92
|
+
import { listFiles } from './functions/list-files'
|
|
93
|
+
|
|
94
|
+
export const serverFunctions = [getCwd, listFiles] as const
|
|
95
|
+
|
|
96
|
+
declare module 'devframe' {
|
|
97
|
+
interface DevframeRpcServerFunctions
|
|
98
|
+
extends import('devframe/rpc').RpcDefinitionsToFunctions<typeof serverFunctions> {}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
// src/devframe.ts
|
|
104
|
+
import { defineDevframe } from 'devframe/types'
|
|
105
|
+
import { setMyToolContext } from './context'
|
|
106
|
+
import { serverFunctions } from './rpc'
|
|
107
|
+
|
|
108
|
+
export default defineDevframe({
|
|
109
|
+
id: 'my-tool',
|
|
110
|
+
setup(ctx) {
|
|
111
|
+
setMyToolContext(ctx, { loaders: createLoaders() })
|
|
112
|
+
serverFunctions.forEach(fn => ctx.rpc.register(fn))
|
|
113
|
+
},
|
|
114
|
+
})
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Sharing setup-time state via `src/context.ts`
|
|
118
|
+
|
|
119
|
+
When per-file RPCs need access to runtime values that `setup(ctx)` constructs once — streaming channels, shared state handles, watchers, loaders, caches — expose them through a `WeakMap<DevframeNodeContext, T>` in a sibling `src/context.ts`. This mirrors the framework's own `internalContextMap` in `packages/devframe/src/node/hub-internals/context.ts`. The WeakMap keys off the existing `DevframeNodeContext` so contexts are garbage-collected automatically when the host tears down.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
// src/context.ts
|
|
123
|
+
import type { DevframeNodeContext } from 'devframe/types'
|
|
124
|
+
|
|
125
|
+
export interface MyToolContext {
|
|
126
|
+
loaders: { list: () => Promise<string[]> }
|
|
127
|
+
// …channels, shared state handles, watchers, etc.
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const map = new WeakMap<DevframeNodeContext, MyToolContext>()
|
|
131
|
+
|
|
132
|
+
export function setMyToolContext(ctx: DevframeNodeContext, value: MyToolContext): void {
|
|
133
|
+
map.set(ctx, value)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function getMyToolContext(ctx: DevframeNodeContext): MyToolContext {
|
|
137
|
+
const value = map.get(ctx)
|
|
138
|
+
if (!value)
|
|
139
|
+
throw new Error('my-tool context not initialised — call setMyToolContext in devframe.setup')
|
|
140
|
+
return value
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Stateless RPCs and tiny demos can keep the inline shorthand inside `setup(ctx)` — reach for `src/rpc/functions/` and `src/context.ts` once you have more than one or two functions, or any shared setup state.
|
|
145
|
+
|
|
69
146
|
## Namespacing
|
|
70
147
|
|
|
71
148
|
**Always prefix** RPC names, dock IDs, command IDs, shared-state keys, and agent tool IDs with the devframe `id`:
|
|
@@ -76,7 +153,7 @@ See `templates/counter-devframe.ts` for a runnable counter example, `templates/s
|
|
|
76
153
|
'get-modules' // ✗ — may collide with other devframes sharing the host
|
|
77
154
|
```
|
|
78
155
|
|
|
79
|
-
##
|
|
156
|
+
## DevframeNodeContext at a glance
|
|
80
157
|
|
|
81
158
|
`setup(ctx)` receives the framework-neutral server-side surface. Each host corresponds to a [docs](https://devfra.me/) page:
|
|
82
159
|
|
|
@@ -340,9 +417,9 @@ const rpc = await connectDevframe()
|
|
|
340
417
|
const data = await rpc.call('my-inspector:get-stats', { limit: 10 })
|
|
341
418
|
```
|
|
342
419
|
|
|
343
|
-
`connectDevframe` auto-detects the backend via `/.
|
|
420
|
+
`connectDevframe` auto-detects the backend via `/.devframe/.connection.json`:
|
|
344
421
|
|
|
345
|
-
- **websocket** (dev mode) — full read/write, requires auth handshake. Listen for token updates on the `
|
|
422
|
+
- **websocket** (dev mode) — full read/write, requires auth handshake. Listen for token updates on the `devframe-auth` BroadcastChannel.
|
|
346
423
|
- **static** (build / spa output) — read-only, resolves calls from the baked RPC dump.
|
|
347
424
|
|
|
348
425
|
Use `rpc.sharedState.get(key)` for observable state, `rpc.client.register(defineRpcFunction(...))` to receive server broadcasts, and `rpc.callOptional(...)` when a missing handler should resolve to `undefined` instead of throwing.
|
|
@@ -373,7 +450,7 @@ At runtime, static clients look up the argument hash in the dump; misses resolve
|
|
|
373
450
|
|
|
374
451
|
| Subcommand | Action |
|
|
375
452
|
|------------|--------|
|
|
376
|
-
| *(default)* | Dev server on port 9999 (or `--port`) — WebSocket RPC, `cli.distDir` served at `/.
|
|
453
|
+
| *(default)* | Dev server on port 9999 (or `--port`) — WebSocket RPC, `cli.distDir` served at `/.devframe/` |
|
|
377
454
|
| `build` | Static snapshot → `./dist-static/` (configurable via `--out-dir`) |
|
|
378
455
|
| `spa` | Deployable SPA → `./dist-spa/` |
|
|
379
456
|
| `mcp` | stdio MCP server (experimental) |
|
|
@@ -405,7 +482,7 @@ For "open file in editor" + "reveal in finder", prefer the prebuilt `openHelpers
|
|
|
405
482
|
|
|
406
483
|
- Unit-test host classes with fake contexts.
|
|
407
484
|
- Run `templates/counter-devframe.ts` under each adapter for integration coverage.
|
|
408
|
-
- Snapshot the build-static RPC dump (`<outDir>/.
|
|
485
|
+
- Snapshot the build-static RPC dump (`<outDir>/.devframe/.rpc-dump/index.json`) to catch accidental drift in `static` function outputs.
|
|
409
486
|
|
|
410
487
|
## Further reading
|
|
411
488
|
|
package/dist/node/internal.mjs
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { t as createStorage } from "../storage-CZVTPKsw.mjs";
|
|
2
|
-
import { n as revokeActiveConnectionsForToken, r as revokeAuthToken, t as humanId } from "../human-id-Dq-qgtES.mjs";
|
|
3
|
-
import { n as resolveBasePath, t as normalizeBasePath } from "../_shared-CUFqO4kJ.mjs";
|
|
4
|
-
import { join } from "pathe";
|
|
5
|
-
//#region src/node/internal/context.ts
|
|
6
|
-
const internalContextMap = /* @__PURE__ */ new WeakMap();
|
|
7
|
-
function getInternalContext(context) {
|
|
8
|
-
if (!internalContextMap.has(context)) {
|
|
9
|
-
const storage = createStorage({
|
|
10
|
-
filepath: join(context.host.getStorageDir("global"), "auth.json"),
|
|
11
|
-
initialValue: { trusted: {} }
|
|
12
|
-
});
|
|
13
|
-
const remoteTokens = /* @__PURE__ */ new Map();
|
|
14
|
-
function revokeRemoteToken(token) {
|
|
15
|
-
if (!remoteTokens.delete(token)) return;
|
|
16
|
-
revokeActiveConnectionsForToken(context, token);
|
|
17
|
-
}
|
|
18
|
-
const internalContext = {
|
|
19
|
-
storage: { auth: storage },
|
|
20
|
-
revokeAuthToken: (token) => revokeAuthToken(context, storage, token),
|
|
21
|
-
remoteTokens,
|
|
22
|
-
allocateRemoteToken(dockId, origin, originLock) {
|
|
23
|
-
const token = humanId();
|
|
24
|
-
remoteTokens.set(token, {
|
|
25
|
-
dockId,
|
|
26
|
-
origin,
|
|
27
|
-
originLock
|
|
28
|
-
});
|
|
29
|
-
return token;
|
|
30
|
-
},
|
|
31
|
-
revokeRemoteToken,
|
|
32
|
-
revokeRemoteTokensForDock(dockId) {
|
|
33
|
-
const tokensToRevoke = [];
|
|
34
|
-
for (const [token, record] of remoteTokens) if (record.dockId === dockId) tokensToRevoke.push(token);
|
|
35
|
-
for (const token of tokensToRevoke) revokeRemoteToken(token);
|
|
36
|
-
},
|
|
37
|
-
isRemoteTokenTrusted(token, requestOrigin) {
|
|
38
|
-
const record = remoteTokens.get(token);
|
|
39
|
-
if (!record) return false;
|
|
40
|
-
if (!record.originLock) return true;
|
|
41
|
-
return !!requestOrigin && record.origin === requestOrigin;
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
internalContextMap.set(context, internalContext);
|
|
45
|
-
}
|
|
46
|
-
return internalContextMap.get(context);
|
|
47
|
-
}
|
|
48
|
-
//#endregion
|
|
49
|
-
export { getInternalContext, internalContextMap, normalizeBasePath, resolveBasePath };
|