You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/errors/DF0066.md
+9-11Lines changed: 9 additions & 11 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,29 +10,27 @@ outline: deep
10
10
11
11
## Cause
12
12
13
-
Wire services are deduplicated by npm package name: the first installation wins, and later installs of the same package return the existing node API. Option sets from multiple installers only merge **before** the `ctx.services.ready()`barrier fires — an install that arrives after the service was constructed can no longer influence its configuration, so any options it carried are dropped with this warning.
13
+
Wire services are deduplicated by npm package name: the first installation wins, and later installs of the same package return the existing node API. Declared services are constructed once **before setup runs**, deep-merging every declarer's options. Calling `ctx.services.install()`for an already-constructed package — the dynamic escape hatch used after that point — can no longer influence its configuration, so any options it carries are dropped with this warning.
14
14
15
15
## Example
16
16
17
17
```ts
18
-
awaitctx.services.ready()
19
-
20
-
// ✗ The service is already constructed; { themes } is ignored.
Install the service (or declare it in `DevframeDefinition.services`) before the barrier — a host's explicit installs during setup/`configure` naturally run before the adapter fires `ready()`, so its options join the merge:
25
+
Declare the service so its options join the pre-setup merge — on the plugin's `DevframeDefinition.services`, or host-wide via `initHub({ services })`:
services: [createShikiService({ themes })], // ✓ merges before setup
30
+
devframes: [/* … */],
33
31
})
34
32
```
35
33
36
34
## Source
37
35
38
-
-[`packages/devframe/src/node/host-services.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/node/host-services.ts) — `install()`/the barrier flush warn when an already-installed package is installed again.
36
+
-[`packages/devframe/src/node/host-services.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/node/host-services.ts) — `installPackage` warns when an already-installed package is installed again.
Copy file name to clipboardExpand all lines: docs/errors/DF0067.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,7 +10,7 @@ outline: deep
10
10
11
11
## Cause
12
12
13
-
A service descriptor marked `required: true` names a package that could not be resolved and imported at the `ctx.services.ready()` barrier. Descriptors resolve against the declaring plugin's own dependencies first (then the workspace root), so this usually means the service package is missing from the declarer's `dependencies`, or isn't installed.
13
+
A service descriptor marked `required: true` names a package that could not be resolved and imported when services are constructed before setup. Descriptors resolve against the declaring plugin's own dependencies first (then the workspace root), so this usually means the service package is missing from the declarer's `dependencies`, or isn't installed.
14
14
15
15
Descriptors without `required` degrade instead: the missing service is skipped and clients observe `services.has(pkg) === false`.
16
16
@@ -19,7 +19,7 @@ Descriptors without `required` degrade instead: the missing service is skipped a
19
19
```ts
20
20
defineDevframe({
21
21
services: [
22
-
// ✗ Throws at the ready() barrier when the package isn't installed.
22
+
// ✗ Throws before setup when the package isn't installed.
@@ -31,4 +31,4 @@ Install the service package next to whoever declares it — a plugin declaring i
31
31
32
32
## Source
33
33
34
-
-[`packages/devframe/src/node/host-services.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/node/host-services.ts) — the barrier flush throws when a `required` descriptor's package fails to import.
34
+
-[`packages/devframe/src/node/host-services.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/node/host-services.ts) — the pre-setup construction throws when a `required` descriptor's package fails to import.
Copy file name to clipboardExpand all lines: docs/errors/DF0068.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,7 +10,7 @@ outline: deep
10
10
11
11
## Cause
12
12
13
-
A service descriptor marked `required: true` declares a `version` range, and the version of the service that actually resolved falls outside it. The range is checked at the `ctx.services.ready()` barrier against the resolved definition's own `version`.
13
+
A service descriptor marked `required: true` declares a `version` range, and the version of the service that actually resolved falls outside it. The range is checked when services are constructed before setup against the resolved definition's own `version`.
14
14
15
15
Without `required`, the same mismatch installs the service anyway and warns with [`DF0069`](/errors/DF0069).
16
16
@@ -31,4 +31,4 @@ Align the installed service package with the declared range (update whichever si
31
31
32
32
## Source
33
33
34
-
-[`packages/devframe/src/node/host-services.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/node/host-services.ts) — the barrier flush checks each descriptor's `version` range against the resolved definition.
34
+
-[`packages/devframe/src/node/host-services.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/node/host-services.ts) — the pre-setup construction checks each descriptor's `version` range against the resolved definition.
Copy file name to clipboardExpand all lines: docs/errors/DF0069.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -31,4 +31,4 @@ Align the installed service package with the declared range to silence the warni
31
31
32
32
## Source
33
33
34
-
-[`packages/devframe/src/node/host-services.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/node/host-services.ts) — the barrier flush checks each descriptor's `version` range against the resolved definition.
34
+
-[`packages/devframe/src/node/host-services.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/node/host-services.ts) — the pre-setup construction checks each descriptor's `version` range against the resolved definition.
Copy file name to clipboardExpand all lines: docs/errors/DF0070.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -33,4 +33,4 @@ A service package's default export must be its `create<X>Service` factory, retur
33
33
34
34
## Source
35
35
36
-
-[`packages/devframe/src/node/host-services.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/node/host-services.ts) — `install()` validates its input; the barrier flush validates imported factories and the definitions they return.
36
+
-[`packages/devframe/src/node/host-services.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/node/host-services.ts) — `install()` validates its input; the pre-setup construction validates imported factories and the definitions they return.
Copy file name to clipboardExpand all lines: docs/guide/services.md
+20-7Lines changed: 20 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -101,30 +101,37 @@ export default function createOpenService(options?: OpenServiceOptions): Devfram
101
101
102
102
Two declaration merges make it fully typed for consumers: the fully-qualified RPC ids go into `DevframeRpcServerFunctions`, and the package → scope mapping into `DevframeServicesScopeRegistry` (so a client's `services.get()` returns a scoped, typed RPC handle).
103
103
104
-
### Installing
104
+
### Declaring
105
105
106
-
A host with the factory at hand installs explicitly; a plugin declares what it consumes on its definition and the adapter resolves the package **against the plugin's own dependencies**— the base for that resolution is the definition's [`importMetaUrl`](./devframe-definition#resolving-against-the-plugins-own-dependencies), so a plugin ships a service package as its own dependency and users install nothing extra:
106
+
Services are **declarative**. A plugin lists what it consumes on its definition; a host lists shared ones on `initHub`. The adapter resolves each package — for a plugin, **against the plugin's own dependencies**via the definition's [`importMetaUrl`](./devframe-definition#resolving-against-the-plugins-own-dependencies), so a plugin ships a service package as its own dependency and users install nothing extra — and constructs it:
Entries are optional by default — a package that isn't installed is skipped and clients see `has() === false`. Mark an entry `required: true` to fail hard instead ([`DF0067`](https://devfra.me/errors/DF0067) on a missing package, [`DF0068`](https://devfra.me/errors/DF0068) on an unsatisfied `version` range; without it a range mismatch only warns with [`DF0069`](https://devfra.me/errors/DF0069)).
123
126
124
-
Installs queue until the adapter fires the `ctx.services.ready()` barrier after every devframe's setup has run. There each service is constructed **once**, with the option sets from every declarer merged — through the definition's `mergeOptions` when it declares one, otherwise shallow-merged in declaration order, so a host installing last wins. After the barrier, installing an already-installed package returns the existing API and warns ([`DF0066`](https://devfra.me/errors/DF0066)) when its options had to be ignored.
127
+
### Lifecycle: ready before setup
128
+
129
+
Services are constructed and made ready **before any `setup(ctx)` runs**. The hub collects every declared service (across all devframes plus `initHub`), constructs each **once** — deep-merging the option sets from every declarer (objects recurse, arrays union-dedupe, scalars take the later value; a service may override with its own `mergeOptions`) — and only then runs the setups. So `setup(ctx)` can consume a service synchronously via `ctx.services.get(pkg)`, including one another devframe declared.
125
130
126
131
Server-side consumers get the node API from the same registry — `ctx.services.get('@devframes/service-open')` or `whenAvailable` — with no RPC hop.
127
132
133
+
Declarative covers the common case. For a service whose configuration is only known at runtime, `ctx.services.install(input)` is the dynamic escape hatch: after the pre-setup construction it builds immediately; re-installing an already-constructed package returns the existing API and warns ([`DF0066`](https://devfra.me/errors/DF0066)) if it carried options that can no longer merge.
134
+
128
135
### Feature-detecting on the client
129
136
130
137
Installed services are advertised through the `devframe:services`[shared state](./shared-state); the client mirrors it on `rpc.services`:
@@ -144,6 +151,12 @@ state.on('updated', render)
144
151
145
152
`has()`/`get()`/`keys()` are synchronous snapshots of the advertisement — before the first sync lands they read as empty, and `get()` returns `undefined` rather than throwing, so the natural shape of consuming code is "render the fallback until the service appears". Each handle carries the advertised `version` and `meta` for finer gating.
146
153
154
+
### Built-in services
155
+
156
+
**`@devframes/service-open`** (`devframes:service:open`) opens files in the user's editor (`open-in-editor`, with optional `line`/`column`) or reveals them in the OS file explorer (`open-in-finder`). Paths may be absolute or relative to the workspace root (so a client with only a workspace-relative path — a message's file position, say — calls it directly); the service refuses anything outside the workspace root and the configured extra `roots` (`DS_OPEN_0002`), and gates editor commands to the `KNOWN_EDITORS` picklist. Options: `{ editor?, roots? }` — the preferred editor (later installer wins) and additional openable directories (merged as a union). It supersedes the per-plugin `devframe/recipes/common-rpc-functions` registrations, now deprecated.
157
+
158
+
**`@devframes/service-shiki`** (`devframes:service:shiki`) renders [Shiki](https://shiki.style) syntax highlighting on the server, so plugin bundles stop shipping grammars and themes. Three RPC queries — `highlight` (dual-theme HTML), `code-to-hast`, and `code-to-tokens` (for renderers that own their DOM, e.g. diff views) — all client-`cacheable` and LRU-cached server-side per `(code, lang, themes)`. Unknown languages degrade to plain text. Options: `{ themes?, langs? }` — the default light/dark pair (defaults `vitesse-light`/`vitesse-dark`, matching the design system; later installer wins) and languages to eagerly load (merged as a union).
159
+
147
160
## Services, RPC, or shared state?
148
161
149
162
Each mechanism covers a different direction of travel:
Copy file name to clipboardExpand all lines: docs/helpers/common-rpc-functions.md
+3Lines changed: 3 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,6 +4,9 @@ outline: deep
4
4
5
5
# Common RPC Functions
6
6
7
+
> [!WARNING]
8
+
> Deprecated in favor of the [`@devframes/service-open` wire service](/guide/services#built-in-services) — one host-level installation shared by every plugin, feature-detectable from clients, with workspace-root path containment on top of the editor gating. The recipe keeps working; removal lands in a future major.
9
+
7
10
Prebuilt RPC actions for the two file-system actions every CLI devtool needs — opening a file in the editor, revealing a path in the OS file explorer. Use the recipe instead of re-implementing them so every devframe converges on the same registered names and payload shape.
Copy file name to clipboardExpand all lines: docs/plugins/assets.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -95,12 +95,12 @@ All functions are namespaced `devframes:plugin:assets:*`:
95
95
|`list`|`query`, `snapshot: true`| Every file under the managed directory, with type, size, and last-modified time. |
96
96
|`capabilities`|`query`, `snapshot: true`| Whether write actions are enabled, and the upload allow-list — lets the UI gate itself proactively. |
97
97
|`read-image-meta`|`query`| Width, height, and orientation for an image asset. |
98
-
|`read-text`|`query`| Truncated text content, for preview. |
98
+
|`read-text`|`query`| Truncated text content, for preview. When the host advertises the [`@devframes/service-shiki` wire service](/guide/services#built-in-services), the panel renders it server-highlighted; otherwise it falls back to a plain `<pre>`. |
99
99
|`upload`|`action`| Allocates a streaming upload slot; the client pipes the file's bytes over the paired channel. |
100
100
|`rename`|`action`| Renames an asset within its folder, preserving its extension. |
101
101
|`delete`|`action`| Deletes one or more assets in a single call. |
102
102
|`mkdir`|`action`| Creates a folder, including missing parents. |
103
-
|`open-in-editor` / `reveal-in-folder`|`action`| Launch the asset in your editor, or reveal its containing folder in the OS file manager. Always registered, regardless of `write`. |
103
+
|`open-in-editor` / `reveal-in-folder`|`action`| Launch the asset in your editor, or reveal its containing folder in the OS file manager, delegating to the [`@devframes/service-open` wire service](/guide/services#built-in-services) (installed by the plugin with the managed dir as an allowed root). Always registered, regardless of `write`. |
104
104
105
105
`upload` / `rename` / `delete` / `mkdir` are registered only when `write` is enabled.
0 commit comments