feat: services - installable, client-advertised shared capabilities - #256
Merged
Conversation
Introduce the wire-service layer on top of the existing ctx.services registry: npm-packaged server-side capabilities (open-in-editor, shiki highlighting, …) that a host installs once and every plugin/client consumes without re-implementing or re-bundling them. - ServiceDefinition / ServiceDescriptor types, keyed by npm package name, with RPC functions namespaced under a service scope - ctx.services.install() + collect-then-setup ready() barrier: option sets from every declarer merge (mergeOptions or shallow, later wins) and each service constructs once; its node API is provided under the package name for in-process consumers - DevframeDefinition.services declarative list, resolved relative to the declaring plugin's own dependencies; required entries throw on missing package or unsatisfied version range, optional ones degrade silently - advertisement over the reactive devframe:services shared state; client.services.has()/get()/keys() with scoped, typed RPC handles - adapters (initiate/build/embedded/mcp) and the hub fire the barrier; a first-connection safety net covers hosts that forget - diagnostics DF0066–DF0071, tests, API snapshots
✅ Deploy Preview for devfra ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
- fold DevframeServiceSetupInfo / DevframeServiceInstallOptions into inline types; drop isReady and the scoped-context services passthrough - move installDefinitionServices to devframe/internal (unstable surface) and keep createDevframeServicesClient internal to the client - docs: wire-services guide section, client + definition-field mentions, error pages DF0066–DF0071
install()'s resolveFrom now also accepts an npm package name (the declaring plugin's packageName), expanded to that package's location so its declared services resolve against the plugin's own dependencies — adapters and the hub queue def.services with a plain loop, and the installDefinitionServices helper (whose awaited form could deadlock pre-barrier) is gone.
antfubot
added a commit
to vitejs/devtools
that referenced
this pull request
Aug 18, 2026
v0.9.1's Services feature (devframes/devframe#256) has the client read the devframe:services shared state on load. A static snapshot doesn't stand up the live hub, so nothing fired the services barrier that publishes that state — leaving the RPC dump without a match and the client logging a hard error. Fire context.services.ready() before collecting the dump (idempotent; publishes an empty state when no services are installed), mirroring the dock-renderers seeding.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Introduces the wire-service layer on top of the existing node-side
ctx.servicesregistry: npm-packaged, server-side capabilities (open-in-editor, shiki highlighting, …) that a host installs once and every plugin and browser client consumes — no per-plugin re-implementation or re-bundling, and client UIs can feature-detect a service and degrade gracefully (hide the "open in editor" button, render a plain<pre>, …).This is PR 1 of the series designed with the maintainer: core mechanism here;
@devframes/service-open+@devframes/service-shikiand the plugin migrations follow.How it works
Definition & identity — a service ships as an npm package whose default export is a
create<X>Servicefactory returning aDevframeServiceDefinition { package, version, scope, meta?, options?, mergeOptions?, setup }. The registry key is the npm package name; its RPC functions live under the definition'sscopenamespace (devframes:service:<slug>:<fn>), andsetupreceives a pre-scoped context and returns the service's node API (provided under the package name, so server-side consumers skip the RPC hop).Install & the ready() barrier —
ctx.services.install()accepts a ready definition or a declarative descriptor{ package, version?, required?, options? };DevframeDefinition.servicesdeclares them per plugin, resolved against the declaring plugin's own dependencies. Installs queue until the collect-then-setup barrierctx.services.ready()(fired by every adapter — initiate/build/embedded/MCP — and byinitHubafter configure/UI setup, with a first-connection safety net): option sets from every declarer are merged (mergeOptionsor shallow, later wins) and each service is constructed exactly once. Missing or version-mismatched services throw whenrequired, otherwise degrade with a debug skip / warning.Client discovery — installed services are advertised on the reactive
devframe:servicesshared state. The client gainsrpc.serviceswith synchronoushas()/get()/keys()(plus the state itself for reactive UI);get()returns the advertisement meta and a scoped, typed RPC handle, typed via the newDevframeServicesScopeRegistrydeclaration-merge point.Diagnostics — new coded diagnostics
DF0066–DF0071(duplicate install, required import failure, required/optional version-range mismatch, invalid definition, deferred-flush failure).Public surface (kept minimal)
DevframeServicesHostgainsinstall()+ready()only; option/info bags are inline typesDevframeServicesScopeRegistrymerge point — nothing elseinstallDefinitionServiceslives ondevframe/internal(the explicitly-unstable surface); the client factory is internal, only its types are exportedDocs
guide/services.md— new Wire services section (shipping, installing, option merging, client feature-detection)guide/client.md+guide/devframe-definition.md—rpc.services/servicesfield mentionserrors/DF0066.md–DF0071.mdreference pagesValidation
pnpm lint && pnpm knip && pnpm test && pnpm typecheck && pnpm buildall green (1190 tests, 106 files)Created with the help of an agent.