Skip to content

feat(glsp-client): ship the GLSP client classes for every host - #419

Merged
martin-fleck-at merged 1 commit into
mainfrom
feat/glsp-client-package
Oct 13, 2026
Merged

martin-fleck-at merged 1 commit into
mainfrom
feat/glsp-client-package

Conversation

@martin-fleck-at

@martin-fleck-at martin-fleck-at commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

The framework's GLSP client classes lived in glsp-client-theia, so a browser page or a VS Code webview got none of them: a failed load vanished as an uncaught rejection, sprotty's view-registration errors went to its default NullLogger, and only the Theia module repaired the routes calcRoute collapses while obstacles are unmeasured. This adds @hydranium/glsp-client, with no Theia dependency, moves the classes that are not Theia's by nature into it, and gives every host one binder. glsp-client-theia depends on it and binds through it; order-flow's browser page and VS Code webview call the same binder the Theia module calls.

What moves. HydraniumGlspActionDispatcher, HydraniumDiagramLoader and HydraniumHiddenBoundsUpdater inject their logger, tracer and clock through HydraniumClientTypes, typed by the protocol interfaces, instead of the Theia channel classes. HydraniumStatusOverlay, HydraniumGlspClient and the bind recorder move as they are. The loader's failure message is declared as DIAGRAM_LOAD_FAILED under ./messages; the neutral loader formats its English and the Theia subclass HydraniumGlspTheiaDiagramLoader renders it through nls under the existing key.

The binder. createGlspClientModule(context, { logger? }) rebinds the four classes and sprotty's TYPES.ILogger to SprottyLoggerAdapter, which forwards to the host's logger. It binds a protocol ConsoleLogger only when the host passed none and bound none, and a DefaultTracer over the bound logger and clock when the tracer is unbound. createGlspClientTheiaModule binds the three tokens from the Output channel first, with the per-class tag and a lazy bridge to client-theia's Clock, then calls the neutral binder and rebinds the loader to the Theia one.

Breaks for Theia adopters. The moved classes import from @hydranium/glsp-client, which glsp-client-theia lists as a peer. The three classes' channel field is now hydraniumLogger, typed Logger. @hydranium/glsp-client-theia/testing is gone; the bind recorder is @hydranium/glsp-client/testing. sprotty's TYPES.LogLevel no longer filters anything: the adapter reports sprotty's most verbose level and the framework threshold decides.

Decisions and their cost

  • Tokens in a registry, HydraniumClientTypes. As the server's HydraniumTypes, so a token's name cannot collide with the type it injects. The identifier helper stays private until an adopter mints a client token.
  • The clock token stays in client-theia. The protocol Clock carries no DI coupling by design; the Theia binder bridges the two tokens with one lazy binding, so a clock an adopter module binds later is still used.
  • The field is hydraniumLogger, not logger. GLSPActionDispatcher and GLSPHiddenBoundsUpdater already carry sprotty's logger: ILogger, which is why the field was channel; the new name says which logger it is on every host.
  • A console logger by default. A host that passes and binds no logger gets the protocol ConsoleLogger, which is loud; the classes themselves require the token, so a container that unbinds it fails at resolution rather than running silent.
  • sprotty's log goes to trace, and info to debug, with parameters reduced to their names. sprotty passes whole actions and models there, and the Theia channel logger stringifies every parameter as JSON, so forwarding them would write the model into the Output channel on every action; an action's kind or an object's class is what the line keeps, so Handle requestModel and Executing SetModelCommand still say what happened. sprotty's info is diagnosis, chatty on every hover's feedback, so the level is debug; the one anomaly reported there, a request GLSP resolved silently after its timeout, is raised to warn by matching GLSP's wording, degrading to debug if upstream rewords it. error and warn keep their parameters whole, where the parameter is the useful part.
  • The adapter tags each line with sprotty's calling class nested under the host's own tag, cached per class. sub, not for, since for replaces the host's component: the page's lines read [GLSP client :: Viewer] and Theia's [SprottyLoggerAdapter :: Viewer], so the origin is a tag the host renders as every other component, not a prefix inside the message. Where sprotty passes a sentence as the caller, as its layout registries do, the sentence prefixes the message instead of being dropped. A derivation per line was rejected for its allocation on every action; one per class costs a few dozen loggers, and log and info check the framework threshold before formatting a line at all. The adapter's test runs over a real AbstractLogger, since a double recording the name it was handed cannot tell a nested tag from a replaced one. The message GLSP sends an action in names the action too, so the outgoing half of the traffic reads as the incoming one.
  • Both binders are also tested by resolution in a real GLSP container. The recorder-based tests assert binding chains; a wrong token on one @inject would pass them and fail only on a host, so one test per package builds a container over GLSP's default modules and resolves the five tokens to the real classes, with the passed logger and the clock fallback checked on the dispatcher.
  • The browser page tags the client's lines apart from the head's. The page hands the binder a logger tagged GLSP client, beside the GLSP head tag its connection carries, so a load failure the page itself reports does not read as a line the worker sent.
  • The Theia binder binds its three tokens unguarded. As bindChannelLogger binds the channel logger; an adopter who binds one of the tokens does so after the module, as for every token it binds.
  • The Theia suite runs against the sibling's sources. Its built lib is CommonJS, so its require of @eclipse-glsp/client reaches the stylesheets through Node past the prebundle; the e2e tiers exercise the built artefact.
  • Two codes for one sentence. The neutral declaration is hydranium/glsp-client/diagram-load-failed; the Theia key stays hydranium/glsp-client-theia/diagram-load-failed, so each side's catalogue holds the code of the side that renders it. A test holds the two English texts equal.
  • The root is browser-bound, and the conventions now say so. The package carries no platform in its name because every GLSP host is a browser, and its root follows @eclipse-glsp/client's: it renders and loads stylesheets, so it needs a bundler with a CSS loader. The host-integration root rule in the conventions names it beside the Theia packages; moving the classes to a ./browser tier would leave an empty root.
  • ./messages loads no GLSP. DIAGRAM_LOAD_FAILED is declared in a module of its own rather than in the loader that raises it, so a bundle that only translates the code, such as a properties webview, needs no CSS loader. check:neutral gates the barrel for Node imports and does not look inside GLSP, so the claim rests on the module's imports.
  • A passed logger replaces a host binding. options.logger rebinds when the token is already bound, since two bindings on one token make every resolution ambiguous.
  • The Theia binder rebinds DiagramLoader twice. The neutral binder binds the neutral loader, the Theia binder replaces it; the same one-line move a browser adopter with a catalogue of their own makes.
  • The browser console test expects two lines. The dispatcher's model-initialized timing now reaches the page's console logger at info; the test asserts exactly those two lines rather than filtering them, so a framework line that stops arriving is seen too. The duration and timer id are matched loosely, since the tracer prints seconds past one second and numbers timers module-wide.

How I know it works

  • The new package's suite and the Theia package's suite pass, including the binder resolved in a real container and the Theia binder's per-class tag and lazy clock.
  • Red controls, each restored afterwards: rewording the Theia loader's English fails its agreement test; routing sprotty's log to debug fails the adapter test, as does routing the timeout notice to debug, dropping the guard on a prototype-less parameter deriving the caller's logger with for instead of sub, and formatting a line below the threshold; dropping the TYPES.ILogger rebind fails the binder test; always binding a passed logger fails the replacement test.
  • An unprimed review of the full diff ran before this revision; its findings are the last five Decisions above, the conventions' registry sentence naming both registries, the Theia compatibility probe firing on a change to the new package, and the neutral gate's comment ordering.
  • Requiring the built ./messages barrel in bare Node loads no GLSP module.
  • The browser e2e tier passes with the page on the binder; its console test failed first on the dispatcher's two new lines and passes with them asserted.
  • The Theia e2e tier passes with the configuration on the binder.
  • check:neutral, check:glob-coverage, check:exports, check:readme, check:readmes, check:deps, check:phantom-deps, check:nls-extract, check:protected-signatures, check:ast-grep, check:docs and the formatter pass locally, run after the new files were staged.

Follow-ups

The framework's GLSP client classes lived in glsp-client-theia, so a
browser page or a VS Code webview got none of them: a failed load
vanished as an uncaught rejection, sprotty's view-registration errors
went to its NullLogger, and only the Theia module repaired routes.

New package @hydranium/glsp-client, with no Theia dependency
- Move the action dispatcher, diagram loader, hidden-bounds updater,
  status overlay, GLSP client and bind recorder out of glsp-client-theia
- Inject the logger, tracer and clock through HydraniumClientTypes,
  typed by the protocol interfaces, so each host binds its own
- Declare the load failure as DIAGRAM_LOAD_FAILED in a module of its own,
  so ./messages loads no GLSP, and format its English in the neutral loader
- Add SprottyLoggerAdapter, so sprotty's errors reach the host's logger
  with the calling class nested under its tag; its chatter goes to trace
  and debug with parameters reduced to names, its silently resolved
  request timeout to warn
- Add createGlspClientModule, which rebinds the classes and sprotty's
  logger; a passed logger replaces a host binding, and a host that
  neither binds nor passes one gets the console
- Gate the three entries in check:neutral and the Node-global lint scope,
  compile the README snippet, run the Theia range probe on a change here,
  and name the browser-bound root and the token registry in the conventions
- Resolve the real classes through both binders in a GLSP container, so a
  wrong injection token fails the suite rather than a host

glsp-client-theia binds through it
- createGlspClientTheiaModule binds the tokens from the Output channel,
  keeping the per-class tag, before calling the neutral binder
- HydraniumGlspTheiaDiagramLoader renders the failure through nls under
  the existing key; a test holds its English equal to the declaration
- Drop the ./testing subpath, whose only content moved
- Alias the sibling to its sources under vitest, since its CommonJS lib
  requires GLSP's stylesheets through Node past the prebundle

Hosts
- The browser page and the VS Code webview call the binder; the Theia
  configuration drops its hand-rolled sprotty logger rebind
- The page tags the client's lines GLSP client, apart from the head's
- The browser console test expects the dispatcher's two model-initialized
  lines, which the page logger now carries

Breaks for Theia adopters: the moved classes import from the new
package, the classes' channel field is hydraniumLogger, and
@hydranium/glsp-client-theia/testing is gone.

Fixes #150
@martin-fleck-at
martin-fleck-at force-pushed the feat/glsp-client-package branch from 2428371 to 6f1b201 Compare October 13, 2026 16:02
@martin-fleck-at
martin-fleck-at merged commit a789d5e into main Oct 13, 2026
13 checks passed
@martin-fleck-at
martin-fleck-at deleted the feat/glsp-client-package branch October 13, 2026 16:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant