feat(extension): make defineExtension single-use, like the host it serves - #48
Merged
Conversation
…rves A second `activate` on one `defineExtension` result used to build a second application from scratch: a new log channel VS Code never saw closed, new registrations the first application's failsafe knew nothing about, and the first application left behind, still owning what it had registered. The host underneath is single-flight and single-use -- a start in flight is joined, and a stopped or failed host refuses to start again -- but the facade was hiding that by starting over. Now the facade keeps one application. A second `activate` while the first is starting or running joins it and resolves to the same value; after `deactivate`, or after a start that failed, it rejects with a `FrameworkError` of kind `activation` (code `EXTENSION_NOT_RESTARTABLE`, `details.state` naming the host state) rather than rebuilding anything. The host's own `InvalidHostStateError` stays internal; the facade translates it into the vocabulary a consumer already handles. VS Code activates an extension once per session, so nothing changes in the editor. The change is visible only to a test that activates one definition twice, which the guide already steers away from: build one per test, or run the plan through `createTestHost`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Merged
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.
Summary
defineExtensionnow describes one application, and behaves like it.A second
activateon the same result used to build a second application from scratch — a new log channel VS Code never saw closed, new registrations the first application's failsafe knew nothing about, and the first application left behind still owning what it had registered. The host underneath has always been single-flight and single-use; the facade hid that by starting over.Now:
activatearrives while…deactivateis pending, or after it settledFrameworkError, kindactivation, codeEXTENSION_NOT_RESTARTABLE,details.state='stopping'/'stopped'details.state='failed'The refusal never opens a channel or touches VS Code. The host's own
InvalidHostStateErrorstays internal; the facade translates it into the error type consumers already handle.Compatibility
This is a behaviour change, and it is listed under Changed.
It cannot be observed in the editor: VS Code activates an extension once per session, and a reload re-imports the entry module, which yields a fresh definition. It can be observed in a test that activates one
defineExtensionresult twice across cases — that test now fails on the second activation with a clear code instead of passing against a second, hidden application. The guide's advice already covers it: build one definition per test, or run the plan throughcreateTestHost, which is what it exists for.A published extension built on this package activates once in
beforeAlland deactivates once inafterAll; its type-check, lint and 271 tests pass against this build unchanged.Verification
npm run quality(1063 tests) andnpm run verify:packagepass.Five new cases in the composition-root suite cover the four rows above and the failed-start case, asserting the value identity on the joined and running paths, the error kind, code and state on the refusing paths, and that exactly one log channel ever existed.
🤖 Generated with Claude Code