@hydranium/glsp-server 1.0.0-next.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +97 -0
- package/lib/browser/index.d.ts +10 -0
- package/lib/browser/index.d.ts.map +1 -0
- package/lib/browser/index.js +15 -0
- package/lib/browser/index.js.map +1 -0
- package/lib/browser/start-glsp-server-in-worker.d.ts +121 -0
- package/lib/browser/start-glsp-server-in-worker.d.ts.map +1 -0
- package/lib/browser/start-glsp-server-in-worker.js +102 -0
- package/lib/browser/start-glsp-server-in-worker.js.map +1 -0
- package/lib/command/hydranium-glsp-recording-command.d.ts +132 -0
- package/lib/command/hydranium-glsp-recording-command.d.ts.map +1 -0
- package/lib/command/hydranium-glsp-recording-command.js +159 -0
- package/lib/command/hydranium-glsp-recording-command.js.map +1 -0
- package/lib/command/index.d.ts +10 -0
- package/lib/command/index.d.ts.map +1 -0
- package/lib/command/index.js +10 -0
- package/lib/command/index.js.map +1 -0
- package/lib/computed-bounds/hydranium-glsp-computed-bounds-action-handler.d.ts +61 -0
- package/lib/computed-bounds/hydranium-glsp-computed-bounds-action-handler.d.ts.map +1 -0
- package/lib/computed-bounds/hydranium-glsp-computed-bounds-action-handler.js +93 -0
- package/lib/computed-bounds/hydranium-glsp-computed-bounds-action-handler.js.map +1 -0
- package/lib/computed-bounds/index.d.ts +10 -0
- package/lib/computed-bounds/index.d.ts.map +1 -0
- package/lib/computed-bounds/index.js +10 -0
- package/lib/computed-bounds/index.js.map +1 -0
- package/lib/dispatcher/index.d.ts +10 -0
- package/lib/dispatcher/index.d.ts.map +1 -0
- package/lib/dispatcher/index.js +10 -0
- package/lib/dispatcher/index.js.map +1 -0
- package/lib/dispatcher/server-action-dispatcher.d.ts +52 -0
- package/lib/dispatcher/server-action-dispatcher.d.ts.map +1 -0
- package/lib/dispatcher/server-action-dispatcher.js +79 -0
- package/lib/dispatcher/server-action-dispatcher.js.map +1 -0
- package/lib/index.d.ts +19 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +20 -0
- package/lib/index.js.map +1 -0
- package/lib/launcher/abstract-hydranium-glsp-diagram-module.d.ts +75 -0
- package/lib/launcher/abstract-hydranium-glsp-diagram-module.d.ts.map +1 -0
- package/lib/launcher/abstract-hydranium-glsp-diagram-module.js +100 -0
- package/lib/launcher/abstract-hydranium-glsp-diagram-module.js.map +1 -0
- package/lib/launcher/glsp-app-module.d.ts +87 -0
- package/lib/launcher/glsp-app-module.d.ts.map +1 -0
- package/lib/launcher/glsp-app-module.js +71 -0
- package/lib/launcher/glsp-app-module.js.map +1 -0
- package/lib/launcher/glsp-framework-overrides.d.ts +32 -0
- package/lib/launcher/glsp-framework-overrides.d.ts.map +1 -0
- package/lib/launcher/glsp-framework-overrides.js +49 -0
- package/lib/launcher/glsp-framework-overrides.js.map +1 -0
- package/lib/launcher/glsp-server-overrides.d.ts +39 -0
- package/lib/launcher/glsp-server-overrides.d.ts.map +1 -0
- package/lib/launcher/glsp-server-overrides.js +45 -0
- package/lib/launcher/glsp-server-overrides.js.map +1 -0
- package/lib/launcher/hydranium-glsp-server.d.ts +62 -0
- package/lib/launcher/hydranium-glsp-server.d.ts.map +1 -0
- package/lib/launcher/hydranium-glsp-server.js +101 -0
- package/lib/launcher/hydranium-glsp-server.js.map +1 -0
- package/lib/launcher/index.d.ts +12 -0
- package/lib/launcher/index.d.ts.map +1 -0
- package/lib/launcher/index.js +22 -0
- package/lib/launcher/index.js.map +1 -0
- package/lib/logging/glsp-client-logger.d.ts +110 -0
- package/lib/logging/glsp-client-logger.d.ts.map +1 -0
- package/lib/logging/glsp-client-logger.js +151 -0
- package/lib/logging/glsp-client-logger.js.map +1 -0
- package/lib/logging/index.d.ts +10 -0
- package/lib/logging/index.d.ts.map +1 -0
- package/lib/logging/index.js +10 -0
- package/lib/logging/index.js.map +1 -0
- package/lib/node/index.d.ts +10 -0
- package/lib/node/index.d.ts.map +1 -0
- package/lib/node/index.js +13 -0
- package/lib/node/index.js.map +1 -0
- package/lib/node/start-glsp-server.d.ts +122 -0
- package/lib/node/start-glsp-server.d.ts.map +1 -0
- package/lib/node/start-glsp-server.js +103 -0
- package/lib/node/start-glsp-server.js.map +1 -0
- package/lib/state/abstract-hydranium-glsp-state.d.ts +333 -0
- package/lib/state/abstract-hydranium-glsp-state.d.ts.map +1 -0
- package/lib/state/abstract-hydranium-glsp-state.js +451 -0
- package/lib/state/abstract-hydranium-glsp-state.js.map +1 -0
- package/lib/state/full-text-hydranium-glsp-state.d.ts +43 -0
- package/lib/state/full-text-hydranium-glsp-state.d.ts.map +1 -0
- package/lib/state/full-text-hydranium-glsp-state.js +60 -0
- package/lib/state/full-text-hydranium-glsp-state.js.map +1 -0
- package/lib/state/hydranium-glsp-index.d.ts +220 -0
- package/lib/state/hydranium-glsp-index.d.ts.map +1 -0
- package/lib/state/hydranium-glsp-index.js +320 -0
- package/lib/state/hydranium-glsp-index.js.map +1 -0
- package/lib/state/hydranium-shared-core-services.d.ts +90 -0
- package/lib/state/hydranium-shared-core-services.d.ts.map +1 -0
- package/lib/state/hydranium-shared-core-services.js +89 -0
- package/lib/state/hydranium-shared-core-services.js.map +1 -0
- package/lib/state/index.d.ts +17 -0
- package/lib/state/index.d.ts.map +1 -0
- package/lib/state/index.js +17 -0
- package/lib/state/index.js.map +1 -0
- package/lib/state/model-ready-timeout-error.d.ts +28 -0
- package/lib/state/model-ready-timeout-error.d.ts.map +1 -0
- package/lib/state/model-ready-timeout-error.js +36 -0
- package/lib/state/model-ready-timeout-error.js.map +1 -0
- package/lib/state/reconcile-source-model-write.d.ts +43 -0
- package/lib/state/reconcile-source-model-write.d.ts.map +1 -0
- package/lib/state/reconcile-source-model-write.js +47 -0
- package/lib/state/reconcile-source-model-write.js.map +1 -0
- package/lib/state/reconciling-multi-document-glsp-state.d.ts +164 -0
- package/lib/state/reconciling-multi-document-glsp-state.d.ts.map +1 -0
- package/lib/state/reconciling-multi-document-glsp-state.js +222 -0
- package/lib/state/reconciling-multi-document-glsp-state.js.map +1 -0
- package/lib/state/reconciling-transfer-hydranium-glsp-state.d.ts +91 -0
- package/lib/state/reconciling-transfer-hydranium-glsp-state.d.ts.map +1 -0
- package/lib/state/reconciling-transfer-hydranium-glsp-state.js +129 -0
- package/lib/state/reconciling-transfer-hydranium-glsp-state.js.map +1 -0
- package/lib/storage/hydranium-glsp-storage.d.ts +348 -0
- package/lib/storage/hydranium-glsp-storage.d.ts.map +1 -0
- package/lib/storage/hydranium-glsp-storage.js +634 -0
- package/lib/storage/hydranium-glsp-storage.js.map +1 -0
- package/lib/storage/index.d.ts +11 -0
- package/lib/storage/index.d.ts.map +1 -0
- package/lib/storage/index.js +11 -0
- package/lib/storage/index.js.map +1 -0
- package/lib/storage/save-conflict-policy.d.ts +56 -0
- package/lib/storage/save-conflict-policy.d.ts.map +1 -0
- package/lib/storage/save-conflict-policy.js +19 -0
- package/lib/storage/save-conflict-policy.js.map +1 -0
- package/lib/submission/hydranium-glsp-submission-handler.d.ts +109 -0
- package/lib/submission/hydranium-glsp-submission-handler.d.ts.map +1 -0
- package/lib/submission/hydranium-glsp-submission-handler.js +157 -0
- package/lib/submission/hydranium-glsp-submission-handler.js.map +1 -0
- package/lib/submission/index.d.ts +10 -0
- package/lib/submission/index.d.ts.map +1 -0
- package/lib/submission/index.js +10 -0
- package/lib/submission/index.js.map +1 -0
- package/lib/testing/glsp-harness.d.ts +170 -0
- package/lib/testing/glsp-harness.d.ts.map +1 -0
- package/lib/testing/glsp-harness.js +231 -0
- package/lib/testing/glsp-harness.js.map +1 -0
- package/lib/testing/index.d.ts +11 -0
- package/lib/testing/index.d.ts.map +1 -0
- package/lib/testing/index.js +13 -0
- package/lib/testing/index.js.map +1 -0
- package/lib/testing/make-noop-glsp-logger.d.ts +42 -0
- package/lib/testing/make-noop-glsp-logger.d.ts.map +1 -0
- package/lib/testing/make-noop-glsp-logger.js +47 -0
- package/lib/testing/make-noop-glsp-logger.js.map +1 -0
- package/lib/util/index.d.ts +10 -0
- package/lib/util/index.d.ts.map +1 -0
- package/lib/util/index.js +10 -0
- package/lib/util/index.js.map +1 -0
- package/lib/util/service-identifier.d.ts +46 -0
- package/lib/util/service-identifier.d.ts.map +1 -0
- package/lib/util/service-identifier.js +47 -0
- package/lib/util/service-identifier.js.map +1 -0
- package/lib/validation/diagnostic-markers.d.ts +48 -0
- package/lib/validation/diagnostic-markers.d.ts.map +1 -0
- package/lib/validation/diagnostic-markers.js +68 -0
- package/lib/validation/diagnostic-markers.js.map +1 -0
- package/lib/validation/hydranium-glsp-model-validator.d.ts +51 -0
- package/lib/validation/hydranium-glsp-model-validator.d.ts.map +1 -0
- package/lib/validation/hydranium-glsp-model-validator.js +118 -0
- package/lib/validation/hydranium-glsp-model-validator.js.map +1 -0
- package/lib/validation/index.d.ts +11 -0
- package/lib/validation/index.d.ts.map +1 -0
- package/lib/validation/index.js +11 -0
- package/lib/validation/index.js.map +1 -0
- package/package.json +111 -0
- package/src/browser/index.ts +15 -0
- package/src/browser/start-glsp-server-in-worker.ts +202 -0
- package/src/command/hydranium-glsp-recording-command.ts +183 -0
- package/src/command/index.ts +10 -0
- package/src/computed-bounds/hydranium-glsp-computed-bounds-action-handler.ts +103 -0
- package/src/computed-bounds/index.ts +10 -0
- package/src/dispatcher/index.ts +10 -0
- package/src/dispatcher/server-action-dispatcher.ts +72 -0
- package/src/index.ts +20 -0
- package/src/launcher/abstract-hydranium-glsp-diagram-module.ts +117 -0
- package/src/launcher/glsp-app-module.ts +106 -0
- package/src/launcher/glsp-framework-overrides.ts +51 -0
- package/src/launcher/glsp-server-overrides.ts +46 -0
- package/src/launcher/hydranium-glsp-server.ts +104 -0
- package/src/launcher/index.ts +22 -0
- package/src/logging/glsp-client-logger.ts +184 -0
- package/src/logging/index.ts +10 -0
- package/src/node/index.ts +13 -0
- package/src/node/start-glsp-server.ts +214 -0
- package/src/state/abstract-hydranium-glsp-state.ts +503 -0
- package/src/state/full-text-hydranium-glsp-state.ts +63 -0
- package/src/state/hydranium-glsp-index.ts +337 -0
- package/src/state/hydranium-shared-core-services.ts +92 -0
- package/src/state/index.ts +17 -0
- package/src/state/model-ready-timeout-error.ts +33 -0
- package/src/state/reconcile-source-model-write.ts +77 -0
- package/src/state/reconciling-multi-document-glsp-state.ts +248 -0
- package/src/state/reconciling-transfer-hydranium-glsp-state.ts +132 -0
- package/src/storage/hydranium-glsp-storage.ts +660 -0
- package/src/storage/index.ts +11 -0
- package/src/storage/save-conflict-policy.ts +52 -0
- package/src/submission/hydranium-glsp-submission-handler.ts +170 -0
- package/src/submission/index.ts +10 -0
- package/src/testing/glsp-harness.ts +422 -0
- package/src/testing/index.ts +14 -0
- package/src/testing/make-noop-glsp-logger.ts +66 -0
- package/src/util/index.ts +10 -0
- package/src/util/service-identifier.ts +49 -0
- package/src/validation/diagnostic-markers.ts +93 -0
- package/src/validation/hydranium-glsp-model-validator.ts +105 -0
- package/src/validation/index.ts +11 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import { Logger as GlspLogger, LoggerFactory, getRequestParentName } from '@eclipse-glsp/server';
|
|
11
|
+
import { ContainerModule } from 'inversify';
|
|
12
|
+
import type { ServerSharedServices } from '@hydranium/core';
|
|
13
|
+
import { HydraniumTypes } from '../state/hydranium-shared-core-services.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The framework bindings layered on top of GLSP's own app module, shared by
|
|
17
|
+
* every bringup regardless of transport.
|
|
18
|
+
*
|
|
19
|
+
* Two things happen here. GLSP's `Logger` / `LoggerFactory` are replaced with
|
|
20
|
+
* the adopter's — unbind-then-bind, the same pattern `createAppModule` uses
|
|
21
|
+
* internally — so GLSP server logs leave through the adopter's sink rather than
|
|
22
|
+
* a console the host may not own. And `HydraniumTypes.Tracer` is added,
|
|
23
|
+
* caller-tagged the same way the logger is, so an injected tracer's timing lines
|
|
24
|
+
* carry the requesting class's component without every call site naming itself.
|
|
25
|
+
*
|
|
26
|
+
* **Shared rather than written once per launcher, because the two would drift
|
|
27
|
+
* silently.** A transport-specific copy that fell behind would still bind
|
|
28
|
+
* something for each symbol, so nothing would fail — the head would merely log
|
|
29
|
+
* or trace differently depending on how it was started, which no test asks
|
|
30
|
+
* about.
|
|
31
|
+
*
|
|
32
|
+
* `createLogger` is called once per Inversify resolution, with `caller` set to
|
|
33
|
+
* the requesting parent's class name, and once per `LoggerFactory` invocation.
|
|
34
|
+
*/
|
|
35
|
+
export function createGlspFrameworkOverrides(createLogger: (caller?: string) => GlspLogger): ContainerModule {
|
|
36
|
+
return new ContainerModule((bind, unbind, isBound) => {
|
|
37
|
+
if (isBound(GlspLogger)) {
|
|
38
|
+
unbind(GlspLogger);
|
|
39
|
+
}
|
|
40
|
+
if (isBound(LoggerFactory)) {
|
|
41
|
+
unbind(LoggerFactory);
|
|
42
|
+
}
|
|
43
|
+
bind(GlspLogger).toDynamicValue(ctx => createLogger(getRequestParentName(ctx)));
|
|
44
|
+
bind(LoggerFactory).toFactory(() => (caller: string) => createLogger(caller));
|
|
45
|
+
bind(HydraniumTypes.Tracer).toDynamicValue(ctx => {
|
|
46
|
+
const tracer = ctx.container.get<ServerSharedServices>(HydraniumTypes.SharedCoreServices).Tracer;
|
|
47
|
+
const caller = getRequestParentName(ctx);
|
|
48
|
+
return caller ? tracer.for(caller) : tracer;
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import { GLSPServer } from '@eclipse-glsp/server';
|
|
11
|
+
import { ContainerModule } from 'inversify';
|
|
12
|
+
import { HydraniumGlspServer } from './hydranium-glsp-server.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The framework bindings layered on top of the adopter's `ServerModule`, in the
|
|
16
|
+
* SERVER container — one per connected application, a child of the app
|
|
17
|
+
* container every bringup composes.
|
|
18
|
+
*
|
|
19
|
+
* **The tier matters, and the app container cannot stand in for it.** GLSP's
|
|
20
|
+
* launcher creates the server container as a child and loads the adopter's
|
|
21
|
+
* `ServerModule` into it, so a `GLSPServer` binding made further up is shadowed
|
|
22
|
+
* rather than consulted, and inversify refuses to rebind a parent's binding
|
|
23
|
+
* from a child at all. The framework's app-tier overrides therefore cannot
|
|
24
|
+
* reach this symbol.
|
|
25
|
+
*
|
|
26
|
+
* **Load this AFTER the adopter's `ServerModule`, never beside it.** Both bind
|
|
27
|
+
* `GLSPServer`, so a plain second binding resolves to an ambiguous-match error
|
|
28
|
+
* rather than to either one; `rebind` needs the first binding to already be
|
|
29
|
+
* present.
|
|
30
|
+
*
|
|
31
|
+
* Keeping this out of the adopter's hands is the point: an adopter passes their
|
|
32
|
+
* own `ServerModule` in, so anything they must add themselves is a fix the
|
|
33
|
+
* adopters who most need it will not have.
|
|
34
|
+
*
|
|
35
|
+
* **It also decides what GLSP's own log lines are tagged with.** GLSP resolves
|
|
36
|
+
* a logger per injecting class and labels it with the bound implementation's
|
|
37
|
+
* name, so every line upstream's server logs carries
|
|
38
|
+
* {@link HydraniumGlspServer} rather than `DefaultGLSPServer`. Anything keyed
|
|
39
|
+
* on that label — a log filter, an assertion — reads the class bound here, and
|
|
40
|
+
* a further adopter subclass moves it again.
|
|
41
|
+
*/
|
|
42
|
+
export function createGlspServerOverrides(): ContainerModule {
|
|
43
|
+
return new ContainerModule((_bind, _unbind, _isBound, rebind) => {
|
|
44
|
+
rebind(GLSPServer).to(HydraniumGlspServer).inSingletonScope();
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
type ClientSession,
|
|
12
|
+
DefaultGLSPServer,
|
|
13
|
+
GLSPServerError,
|
|
14
|
+
RejectAction,
|
|
15
|
+
type RequestAction,
|
|
16
|
+
type ResponseAction
|
|
17
|
+
} from '@eclipse-glsp/server';
|
|
18
|
+
import { injectable } from 'inversify';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The GLSP server the framework binds in place of upstream's
|
|
22
|
+
* {@link DefaultGLSPServer}, so a failed REQUEST reaches its reader with the
|
|
23
|
+
* text the thrower wrote.
|
|
24
|
+
*
|
|
25
|
+
* **What breaks without it.** Upstream projects a failing request's detail as
|
|
26
|
+
* `error.cause?.toString()` whenever the error is a {@link GLSPServerError},
|
|
27
|
+
* and that one value feeds both the server log and the client's
|
|
28
|
+
* {@link RejectAction}. A {@link GLSPServerError} carrying no `cause` therefore
|
|
29
|
+
* reaches no reader at all — not the toast, not the details pane, not the log —
|
|
30
|
+
* while a plain `Error` survives, because the other branch reads the error
|
|
31
|
+
* itself. The typed error is the trap, and it is the one upstream's own
|
|
32
|
+
* `getOrThrow` helper raises: that helper's signature cannot pass a `cause`, so
|
|
33
|
+
* an adopter using the sanctioned helper has no fix available on their side.
|
|
34
|
+
* Restating the cause at each throw site is the alternative, and it costs
|
|
35
|
+
* every adopter a rule they must know and cannot apply to `getOrThrow`.
|
|
36
|
+
*
|
|
37
|
+
* Only the REQUEST path needs this. Non-request actions fail through
|
|
38
|
+
* upstream's `handleProcessError`, which reads `message` directly.
|
|
39
|
+
*
|
|
40
|
+
* Adopters who bind their own GLSP server should extend this class rather than
|
|
41
|
+
* {@link DefaultGLSPServer}; the framework's server-container override replaces
|
|
42
|
+
* whatever the adopter's `ServerModule` bound, so a subclass of upstream's
|
|
43
|
+
* server would be discarded.
|
|
44
|
+
*/
|
|
45
|
+
@injectable()
|
|
46
|
+
export class HydraniumGlspServer extends DefaultGLSPServer {
|
|
47
|
+
/**
|
|
48
|
+
* The detail projected into the client's {@link RejectAction} and the server
|
|
49
|
+
* log when a request fails.
|
|
50
|
+
*
|
|
51
|
+
* A {@link GLSPServerError} with no `cause` falls back to its `message`; a
|
|
52
|
+
* nullish `cause` counts as absent, matching the optional chaining upstream
|
|
53
|
+
* applies. Anything else stringifies as upstream does, so a plain `Error`
|
|
54
|
+
* keeps its `"Error: …"` prefix and callers that match on it still match.
|
|
55
|
+
*/
|
|
56
|
+
protected requestFailureDetail(error: unknown): string | undefined {
|
|
57
|
+
if (error instanceof GLSPServerError) {
|
|
58
|
+
return error.cause === undefined || error.cause === null ? error.message : String(error.cause);
|
|
59
|
+
}
|
|
60
|
+
return error === undefined || error === null ? undefined : String(error);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Lifted verbatim from `@eclipse-glsp/server` 2.7.0's
|
|
65
|
+
* {@link DefaultGLSPServer.handleClientRequest}, with the detail computation
|
|
66
|
+
* delegated to {@link requestFailureDetail}. Nothing else here — the
|
|
67
|
+
* timeout branch, the response dispatch, the nested send-failure handling —
|
|
68
|
+
* relates to that fallback.
|
|
69
|
+
*
|
|
70
|
+
* **This body does not track upstream.** A release that changes request
|
|
71
|
+
* handling would silently not apply, on the path every request takes. There
|
|
72
|
+
* is no narrower seam to override: upstream computes the detail inline. The
|
|
73
|
+
* accompanying test pins upstream's own source for this method, so a bump
|
|
74
|
+
* that changes it fails rather than diverging quietly; re-lift the body when
|
|
75
|
+
* it does.
|
|
76
|
+
*/
|
|
77
|
+
protected override async handleClientRequest(
|
|
78
|
+
clientSession: ClientSession,
|
|
79
|
+
action: RequestAction<ResponseAction>,
|
|
80
|
+
clientId: string
|
|
81
|
+
): Promise<void> {
|
|
82
|
+
try {
|
|
83
|
+
const response =
|
|
84
|
+
action.timeout !== undefined
|
|
85
|
+
? await clientSession.actionDispatcher.requestUntil(action, action.timeout, true)
|
|
86
|
+
: await clientSession.actionDispatcher.request(action);
|
|
87
|
+
if (response) {
|
|
88
|
+
this.sendResponseToClient(clientId, response);
|
|
89
|
+
}
|
|
90
|
+
} catch (error: unknown) {
|
|
91
|
+
const detail = this.requestFailureDetail(error);
|
|
92
|
+
this.logger.error(`Failed to handle request '${action.kind}' (${action.requestId}):`, detail);
|
|
93
|
+
try {
|
|
94
|
+
const reject = RejectAction.create(`Failed to handle request '${action.kind}' (${action.requestId})`, {
|
|
95
|
+
responseId: action.requestId,
|
|
96
|
+
detail
|
|
97
|
+
});
|
|
98
|
+
this.sendResponseToClient(clientId, reject);
|
|
99
|
+
} catch (sendError: unknown) {
|
|
100
|
+
this.logger.error(`Failed to send rejection for request '${action.requestId}':`, sendError);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
export * from './glsp-app-module.js';
|
|
11
|
+
export * from './abstract-hydranium-glsp-diagram-module.js';
|
|
12
|
+
// Exported so an adopter binding their own GLSP server extends the framework's
|
|
13
|
+
// rather than upstream's — the server-container override discards a subclass of
|
|
14
|
+
// `DefaultGLSPServer`.
|
|
15
|
+
export * from './hydranium-glsp-server.js';
|
|
16
|
+
// The socket bringup lives in `@hydranium/glsp-server/node` and the worker
|
|
17
|
+
// bringup in `@hydranium/glsp-server/browser`, not here: each pulls the
|
|
18
|
+
// upstream build for its platform, which the portable `.` entry must stay free
|
|
19
|
+
// of. `glsp-framework-overrides.js` and `glsp-server-overrides.js` are
|
|
20
|
+
// deliberately NOT re-exported — they are the bindings those two bringups share,
|
|
21
|
+
// reached by relative import, and an adopter composing a container by hand wants
|
|
22
|
+
// the launcher rather than its internals.
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2023-2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import { type Logger as GlspLogger, LogLevel } from '@eclipse-glsp/server';
|
|
11
|
+
import { LspLogger, type LspLoggerOptions, type ServerSharedServices } from '@hydranium/core';
|
|
12
|
+
import { Logger, type LogThreshold } from '@hydranium/protocol';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Options accepted by {@link GlspClientLogger}. Extends
|
|
16
|
+
* {@link LspLoggerOptions} (which carries the optional `component`
|
|
17
|
+
* label) with the GLSP-specific per-instance {@link logLevel} threshold.
|
|
18
|
+
*/
|
|
19
|
+
export interface GlspClientLoggerOptions extends LspLoggerOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Per-instance NARROWING of the framework threshold. Omit it — the default
|
|
22
|
+
* tracks `Logger.getLevel()` live, which is what makes one
|
|
23
|
+
* `HYDRANIUM_LOG_LEVEL` / one LSP setting govern GLSP output too.
|
|
24
|
+
*
|
|
25
|
+
* It can only narrow, never widen, and that is not a policy choice:
|
|
26
|
+
* `AbstractLogger.send` gates every emission on the process-wide threshold
|
|
27
|
+
* before it reaches the output channel, so a value more verbose than the
|
|
28
|
+
* global is silently ineffective. Set it only to make ONE component quieter
|
|
29
|
+
* than the rest.
|
|
30
|
+
*/
|
|
31
|
+
readonly logLevel?: LogLevel;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Map the framework's {@link LogThreshold} onto GLSP's numeric enum.
|
|
36
|
+
*
|
|
37
|
+
* Two vocabularies have to meet here: ours is a string union with `'off'` and
|
|
38
|
+
* `'trace'`, GLSP's a numeric enum whose most verbose tier is `debug`. `'off'`
|
|
39
|
+
* becomes `none`, and **`'trace'` also becomes `debug`** — GLSP has no finer
|
|
40
|
+
* tier, and mapping it to `none` would silence GLSP at the most verbose
|
|
41
|
+
* framework setting, which is the opposite of what asking for `trace` means.
|
|
42
|
+
*/
|
|
43
|
+
export function glspLogLevelOf(threshold: LogThreshold): LogLevel {
|
|
44
|
+
switch (threshold) {
|
|
45
|
+
case 'off':
|
|
46
|
+
return LogLevel.none;
|
|
47
|
+
case 'error':
|
|
48
|
+
return LogLevel.error;
|
|
49
|
+
case 'warn':
|
|
50
|
+
return LogLevel.warn;
|
|
51
|
+
case 'info':
|
|
52
|
+
return LogLevel.info;
|
|
53
|
+
case 'debug':
|
|
54
|
+
case 'trace':
|
|
55
|
+
return LogLevel.debug;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Adapter from {@link LspLogger} (the framework's LSP-bound logger in
|
|
61
|
+
* `@hydranium/core`) to GLSP's server-side {@link GlspLogger}
|
|
62
|
+
* abstract base. Lets the GLSP container resolve a logger whose output
|
|
63
|
+
* lines interleave with the LSP head's existing log channel — the only
|
|
64
|
+
* way to debug request/response flow across the two heads in one place.
|
|
65
|
+
*
|
|
66
|
+
* GLSP-specific concerns layered on top of {@link LspLogger}:
|
|
67
|
+
*
|
|
68
|
+
* - A {@link logLevel} field, because GLSP's `Logger` contract declares one.
|
|
69
|
+
* **It tracks the framework's process-wide threshold by default**, resolving
|
|
70
|
+
* `Logger.getLevel()` through {@link glspLogLevelOf} on every read — so
|
|
71
|
+
* `HYDRANIUM_LOG_LEVEL`, the LSP log-level setting and the Theia preference
|
|
72
|
+
* all reach GLSP output without a second knob. Assigning the field (or
|
|
73
|
+
* passing `logLevel`) pins an explicit per-instance NARROWING; see
|
|
74
|
+
* {@link GlspClientLoggerOptions.logLevel} for why it cannot widen.
|
|
75
|
+
* Resolved lazily rather than captured in the constructor: the root logger is
|
|
76
|
+
* built during container setup, before the LSP client has sent its
|
|
77
|
+
* configuration, so a captured value would freeze the launch-time default.
|
|
78
|
+
* - The {@link caller} accessor satisfies GLSP's abstract `Logger.caller`
|
|
79
|
+
* contract; it is backed by {@link LspLogger}'s `component` field.
|
|
80
|
+
* - {@link info} / {@link warn} / {@link error} / {@link debug} accept GLSP's
|
|
81
|
+
* `(message, ...params)` shape and {@link combine} the params into the
|
|
82
|
+
* message via {@link formatParam} so objects render as JSON and errors
|
|
83
|
+
* render with stack. GLSP server code routinely calls
|
|
84
|
+
* `logger.error('Failed', error)` expecting the second argument to be
|
|
85
|
+
* surfaced in the log line.
|
|
86
|
+
*
|
|
87
|
+
* Subclasses can override {@link withUri} to render workspace-relative
|
|
88
|
+
* paths instead of stringified URIs (the typical adopter customisation,
|
|
89
|
+
* mirroring what an adopter does in its LSP-side logger).
|
|
90
|
+
*/
|
|
91
|
+
export class GlspClientLogger extends LspLogger implements GlspLogger {
|
|
92
|
+
/**
|
|
93
|
+
* Explicit per-instance narrowing, or `undefined` to track the framework
|
|
94
|
+
* threshold. Kept separate from the resolved value so {@link derive} can pass
|
|
95
|
+
* the OVERRIDE to children rather than the resolution — passing the resolved
|
|
96
|
+
* level would pin every child at whatever the global happened to be when the
|
|
97
|
+
* parent was derived, and container-time derivation is exactly when that
|
|
98
|
+
* value is still the launch-time default.
|
|
99
|
+
*/
|
|
100
|
+
protected logLevelOverride?: LogLevel;
|
|
101
|
+
|
|
102
|
+
constructor(services: ServerSharedServices, options: GlspClientLoggerOptions = {}) {
|
|
103
|
+
super(services, options);
|
|
104
|
+
this.logLevelOverride = options.logLevel;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** GLSP's `Logger.logLevel` contract; the framework threshold unless pinned. */
|
|
108
|
+
get logLevel(): LogLevel {
|
|
109
|
+
return this.logLevelOverride ?? glspLogLevelOf(Logger.getLevel());
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
set logLevel(level: LogLevel) {
|
|
113
|
+
this.logLevelOverride = level;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** GLSP's `Logger.caller` contract; reads {@link LspLogger.component}. */
|
|
117
|
+
get caller(): string | undefined {
|
|
118
|
+
return this.component;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
override info(message: string, ...params: unknown[]): this {
|
|
122
|
+
if (this.enabled(LogLevel.info)) {
|
|
123
|
+
super.info(this.combine(message, params));
|
|
124
|
+
}
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
override warn(message: string, ...params: unknown[]): this {
|
|
129
|
+
if (this.enabled(LogLevel.warn)) {
|
|
130
|
+
super.warn(this.combine(message, params));
|
|
131
|
+
}
|
|
132
|
+
return this;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
override error(message: string, ...params: unknown[]): this {
|
|
136
|
+
if (this.enabled(LogLevel.error)) {
|
|
137
|
+
super.error(this.combine(message, params));
|
|
138
|
+
}
|
|
139
|
+
return this;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
override debug(message: string, ...params: unknown[]): this {
|
|
143
|
+
if (this.enabled(LogLevel.debug)) {
|
|
144
|
+
super.debug(this.combine(message, params));
|
|
145
|
+
}
|
|
146
|
+
return this;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** True when {@link logLevel} permits emission at the given level. */
|
|
150
|
+
protected enabled(level: LogLevel): boolean {
|
|
151
|
+
return this.logLevel !== LogLevel.none && level <= this.logLevel;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Concatenate a message with its varargs, formatting each via {@link formatParam}. */
|
|
155
|
+
protected combine(message: string, params: readonly unknown[]): string {
|
|
156
|
+
return params.length === 0 ? message : `${message} ${params.map(p => this.formatParam(p)).join(',\n')}`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Format a single param: errors → `message\nstack`; everything else → JSON.stringify(p, undefined, 4). */
|
|
160
|
+
protected formatParam(param: unknown): string {
|
|
161
|
+
if (param instanceof Error) {
|
|
162
|
+
return `${param.message}\n${param.stack ?? ''}`;
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
return JSON.stringify(param, undefined, 4);
|
|
166
|
+
} catch {
|
|
167
|
+
return '';
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Override of {@link LspLogger.derive} that carries the per-instance
|
|
173
|
+
* narrowing. The base `derive(component)` constructs the subclass with
|
|
174
|
+
* `{ component }` only, which would drop it.
|
|
175
|
+
*
|
|
176
|
+
* It passes {@link logLevelOverride}, NOT `this.logLevel`: an unpinned parent
|
|
177
|
+
* must yield an unpinned child, or every derived logger freezes at the
|
|
178
|
+
* threshold in force when the container was built.
|
|
179
|
+
*/
|
|
180
|
+
protected override derive(component: string): this {
|
|
181
|
+
const Subclass = this.constructor as new (services: ServerSharedServices, options?: GlspClientLoggerOptions) => this;
|
|
182
|
+
return new Subclass(this.services, { logLevel: this.logLevelOverride, component });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
export * from './glsp-client-logger.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
// Server-only entry (`@hydranium/glsp-server/node`). Holds the socket launcher,
|
|
11
|
+
// which binds GLSP's node `SocketServerLauncher` (`@eclipse-glsp/server/node`).
|
|
12
|
+
// The portable `.` entry stays free of that import so it bundles for the browser.
|
|
13
|
+
export * from './start-glsp-server.js';
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2023-2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
type Logger as GlspLogger,
|
|
12
|
+
type ServerModule,
|
|
13
|
+
type SocketLaunchOptions,
|
|
14
|
+
SocketServerLauncher,
|
|
15
|
+
createAppModule,
|
|
16
|
+
defaultSocketLaunchOptions
|
|
17
|
+
} from '@eclipse-glsp/server/node.js';
|
|
18
|
+
import { Container, type ContainerModule } from 'inversify';
|
|
19
|
+
import type { Logger } from '@hydranium/protocol';
|
|
20
|
+
import { Deferred } from '@hydranium/langium';
|
|
21
|
+
import type * as net from 'node:net';
|
|
22
|
+
import type { IntegratedServer } from '@hydranium/core';
|
|
23
|
+
import { createGlspFrameworkOverrides } from '../launcher/glsp-framework-overrides.js';
|
|
24
|
+
import { createGlspServerOverrides } from '../launcher/glsp-server-overrides.js';
|
|
25
|
+
import type { LspConnectionLike } from '@hydranium/core/node';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Options for {@link startGlspServer}.
|
|
29
|
+
*
|
|
30
|
+
* @see {@link startGlspServer} for the lifecycle semantics.
|
|
31
|
+
*/
|
|
32
|
+
export interface GlspServerOptions {
|
|
33
|
+
/**
|
|
34
|
+
* Port to bind. Default `0` — OS-assigned ephemeral. Resolve the
|
|
35
|
+
* actual port off {@link StartedGlspServer.port} once `started`
|
|
36
|
+
* resolves (only then is the address info available).
|
|
37
|
+
*/
|
|
38
|
+
readonly port?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Host to bind. Default `'127.0.0.1'` (LOCAL ONLY) — the GLSP head is
|
|
41
|
+
* traffic-only-from-the-companion-client by design; rebinding to a
|
|
42
|
+
* routable address is an explicit opt-in that adopters must do
|
|
43
|
+
* deliberately.
|
|
44
|
+
*/
|
|
45
|
+
readonly host?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Adopter-supplied logger factory. Called once per Inversify resolution
|
|
48
|
+
* (with `caller` set to the requesting parent's class name) AND once
|
|
49
|
+
* per `LoggerFactory` invocation.
|
|
50
|
+
*
|
|
51
|
+
* **The threshold belongs in here, not beside it.** GLSP's own log level is
|
|
52
|
+
* a property of the logger this factory returns — e.g.
|
|
53
|
+
* `GlspClientLogger` filters
|
|
54
|
+
* each call against its own `logLevel` option. There is deliberately no
|
|
55
|
+
* launcher-level `logLevel`: the framework replaces GLSP's `Logger` binding
|
|
56
|
+
* outright, so anything passed to GLSP's app module would be discarded, and
|
|
57
|
+
* an option that looks authoritative and is discarded is worse than none.
|
|
58
|
+
*/
|
|
59
|
+
readonly createLogger: (caller?: string) => GlspLogger;
|
|
60
|
+
/**
|
|
61
|
+
* The GLSP {@link ServerModule} with per-diagram modules
|
|
62
|
+
* pre-configured via `new ServerModule().configureDiagramModule(...)`.
|
|
63
|
+
*/
|
|
64
|
+
readonly serverModule: ServerModule;
|
|
65
|
+
/**
|
|
66
|
+
* Additional Inversify modules to load on the app container AFTER GLSP's
|
|
67
|
+
* own app module (via `createAppModule`, which binds `InjectionContainer`
|
|
68
|
+
* plus the GLSP-version-specific app bindings) and the framework overrides
|
|
69
|
+
* that route GLSP's logger and logger factory through the adopter logger
|
|
70
|
+
* and add the shared Tracer. Adopters wire language-services bindings
|
|
71
|
+
* (e.g. their own shared-services symbol) here.
|
|
72
|
+
*/
|
|
73
|
+
readonly appModules?: ReadonlyArray<ContainerModule>;
|
|
74
|
+
/**
|
|
75
|
+
* Optional LSP connection used to register a port-discovery request
|
|
76
|
+
* handler. When both `lspConnection` and `portCommand` are set, the
|
|
77
|
+
* framework registers `lspConnection.onRequest(portCommand, () => port)`
|
|
78
|
+
* after the GLSP socket is listening — the standard handshake adopters
|
|
79
|
+
* use to bridge LSP clients to the GLSP head.
|
|
80
|
+
*/
|
|
81
|
+
readonly lspConnection?: LspConnectionLike;
|
|
82
|
+
/**
|
|
83
|
+
* LSP request method name to register. Required when `lspConnection` is
|
|
84
|
+
* set. Adopters define this constant in their own protocol package.
|
|
85
|
+
*/
|
|
86
|
+
readonly portCommand?: string;
|
|
87
|
+
/**
|
|
88
|
+
* Optional framework-lifecycle logger. Receives `listening` / `error`
|
|
89
|
+
* lifecycle messages from the launcher itself (the GLSP container's
|
|
90
|
+
* own logger handles application-level GLSP logs).
|
|
91
|
+
*/
|
|
92
|
+
readonly logger?: Logger;
|
|
93
|
+
/**
|
|
94
|
+
* Optional diagnostic hook called for every incoming `net.Socket` the
|
|
95
|
+
* GLSP server's `net.Server` accepts. Fires as a side-effect of the
|
|
96
|
+
* framework's own `'connection'` listener — GLSP's per-connection setup
|
|
97
|
+
* runs independently from its own listener registered by
|
|
98
|
+
* `SocketServerLauncher`. Adopters typically attach `'data'` / `'close'`
|
|
99
|
+
* listeners inside the hook for byte-level observability when debugging
|
|
100
|
+
* wire-level handshake issues.
|
|
101
|
+
*/
|
|
102
|
+
readonly onClientConnection?: (socket: net.Socket) => void;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Live GLSP server handle returned by {@link startGlspServer}.
|
|
107
|
+
*
|
|
108
|
+
* - {@link IntegratedServer.started} resolves once the GLSP socket is
|
|
109
|
+
* listening AND (if configured) the port-discovery handler has been
|
|
110
|
+
* registered on the LSP connection.
|
|
111
|
+
* - {@link IntegratedServer.stopped} resolves once GLSP's
|
|
112
|
+
* `SocketServerLauncher.start` promise settles (i.e. the underlying
|
|
113
|
+
* `net.Server` has closed).
|
|
114
|
+
* - {@link port} is `undefined` until `started` resolves.
|
|
115
|
+
*/
|
|
116
|
+
export interface StartedGlspServer extends IntegratedServer {
|
|
117
|
+
readonly port: number | undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Start a GLSP socket head. Builds the GLSP app container with framework-
|
|
122
|
+
* default `Logger` / `LoggerFactory` bindings, layers adopter modules,
|
|
123
|
+
* resolves {@link SocketServerLauncher}, configures the adopter
|
|
124
|
+
* {@link ServerModule}, and starts listening.
|
|
125
|
+
*
|
|
126
|
+
* **GLSP launcher coupling.** GLSP's {@link SocketServerLauncher} owns the
|
|
127
|
+
* `net.Server` and per-connection `MessageConnection` construction;
|
|
128
|
+
* the framework wrapper composes ABOVE that launcher rather than replacing
|
|
129
|
+
* it. The framework's own `startSocketServer`
|
|
130
|
+
* helper is NOT used here — it's designed for stdio/raw-JSON-RPC heads
|
|
131
|
+
* (data-server's pattern), not GLSP's Inversify-driven per-connection
|
|
132
|
+
* server-instance model. Bridging the two would lose GLSP's action-handler
|
|
133
|
+
* registry plumbing.
|
|
134
|
+
*/
|
|
135
|
+
export function startGlspServer(options: GlspServerOptions): StartedGlspServer {
|
|
136
|
+
const launchOptions: SocketLaunchOptions = {
|
|
137
|
+
...defaultSocketLaunchOptions,
|
|
138
|
+
host: options.host ?? '127.0.0.1',
|
|
139
|
+
port: options.port ?? 0
|
|
140
|
+
};
|
|
141
|
+
const lifecycle = options.logger;
|
|
142
|
+
|
|
143
|
+
// Start from GLSP's own app module rather than re-deriving its bindings by
|
|
144
|
+
// hand. createAppModule binds InjectionContainer + Logger/LoggerFactory and
|
|
145
|
+
// — crucially — whatever else the resolved GLSP version's app container needs,
|
|
146
|
+
// so the framework tracks GLSP across versions instead of drifting. consoleLog /
|
|
147
|
+
// fileLog are forced off so GLSP binds a throwaway NullLogger with no winston
|
|
148
|
+
// instance; the override module below replaces Logger/LoggerFactory with the
|
|
149
|
+
// adopter's logger, so GLSP server logs flow through options.createLogger.
|
|
150
|
+
const glspAppModule = createAppModule({ ...launchOptions, consoleLog: false, fileLog: false });
|
|
151
|
+
|
|
152
|
+
const appContainer = new Container();
|
|
153
|
+
appContainer.load(glspAppModule, createGlspFrameworkOverrides(options.createLogger), ...(options.appModules ?? []));
|
|
154
|
+
|
|
155
|
+
const launcher = appContainer.resolve<SocketServerLauncher>(SocketServerLauncher);
|
|
156
|
+
// Passed as an additional module rather than folded into the app container:
|
|
157
|
+
// the launcher loads these into the per-connection SERVER container, which is
|
|
158
|
+
// the only tier where the adopter's `ServerModule` binding of `GLSPServer`
|
|
159
|
+
// can be rebound.
|
|
160
|
+
launcher.configure(options.serverModule, createGlspServerOverrides());
|
|
161
|
+
|
|
162
|
+
const started = new Deferred<void>();
|
|
163
|
+
const portHandle: { port: number | undefined } = { port: undefined };
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
const stoppedPromise = Promise.resolve(launcher.start(launchOptions));
|
|
167
|
+
|
|
168
|
+
// GLSP's `SocketServerLauncher.netServer` field is `protected`; we
|
|
169
|
+
// access it via property indexing. The alternative is to fork the
|
|
170
|
+
// launcher or add a 'listening' callback to GLSP upstream.
|
|
171
|
+
const netServer = (launcher as unknown as { netServer: net.Server }).netServer;
|
|
172
|
+
|
|
173
|
+
netServer.on('listening', () => {
|
|
174
|
+
const address = netServer.address();
|
|
175
|
+
if (address && typeof address !== 'string') {
|
|
176
|
+
portHandle.port = address.port;
|
|
177
|
+
if (options.lspConnection && options.portCommand) {
|
|
178
|
+
options.lspConnection.onRequest(options.portCommand, () => portHandle.port);
|
|
179
|
+
}
|
|
180
|
+
lifecycle?.info(`[GlspServer] Ready to accept new client requests on port: ${address.port}`);
|
|
181
|
+
started.resolve();
|
|
182
|
+
} else {
|
|
183
|
+
const message = address === null ? 'address is null' : `bound to non-TCP "${String(address)}"`;
|
|
184
|
+
lifecycle?.error(`[GlspServer] Could not resolve address info — ${message}. Shutting down.`);
|
|
185
|
+
started.reject(new Error(`startGlspServer could not resolve address info — ${message}`));
|
|
186
|
+
netServer.close();
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
netServer.on('error', (err: Error) => {
|
|
190
|
+
lifecycle?.error(`[GlspServer] Error: ${err.message}`);
|
|
191
|
+
started.reject(err);
|
|
192
|
+
});
|
|
193
|
+
if (options.onClientConnection) {
|
|
194
|
+
netServer.on('connection', options.onClientConnection);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
get port() {
|
|
199
|
+
return portHandle.port;
|
|
200
|
+
},
|
|
201
|
+
started: started.promise,
|
|
202
|
+
stopped: stoppedPromise
|
|
203
|
+
};
|
|
204
|
+
} catch (error) {
|
|
205
|
+
lifecycle?.error('Error in GLSP server launcher:', error);
|
|
206
|
+
return {
|
|
207
|
+
get port() {
|
|
208
|
+
return portHandle.port;
|
|
209
|
+
},
|
|
210
|
+
started: Promise.reject(error),
|
|
211
|
+
stopped: Promise.resolve()
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
}
|