Skip to content

Commit b44ac51

Browse files
antfubotopencode
andcommitted
refactor(devframe)!: keep utils/hash; move hub-only internals to devframe/internal
- Restore `devframe/utils/hash` — kept as a public utility (maintainer request). - Add a new `devframe/internal` entry point for the low-level primitives shared only between `devframe` and `@devframes/hub`, and relocate `createContextRpcServer`, `DevframeAgentHost`, and `coerceAgentPositionalArgs` there from `devframe/node`. `devframe/internal` is an explicitly-unstable cross-package surface; the hub imports these three from it. This keeps the public `devframe/node` barrel to the genuinely-public server-assembly API. Updates the migration guide, SKILL/utilities docs, knip entry list, plan, and regenerates the tsnapi snapshots. Co-authored-by: opencode <noreply@opencode.ai>
1 parent 02c23fd commit b44ac51

25 files changed

Lines changed: 148 additions & 66 deletions

alias.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ export const alias = {
1818
'devframe/node/auth': r('devframe/src/node/auth/index.ts'),
1919
'devframe/node/hub-internals': r('devframe/src/node/hub-internals/index.ts'),
2020
'devframe/node': r('devframe/src/node/index.ts'),
21+
'devframe/internal': r('devframe/src/internal/index.ts'),
2122
'devframe/constants': r('devframe/src/constants.ts'),
2223
'devframe/utils/agent-tool-name': r('devframe/src/utils/agent-tool-name.ts'),
2324
'devframe/utils/colors': r('devframe/src/utils/colors.ts'),
2425
'devframe/utils/crypto-token': r('devframe/src/utils/crypto-token.ts'),
2526
'devframe/utils/events': r('devframe/src/utils/events.ts'),
27+
'devframe/utils/hash': r('devframe/src/utils/hash.ts'),
2628
'devframe/utils/launch-editor': r('devframe/src/utils/launch-editor.ts'),
2729
'devframe/utils/nanoid': r('devframe/src/utils/nanoid.ts'),
2830
'devframe/utils/open': r('devframe/src/utils/open.ts'),

docs/guide/migration-0.9.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,21 +114,20 @@ import { defineDevframe, defineRpcFunction } from 'devframe'
114114

115115
`devframe/types` still resolves as the type-only subpath — useful for `declare module 'devframe/types'` augmentations — but `devframe` is the canonical import for both values and types.
116116

117-
## `devframe/utils/{hash,promise,scope}` are removed
117+
## `devframe/utils/{promise,scope}` are removed
118118

119-
Three utility subpaths with no integration consumers are removed:
119+
Two utility subpaths with no integration consumers are removed:
120120

121121
| Removed | Replacement |
122122
|---------|-------------|
123123
| `import { promiseWithResolver } from 'devframe/utils/promise'` | `Promise.withResolvers()` (native) |
124-
| `import { hash } from 'devframe/utils/hash'` | Any structural-hash library (e.g. `ohash`) |
125124
| `import { isQualifiedName, qualifyName } from 'devframe/utils/scope'` | Inline the check (`name.includes(':')`) |
126125

127-
The other `devframe/utils/*` helpers — `colors`, `open`, `launch-editor`, `nanoid`, `crypto-token`, `structured-clone`, `events`, `shared-state`, `streaming-channel`, `when`, `simple-schema`, `serve-static`, `agent-tool-name` — are unchanged.
126+
The other `devframe/utils/*` helpers — `colors`, `open`, `launch-editor`, `hash`, `nanoid`, `crypto-token`, `structured-clone`, `events`, `shared-state`, `streaming-channel`, `when`, `simple-schema`, `serve-static`, `agent-tool-name` — are unchanged.
128127

129128
## `devframe/node` is slimmed to the server-assembly surface
130129

131-
`devframe/node` keeps the API that hosts wiring up their own runtime actually use — `createHostContext`, `createH3DevframeHost`, `startHttpAndWs`, `createContextRpcServer`, `createStorage`, `registerDevframeInstance` / `listLiveDevframeInstances`, `DevframeAgentHost`, `coerceAgentPositionalArgs`, `isObject`, `normalizeHttpServerUrl`, and the `RpcFunctionsHost` / instance-record types.
130+
`devframe/node` keeps the API that hosts wiring up their own runtime actually use — `createHostContext`, `createH3DevframeHost`, `startHttpAndWs`, `createStorage`, `registerDevframeInstance` / `listLiveDevframeInstances`, `isObject`, `normalizeHttpServerUrl`, and the `RpcFunctionsHost` / instance-record types.
132131

133132
The internal host implementations and low-level factories are no longer exported:
134133

@@ -139,7 +138,17 @@ The internal host implementations and low-level factories are no longer exported
139138
| `createScopedNodeContext`, `createNodeSettings` | Internal to context assembly. |
140139
| `toDialableHost`, `formatHostForUrl` | Internal host-URL helpers. |
141140

142-
A host that binds its own transport composes from `createContextRpcServer` (`devframe/node`) plus `devframe/rpc/server`, `devframe/rpc/transports/*`, and `devframe/node/hub-internals` — the path `@devframes/hub`'s `initHub` and `@vitejs/devtools` both take.
141+
## Cross-package internals move to `devframe/internal`
142+
143+
The low-level primitives that only exist for the `devframe``@devframes/hub` boundary now live at the new `devframe/internal` entry point, which is explicitly **unstable** (it can change in any minor release). They were previously on `devframe/node`:
144+
145+
| Moved | From | To |
146+
|---|---|---|
147+
| `createContextRpcServer` (+ `ContextRpcServer`, `CreateContextRpcServerOptions`) | `devframe/node` | `devframe/internal` |
148+
| `DevframeAgentHost` (class) | `devframe/node` | `devframe/internal` |
149+
| `coerceAgentPositionalArgs` (+ `AgentArgsFallback`) | `devframe/node` | `devframe/internal` |
150+
151+
A host that binds its own transport composes from `createContextRpcServer` (`devframe/internal`) plus `devframe/rpc/server`, `devframe/rpc/transports/*`, and `devframe/node/hub-internals` — the path `@devframes/hub`'s `initHub` takes. Application code should prefer the adapters and `devframe/node`.
143152

144153
## `@devframes/hub` category order lives only on `/constants`
145154

docs/helpers/utilities.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ outline: deep
44

55
# Utilities
66

7-
Devframe ships a set of small, stable helpers under the `devframe/utils/*` subpaths. They cover the most common ancillary tasks a devtool needs — colorising terminal output, opening files in an editor, generating IDs and tokens — without forcing every author to pick (and install) their own library.
7+
Devframe ships a set of small, stable helpers under the `devframe/utils/*` subpaths. They cover the most common ancillary tasks a devtool needs — colorising terminal output, hashing arbitrary values, opening files in an editor — without forcing every author to pick (and install) their own library.
88

99
Each helper is bundled inside devframe. Importing from `devframe/utils/*` is enough — there's no separate `npm install` for these dependencies.
1010

@@ -48,6 +48,16 @@ launchEditor('src/main.ts:42:7', 'code')
4848

4949
The auto-detection reads the `LAUNCH_EDITOR` environment variable and falls back to common defaults. Most devframes consume this through the prebuilt `openInEditor` recipe — see [Common RPC Functions](./common-rpc-functions).
5050

51+
### `devframe/utils/hash`
52+
53+
Stable, deterministic hash of any structured-cloneable value. Useful for cache keys and dedup.
54+
55+
```ts
56+
import { hash } from 'devframe/utils/hash'
57+
58+
const key = hash({ functionName, args })
59+
```
60+
5161
### `devframe/utils/structured-clone`
5262

5363
JSON-safe serialization for the structured-clone algorithm — round-trips `Map`, `Set`, `Date`, `BigInt`, cycles, and class instances. Used internally by the RPC wire format; exposed for tools that need the same encoding.
@@ -127,6 +137,6 @@ Statically-validated when-clause expressions for conditional UI visibility. The
127137
The utilities are exposed as **stable wrappers over their underlying libraries** rather than bare re-exports. Two consequences:
128138

129139
- **One install.** Consumers do not list these libraries in their own `package.json`. Bundling them inside devframe means version drift across devtools is impossible.
130-
- **Swappable internals.** The wrapper signatures are deliberately narrower than upstream. Devframe can change the implementation (`ansis``picocolors`, …) without a breaking change to dependent devtools.
140+
- **Swappable internals.** The wrapper signatures are deliberately narrower than upstream. Devframe can change the implementation (`ansis``picocolors`, `ohash``crypto.subtle.digest`, …) without a breaking change to dependent devtools.
131141

132142
When you need a feature outside the wrapper's minimal surface, prefer extending the wrapper inside devframe over bypassing it.

knip.jsonc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
"src/adapters/{build,cac,dev,embedded,initiate}.ts",
7777
"src/adapters/mcp/index.ts",
7878
"src/client/index.ts",
79+
"src/internal/index.ts",
7980
"src/node/index.ts",
8081
"src/node/{auth,hub-internals}/index.ts",
8182
"src/recipes/{common-rpc-functions,interactive-auth}.ts",

packages/devframe/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"./constants": "./dist/constants.mjs",
3030
"./helpers/vite": "./dist/helpers/vite.mjs",
3131
"./initiate": "./dist/adapters/initiate.mjs",
32+
"./internal": "./dist/internal/index.mjs",
3233
"./node": "./dist/node/index.mjs",
3334
"./node/auth": "./dist/node/auth.mjs",
3435
"./node/hub-internals": "./dist/node/hub-internals.mjs",
@@ -46,6 +47,7 @@
4647
"./utils/colors": "./dist/utils/colors.mjs",
4748
"./utils/crypto-token": "./dist/utils/crypto-token.mjs",
4849
"./utils/events": "./dist/utils/events.mjs",
50+
"./utils/hash": "./dist/utils/hash.mjs",
4951
"./utils/launch-editor": "./dist/utils/launch-editor.mjs",
5052
"./utils/nanoid": "./dist/utils/nanoid.mjs",
5153
"./utils/open": "./dist/utils/open.mjs",

packages/devframe/src/client/static-rpc.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { DEVFRAME_RPC_DUMP_DIRNAME } from 'devframe/constants'
2+
import { hash } from 'devframe/utils/hash'
23
import { structuredCloneStringify } from 'devframe/utils/structured-clone'
34
import { describe, expect, it } from 'vitest'
4-
import { hash } from '../utils/hash'
55
import { createStaticRpcCaller } from './static-rpc'
66

77
const DEMO_STATIC_VERSION_PATH = `${DEVFRAME_RPC_DUMP_DIRNAME}/demo~version.static.json`

packages/devframe/src/client/static-rpc.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { RpcDumpRecordError } from '../rpc/types'
2+
import { hash } from 'devframe/utils/hash'
23
import { structuredCloneDeserialize } from 'devframe/utils/structured-clone'
34
import { reviveDumpError } from '../rpc/dump/error'
4-
import { hash } from '../utils/hash'
55

66
export type StaticRpcSerialization = 'json' | 'structured-clone'
77

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Internal cross-package surface: low-level host primitives shared between
2+
// `devframe` and `@devframes/hub` (and any first-party host built on the same
3+
// wiring). These are NOT part of the stable public API — they can change in any
4+
// minor release. Application code should use `devframe/node` and the adapters
5+
// instead.
6+
//
7+
// - `createContextRpcServer` — the transport-agnostic RPC core; a host that
8+
// binds its own transport (e.g. the hub's `initHub`) reuses the exact
9+
// session/auth wiring `startHttpAndWs` uses.
10+
// - `DevframeAgentHost` — the agent host implementation the hub composes into
11+
// its own commands host.
12+
// - `coerceAgentPositionalArgs` — positional-arg coercion the hub applies when
13+
// invoking agent tools as commands.
14+
export { coerceAgentPositionalArgs } from '../node/agent-args'
15+
export type { AgentArgsFallback } from '../node/agent-args'
16+
export { DevframeAgentHost } from '../node/host-agent'
17+
export { createContextRpcServer } from '../node/rpc-core'
18+
export type { ContextRpcServer, CreateContextRpcServerOptions } from '../node/rpc-core'
Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
// Node-side public API for hosts that wire up their own runtime — the
22
// server-assembly surface (`createHostContext` → `startHttpAndWs`), the
3-
// transport-agnostic RPC core (`createContextRpcServer`), the instance
4-
// registry, storage, and the two URL/host helpers consumers use.
3+
// instance registry, storage, and the two URL/host helpers consumers use.
54
//
65
// The diagnostics/services/views hosts, the streaming/shared-state/scope/
76
// settings factories, and the internal host helpers (`toDialableHost`,
87
// `formatHostForUrl`) stay internal. `toAgentToolName` lives at
98
// `devframe/utils/agent-tool-name` (a client-safe string transform).
109
//
11-
// `coerceAgentPositionalArgs`, the `DevframeAgentHost` class, and
12-
// `createContextRpcServer` stay public because `@devframes/hub` composes its
13-
// own commands/agent host and hand-rolled server on top of them — the exact
14-
// custom-transport path `createContextRpcServer` exists for.
15-
export { coerceAgentPositionalArgs } from './agent-args'
10+
// The low-level primitives shared only between `devframe` and `@devframes/hub`
11+
// — `createContextRpcServer`, `DevframeAgentHost`, `coerceAgentPositionalArgs`
12+
// — live at `devframe/internal` (an explicitly-unstable cross-package surface),
13+
// not here.
1614
export * from './context'
17-
export { DevframeAgentHost } from './host-agent'
1815
// `RpcFunctionsHostImpl` stays internal; expose only the structural
1916
// `RpcFunctionsHost` type so consumers can type/cast `ctx.rpc` without
2017
// pulling in the implementation's `@internal` members.
@@ -26,10 +23,6 @@ export * from './host-h3'
2623
// lower-level read/probe/prune helpers stay internal to the connector.
2724
export { listLiveDevframeInstances, registerDevframeInstance } from './instance-registry'
2825
export type { DevframeInstanceRecord, DevframeInstanceRegistration } from './instance-registry'
29-
// The transport-agnostic RPC core is public so hosts that bind their own
30-
// transports (a Bun fetch-upgrade route, a custom relay — e.g. `initHub`)
31-
// reuse the exact session/auth wiring `startHttpAndWs` uses.
32-
export * from './rpc-core'
3326
export * from './server'
3427
export * from './storage'
3528
export { isObject, normalizeHttpServerUrl } from './utils'

packages/devframe/src/rpc/cache.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { hash } from '../utils/hash'
1+
import { hash } from 'devframe/utils/hash'
22

33
export interface RpcCacheOptions {
44
functions: string[]

0 commit comments

Comments
 (0)