@hydranium/client-theia 1.0.0-next.160 → 1.0.0-next.168

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/README.md CHANGED
@@ -18,6 +18,12 @@ hydranium server; a non-Theia host does not need it.
18
18
  sides of the conversation read as one transcript. The same call also binds
19
19
  `ChannelTracer`, the `Tracer` token frontend services inject when they time
20
20
  something.
21
+ - **`ConnectionReporter`** with `bindConnectionReporter` — the slot every head
22
+ reports its connection attempts through, reconnects included.
23
+ `DefaultConnectionReporter` shows a progress notification for an attempt still
24
+ running after 3 s and ends it in at most one notification, raising a failure
25
+ once until the head connects again. Rebind the slot to report differently, or
26
+ not at all; the heads keep their retries either way.
21
27
  - **`LogLevelPreferenceContribution`** with `bindLogLevelPreference` — applies the
22
28
  framework log threshold from a Theia preference once per application, and keeps
23
29
  it in sync on change. It is a `FrontendApplicationContribution` on purpose: the
@@ -115,7 +121,7 @@ from here:
115
121
  | Subpath | Holds | Environment |
116
122
  | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
117
123
  | `.` | Nothing. Deliberately empty, so an environment-specific import cannot reach the wrong bundle through a barrel. | browser-neutral (gated) |
118
- | `./browser` | `ChannelLogger`, `ChannelTracer`, `LogLevelPreferenceContribution`, `MemoryDiagnosticsContribution`, `EditorDiskSync`, `HydraniumFileService`, the `bind*` helpers, `captureBrowserRuntime` | browser / Theia frontend (gated) |
124
+ | `./browser` | `ChannelLogger`, `ChannelTracer`, `ConnectionReporter`, `DefaultConnectionReporter`, `LogLevelPreferenceContribution`, `MemoryDiagnosticsContribution`, `EditorDiskSync`, `HydraniumFileService`, the `bind*` helpers, `captureBrowserRuntime` | browser / Theia frontend (gated) |
119
125
  | `./common` | `Clock`, the framed socket write buffer and the connection-resilience options | browser-neutral (gated) |
120
126
  | `./node` | `AbstractSocketForwardingConnectionHandler` and its options, `SocketChannelForwarder` — imports `node:net` | Node / Theia backend |
121
127
  | `./testing` | `makeStubOutputChannelManager`, `makeStubInversifyContext` | browser-neutral (gated) |
@@ -0,0 +1,57 @@
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
+ import { MessageService, type Progress } from '@theia/core';
10
+ import { type interfaces } from '@theia/core/shared/inversify';
11
+ /** What a head connects to, as the whole sentences reported about it. */
12
+ export interface ConnectionTarget {
13
+ readonly connectingMessage: string;
14
+ readonly connectedMessage: string;
15
+ }
16
+ /** One connection attempt; it reports exactly one end. */
17
+ export interface ConnectionAttempt {
18
+ connected(): void;
19
+ /** Ends the attempt with nothing to report, as when its head is disposed
20
+ * or a newer attempt takes over. */
21
+ cancelled(): void;
22
+ /**
23
+ * `message` is the whole sentence to report. `retry`, when given, starts a
24
+ * fresh attempt at once; a head without one keeps retrying on its own.
25
+ */
26
+ failed(message: string, retry?: () => void): void;
27
+ }
28
+ /**
29
+ * How a head's connection attempts reach the user. A head calls
30
+ * {@link connecting} for every attempt, a reconnect included, and keeps the
31
+ * attempt's retries and bounds itself; this decides only what is shown. Rebind
32
+ * the slot to show nothing, or something other than notifications.
33
+ */
34
+ export interface ConnectionReporter {
35
+ connecting(target: ConnectionTarget): ConnectionAttempt;
36
+ }
37
+ export declare const ConnectionReporter: unique symbol;
38
+ /**
39
+ * Reports through Theia notifications: an attempt still running after
40
+ * {@link connectingNoticeDelayMs} shows its progress, and an attempt ends in at
41
+ * most one notification. A failure is shown once per target until it connects
42
+ * again, so a head that keeps retrying does not raise one per attempt.
43
+ */
44
+ export declare class DefaultConnectionReporter implements ConnectionReporter {
45
+ protected readonly messageService: MessageService;
46
+ protected readonly connectingNoticeDelayMs: number;
47
+ /** Targets whose last reported attempt failed. */
48
+ protected readonly failing: Set<ConnectionTarget>;
49
+ connecting(target: ConnectionTarget): ConnectionAttempt;
50
+ protected showConnecting(target: ConnectionTarget): Promise<Progress>;
51
+ protected showConnected(target: ConnectionTarget): void;
52
+ protected showFailed(target: ConnectionTarget, message: string, retry?: () => void): Promise<void>;
53
+ protected retryLabel(): string;
54
+ }
55
+ /** Bind {@link DefaultConnectionReporter} unless the slot is bound already; every head calls this. */
56
+ export declare function bindConnectionReporter(bind: interfaces.Bind, isBound: interfaces.IsBound): void;
57
+ //# sourceMappingURL=connection-reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-reporter.d.ts","sourceRoot":"","sources":["../../src/browser/connection-reporter.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,cAAc,EAAO,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAsB,KAAK,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAEnF,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAC9B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACpC;AAED,0DAA0D;AAC1D,MAAM,WAAW,iBAAiB;IAC/B,SAAS,IAAI,IAAI,CAAC;IAClB;yCACqC;IACrC,SAAS,IAAI,IAAI,CAAC;IAClB;;;OAGG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;CACpD;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IAChC,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,iBAAiB,CAAC;CAC1D;AACD,eAAO,MAAM,kBAAkB,eAA+B,CAAC;AAE/D;;;;;GAKG;AACH,qBACa,yBAA0B,YAAW,kBAAkB;IACzC,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAG,cAAc,CAAC;IAE3E,SAAS,CAAC,QAAQ,CAAC,uBAAuB,EAAE,MAAM,CAAS;IAC3D,kDAAkD;IAClD,SAAS,CAAC,QAAQ,CAAC,OAAO,wBAA+B;IAEzD,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,iBAAiB;IAiCvD,SAAS,CAAC,cAAc,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIrE,SAAS,CAAC,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI;cAIvC,UAAU,CAAC,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAaxG,SAAS,CAAC,UAAU,IAAI,MAAM;CAGhC;AAED,sGAAsG;AACtG,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,GAAG,IAAI,CAI/F"}
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ /********************************************************************************
3
+ * Copyright (c) 2026 CrossBreeze, EclipseSource and others.
4
+ *
5
+ * This program and the accompanying materials are made available under the
6
+ * terms of the MIT License which is available in the project root.
7
+ *
8
+ * SPDX-License-Identifier: MIT
9
+ ********************************************************************************/
10
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
11
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
12
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
13
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
14
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
15
+ };
16
+ var __metadata = (this && this.__metadata) || function (k, v) {
17
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.DefaultConnectionReporter = exports.ConnectionReporter = void 0;
21
+ exports.bindConnectionReporter = bindConnectionReporter;
22
+ const core_1 = require("@theia/core");
23
+ const inversify_1 = require("@theia/core/shared/inversify");
24
+ exports.ConnectionReporter = Symbol('ConnectionReporter');
25
+ /**
26
+ * Reports through Theia notifications: an attempt still running after
27
+ * {@link connectingNoticeDelayMs} shows its progress, and an attempt ends in at
28
+ * most one notification. A failure is shown once per target until it connects
29
+ * again, so a head that keeps retrying does not raise one per attempt.
30
+ */
31
+ let DefaultConnectionReporter = class DefaultConnectionReporter {
32
+ messageService;
33
+ connectingNoticeDelayMs = 3_000;
34
+ /** Targets whose last reported attempt failed. */
35
+ failing = new Set();
36
+ connecting(target) {
37
+ let progress;
38
+ const notice = this.failing.has(target)
39
+ ? undefined
40
+ : setTimeout(() => (progress = this.showConnecting(target)), this.connectingNoticeDelayMs);
41
+ let settled = false;
42
+ const settle = () => {
43
+ if (settled) {
44
+ return false;
45
+ }
46
+ settled = true;
47
+ clearTimeout(notice);
48
+ void progress?.then(shown => shown.cancel());
49
+ return true;
50
+ };
51
+ return {
52
+ connected: () => {
53
+ if (settle() && (this.failing.delete(target) || progress)) {
54
+ this.showConnected(target);
55
+ }
56
+ },
57
+ cancelled: () => {
58
+ settle();
59
+ },
60
+ failed: (message, retry) => {
61
+ if (settle() && !this.failing.has(target)) {
62
+ this.failing.add(target);
63
+ void this.showFailed(target, message, retry);
64
+ }
65
+ }
66
+ };
67
+ }
68
+ showConnecting(target) {
69
+ return this.messageService.showProgress({ text: target.connectingMessage });
70
+ }
71
+ showConnected(target) {
72
+ this.messageService.info(target.connectedMessage);
73
+ }
74
+ async showFailed(target, message, retry) {
75
+ if (!retry) {
76
+ this.messageService.error(message);
77
+ return;
78
+ }
79
+ const label = this.retryLabel();
80
+ if ((await this.messageService.error(message, label)) === label) {
81
+ // The retried attempt reports afresh, progress included.
82
+ this.failing.delete(target);
83
+ retry();
84
+ }
85
+ }
86
+ retryLabel() {
87
+ return core_1.nls.localize('hydranium/client-theia/connection-retry', 'Retry');
88
+ }
89
+ };
90
+ exports.DefaultConnectionReporter = DefaultConnectionReporter;
91
+ __decorate([
92
+ (0, inversify_1.inject)(core_1.MessageService),
93
+ __metadata("design:type", core_1.MessageService)
94
+ ], DefaultConnectionReporter.prototype, "messageService", void 0);
95
+ exports.DefaultConnectionReporter = DefaultConnectionReporter = __decorate([
96
+ (0, inversify_1.injectable)()
97
+ ], DefaultConnectionReporter);
98
+ /** Bind {@link DefaultConnectionReporter} unless the slot is bound already; every head calls this. */
99
+ function bindConnectionReporter(bind, isBound) {
100
+ if (!isBound(exports.ConnectionReporter)) {
101
+ bind(exports.ConnectionReporter).to(DefaultConnectionReporter).inSingletonScope();
102
+ }
103
+ }
104
+ //# sourceMappingURL=connection-reporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-reporter.js","sourceRoot":"","sources":["../../src/browser/connection-reporter.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,7 @@ export * from './browser-capture';
12
12
  export * from './memory-diagnostics-contribution';
13
13
  export * from './session-aware-connection-source';
14
14
  export * from './connection-diagnostics-contribution';
15
+ export * from './connection-reporter';
15
16
  export * from './editor-disk-sync';
16
17
  export * from './hydranium-file-service';
17
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAGlF,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mCAAmC,CAAC;AAClD,cAAc,mCAAmC,CAAC;AAClD,cAAc,uCAAuC,CAAC;AACtD,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAGlF,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mCAAmC,CAAC;AAClD,cAAc,mCAAmC,CAAC;AAClD,cAAc,uCAAuC,CAAC;AACtD,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC"}
@@ -29,6 +29,7 @@ __exportStar(require("./browser-capture"), exports);
29
29
  __exportStar(require("./memory-diagnostics-contribution"), exports);
30
30
  __exportStar(require("./session-aware-connection-source"), exports);
31
31
  __exportStar(require("./connection-diagnostics-contribution"), exports);
32
+ __exportStar(require("./connection-reporter"), exports);
32
33
  __exportStar(require("./editor-disk-sync"), exports);
33
34
  __exportStar(require("./hydranium-file-service"), exports);
34
35
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;;;;;;;;;;;;;;AAElF,gFAAgF;AAChF,mDAAiC;AACjC,yDAAuC;AACvC,oDAAkC;AAClC,oEAAkD;AAClD,oEAAkD;AAClD,wEAAsD;AACtD,qDAAmC;AACnC,2DAAyC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;;;;;;;;;;;;;;AAElF,gFAAgF;AAChF,mDAAiC;AACjC,yDAAuC;AACvC,oDAAkC;AAClC,oEAAkD;AAClD,oEAAkD;AAClD,wEAAsD;AACtD,wDAAsC;AACtC,qDAAmC;AACnC,2DAAyC"}
@@ -70,7 +70,10 @@ export declare abstract class AbstractSocketForwardingConnectionHandler implemen
70
70
  constructor(options: SocketForwardingConnectionHandlerOptions);
71
71
  onConnection(connection: Channel): void;
72
72
  protected initializeServerConnection(channel: Channel): Promise<void>;
73
- protected findPort(): Promise<number>;
73
+ /** Tell the user this handler gave up; `message` is the reason it logged. */
74
+ protected reportConnectFailure(message: string): void;
75
+ /** Poll the port command until it answers, its attempts run out, or `signal` aborts. */
76
+ protected findPort(signal?: AbortSignal): Promise<number>;
74
77
  protected connectToServer(channel: Channel, port: number, preForwardBuffer?: {
75
78
  bufferSub: Disposable;
76
79
  buffered: MessageProvider[];
@@ -1 +1 @@
1
- {"version":3,"file":"abstract-socket-forwarding-connection-handler.d.ts","sourceRoot":"","sources":["../../src/node/abstract-socket-forwarding-connection-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAGlF,OAAO,EACJ,KAAK,OAAO,EACZ,cAAc,EACd,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,OAAO,EACP,KAAK,eAAe,EACpB,cAAc,EAChB,MAAM,aAAa,CAAC;AAIrB,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAGxC;;uCAEuC;AACvC,MAAM,WAAW,wCAAwC;IACtD,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;iEAC6D;IAC7D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;gCAC4B;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;0BACsB;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC;CAC1D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,8BACsB,yCAA0C,YAAW,iBAAiB;IACjE,SAAS,CAAC,cAAc,EAAG,cAAc,CAAC;IAC1C,SAAS,CAAC,cAAc,EAAG,cAAc,CAAC;IAIjD,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAG,OAAO,CAAC;IACrD,4HAA4H;IACjG,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAqB;IAE/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IACvC,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IACxC,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IACtC,SAAS,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAC3C,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAC5C,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAC5C,SAAS,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC;gBAEtD,OAAO,EAAE,wCAAwC;IAW7D,YAAY,CAAC,UAAU,EAAE,OAAO,GAAG,IAAI;cAIvB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;cAyB3D,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;cAwB3B,eAAe,CAC5B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,MAAM,EACZ,gBAAgB,CAAC,EAAE;QAAE,SAAS,EAAE,UAAU,CAAC;QAAC,QAAQ,EAAE,eAAe,EAAE,CAAA;KAAE,GACzE,OAAO,CAAC,IAAI,CAAC;IA8BhB;;;;OAIG;IACH,SAAS,CAAC,yBAAyB,CAAC,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,UAAU;IAI3F;;;;;;;;;;OAUG;IACH,SAAS,CAAC,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,IAAI;CAiBvF"}
1
+ {"version":3,"file":"abstract-socket-forwarding-connection-handler.d.ts","sourceRoot":"","sources":["../../src/node/abstract-socket-forwarding-connection-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAGlF,OAAO,EACJ,KAAK,OAAO,EACZ,cAAc,EACd,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,OAAO,EACP,KAAK,eAAe,EACpB,cAAc,EAChB,MAAM,aAAa,CAAC;AAIrB,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAGxC;;uCAEuC;AACvC,MAAM,WAAW,wCAAwC;IACtD,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;iEAC6D;IAC7D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;gCAC4B;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;0BACsB;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC;CAC1D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,8BACsB,yCAA0C,YAAW,iBAAiB;IACjE,SAAS,CAAC,cAAc,EAAG,cAAc,CAAC;IAC1C,SAAS,CAAC,cAAc,EAAG,cAAc,CAAC;IAIjD,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAG,OAAO,CAAC;IACrD,4HAA4H;IACjG,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAqB;IAE/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IACvC,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IACxC,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IACtC,SAAS,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAC3C,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAC5C,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAC5C,SAAS,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC;gBAEtD,OAAO,EAAE,wCAAwC;IAW7D,YAAY,CAAC,UAAU,EAAE,OAAO,GAAG,IAAI;cAIvB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAqC3E,6EAA6E;IAC7E,SAAS,CAAC,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIrD,wFAAwF;cACxE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;cA0C/C,eAAe,CAC5B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,MAAM,EACZ,gBAAgB,CAAC,EAAE;QAAE,SAAS,EAAE,UAAU,CAAC;QAAC,QAAQ,EAAE,eAAe,EAAE,CAAA;KAAE,GACzE,OAAO,CAAC,IAAI,CAAC;IA8BhB;;;;OAIG;IACH,SAAS,CAAC,yBAAyB,CAAC,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,UAAU;IAI3F;;;;;;;;;;OAUG;IACH,SAAS,CAAC,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,IAAI;CAiBvF"}
@@ -118,32 +118,68 @@ let AbstractSocketForwardingConnectionHandler = class AbstractSocketForwardingCo
118
118
  // window.
119
119
  const buffered = [];
120
120
  const bufferSub = channel.onMessage(provider => buffered.push(provider));
121
+ const closed = new AbortController();
122
+ const closeSub = channel.onClose(() => closed.abort());
121
123
  try {
122
- const port = await this.findPort();
124
+ const port = await this.findPort(closed.signal);
125
+ closed.signal.throwIfAborted();
123
126
  this.logger.info(`[${this.logComponent}] Connecting to ${this.serverName} on port ${port}...`);
124
127
  await this.connectToServer(channel, port, { bufferSub, buffered });
125
128
  this.logger.info(`[${this.logComponent}] Connected to ${this.serverName} on port ${port}.`);
126
129
  }
127
130
  catch (error) {
128
131
  bufferSub.dispose();
132
+ if (closed.signal.aborted) {
133
+ this.logger.info(`[${this.logComponent}] Stopped connecting to ${this.serverName}: the frontend closed the channel.`);
134
+ return;
135
+ }
129
136
  const message = error && typeof error === 'object' && 'message' in error ? String(error.message) : String(error);
130
137
  this.logger.error(`[${this.logComponent}] Could not connect to ${this.serverName}: ${message}`);
131
- this.messageService.error(`Could not connect to ${this.serverName}: ` + message);
138
+ this.reportConnectFailure(message);
139
+ // Closed so the frontend learns of it: left open, the channel has no
140
+ // server behind it, and every request on it waits for good.
141
+ channel.close();
132
142
  }
143
+ finally {
144
+ closeSub.dispose();
145
+ }
146
+ }
147
+ /** Tell the user this handler gave up; `message` is the reason it logged. */
148
+ reportConnectFailure(message) {
149
+ this.messageService.error(`Could not connect to ${this.serverName}: ` + message);
133
150
  }
134
- async findPort() {
151
+ /** Poll the port command until it answers, its attempts run out, or `signal` aborts. */
152
+ async findPort(signal) {
135
153
  const pendingContent = new promise_util_1.Deferred();
136
154
  let counter = 0;
155
+ let timer;
156
+ const stop = () => {
157
+ clearTimeout(timer);
158
+ pendingContent.reject(signal?.reason);
159
+ };
160
+ if (signal?.aborted) {
161
+ stop();
162
+ }
163
+ signal?.addEventListener('abort', stop, { once: true });
137
164
  const tryQueryingPort = () => {
138
- setTimeout(async () => {
165
+ if (signal?.aborted) {
166
+ return;
167
+ }
168
+ timer = setTimeout(async () => {
139
169
  try {
140
170
  const port = await this.commandService.executeCommand(this.portCommand);
141
- if (port) {
142
- pendingContent.resolve(port);
171
+ // An empty answer fails the attempt like a throw does: left
172
+ // unanswered, it neither resolves nor re-queues, and the lookup
173
+ // stays pending with nothing logged.
174
+ if (!port) {
175
+ throw new Error(`Port command '${this.portCommand}' answered without a port.`);
143
176
  }
177
+ pendingContent.resolve(port);
144
178
  }
145
179
  catch (error) {
146
180
  counter++;
181
+ const message = error instanceof Error ? error.message : String(error);
182
+ this.logger.debug(`[${this.logComponent}] Port command '${this.portCommand}' attempt ${counter} failed: ${message}`);
147
183
  if (this.findPortAttempts >= 0 && counter > this.findPortAttempts) {
148
184
  pendingContent.reject(error);
149
185
  }
@@ -1 +1 @@
1
- {"version":3,"file":"abstract-socket-forwarding-connection-handler.js","sourceRoot":"","sources":["../../src/node/abstract-socket-forwarding-connection-handler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"abstract-socket-forwarding-connection-handler.js","sourceRoot":"","sources":["../../src/node/abstract-socket-forwarding-connection-handler.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hydranium/client-theia",
3
- "version": "1.0.0-next.160",
3
+ "version": "1.0.0-next.168",
4
4
  "description": "Cross-head Theia client primitives shared by the hydranium protocol heads. Provides the socket-forwarding connection-handler base that the data-server and GLSP Theia integrations subclass, plus the preference-driven Output-channel logger; carries no head-specific dependency.",
5
5
  "keywords": [
6
6
  "hydranium",
@@ -78,7 +78,7 @@
78
78
  "watch": "tsc -b -w --preserveWatchOutput"
79
79
  },
80
80
  "devDependencies": {
81
- "@hydranium/protocol": "1.0.0-next.160",
81
+ "@hydranium/protocol": "1.0.0-next.168",
82
82
  "@theia/core": "^1.70.0",
83
83
  "@theia/editor": "^1.70.0",
84
84
  "@theia/filesystem": "^1.70.0",
@@ -91,7 +91,7 @@
91
91
  "vscode-jsonrpc": "9.0.1"
92
92
  },
93
93
  "peerDependencies": {
94
- "@hydranium/protocol": "1.0.0-next.160",
94
+ "@hydranium/protocol": "1.0.0-next.168",
95
95
  "@theia/core": "^1.70.0",
96
96
  "@theia/editor": "^1.70.0",
97
97
  "@theia/filesystem": "^1.70.0",
@@ -0,0 +1,121 @@
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 { MessageService, nls, type Progress } from '@theia/core';
11
+ import { inject, injectable, type interfaces } from '@theia/core/shared/inversify';
12
+
13
+ /** What a head connects to, as the whole sentences reported about it. */
14
+ export interface ConnectionTarget {
15
+ readonly connectingMessage: string;
16
+ readonly connectedMessage: string;
17
+ }
18
+
19
+ /** One connection attempt; it reports exactly one end. */
20
+ export interface ConnectionAttempt {
21
+ connected(): void;
22
+ /** Ends the attempt with nothing to report, as when its head is disposed
23
+ * or a newer attempt takes over. */
24
+ cancelled(): void;
25
+ /**
26
+ * `message` is the whole sentence to report. `retry`, when given, starts a
27
+ * fresh attempt at once; a head without one keeps retrying on its own.
28
+ */
29
+ failed(message: string, retry?: () => void): void;
30
+ }
31
+
32
+ /**
33
+ * How a head's connection attempts reach the user. A head calls
34
+ * {@link connecting} for every attempt, a reconnect included, and keeps the
35
+ * attempt's retries and bounds itself; this decides only what is shown. Rebind
36
+ * the slot to show nothing, or something other than notifications.
37
+ */
38
+ export interface ConnectionReporter {
39
+ connecting(target: ConnectionTarget): ConnectionAttempt;
40
+ }
41
+ export const ConnectionReporter = Symbol('ConnectionReporter');
42
+
43
+ /**
44
+ * Reports through Theia notifications: an attempt still running after
45
+ * {@link connectingNoticeDelayMs} shows its progress, and an attempt ends in at
46
+ * most one notification. A failure is shown once per target until it connects
47
+ * again, so a head that keeps retrying does not raise one per attempt.
48
+ */
49
+ @injectable()
50
+ export class DefaultConnectionReporter implements ConnectionReporter {
51
+ @inject(MessageService) protected readonly messageService!: MessageService;
52
+
53
+ protected readonly connectingNoticeDelayMs: number = 3_000;
54
+ /** Targets whose last reported attempt failed. */
55
+ protected readonly failing = new Set<ConnectionTarget>();
56
+
57
+ connecting(target: ConnectionTarget): ConnectionAttempt {
58
+ let progress: Promise<Progress> | undefined;
59
+ const notice = this.failing.has(target)
60
+ ? undefined
61
+ : setTimeout(() => (progress = this.showConnecting(target)), this.connectingNoticeDelayMs);
62
+ let settled = false;
63
+ const settle = (): boolean => {
64
+ if (settled) {
65
+ return false;
66
+ }
67
+ settled = true;
68
+ clearTimeout(notice);
69
+ void progress?.then(shown => shown.cancel());
70
+ return true;
71
+ };
72
+ return {
73
+ connected: () => {
74
+ if (settle() && (this.failing.delete(target) || progress)) {
75
+ this.showConnected(target);
76
+ }
77
+ },
78
+ cancelled: () => {
79
+ settle();
80
+ },
81
+ failed: (message, retry) => {
82
+ if (settle() && !this.failing.has(target)) {
83
+ this.failing.add(target);
84
+ void this.showFailed(target, message, retry);
85
+ }
86
+ }
87
+ };
88
+ }
89
+
90
+ protected showConnecting(target: ConnectionTarget): Promise<Progress> {
91
+ return this.messageService.showProgress({ text: target.connectingMessage });
92
+ }
93
+
94
+ protected showConnected(target: ConnectionTarget): void {
95
+ this.messageService.info(target.connectedMessage);
96
+ }
97
+
98
+ protected async showFailed(target: ConnectionTarget, message: string, retry?: () => void): Promise<void> {
99
+ if (!retry) {
100
+ this.messageService.error(message);
101
+ return;
102
+ }
103
+ const label = this.retryLabel();
104
+ if ((await this.messageService.error(message, label)) === label) {
105
+ // The retried attempt reports afresh, progress included.
106
+ this.failing.delete(target);
107
+ retry();
108
+ }
109
+ }
110
+
111
+ protected retryLabel(): string {
112
+ return nls.localize('hydranium/client-theia/connection-retry', 'Retry');
113
+ }
114
+ }
115
+
116
+ /** Bind {@link DefaultConnectionReporter} unless the slot is bound already; every head calls this. */
117
+ export function bindConnectionReporter(bind: interfaces.Bind, isBound: interfaces.IsBound): void {
118
+ if (!isBound(ConnectionReporter)) {
119
+ bind(ConnectionReporter).to(DefaultConnectionReporter).inSingletonScope();
120
+ }
121
+ }
@@ -14,5 +14,6 @@ export * from './browser-capture';
14
14
  export * from './memory-diagnostics-contribution';
15
15
  export * from './session-aware-connection-source';
16
16
  export * from './connection-diagnostics-contribution';
17
+ export * from './connection-reporter';
17
18
  export * from './editor-disk-sync';
18
19
  export * from './hydranium-file-service';
@@ -117,31 +117,67 @@ export abstract class AbstractSocketForwardingConnectionHandler implements Conne
117
117
  // window.
118
118
  const buffered: MessageProvider[] = [];
119
119
  const bufferSub = channel.onMessage(provider => buffered.push(provider));
120
+ const closed = new AbortController();
121
+ const closeSub = channel.onClose(() => closed.abort());
120
122
  try {
121
- const port = await this.findPort();
123
+ const port = await this.findPort(closed.signal);
124
+ closed.signal.throwIfAborted();
122
125
  this.logger.info(`[${this.logComponent}] Connecting to ${this.serverName} on port ${port}...`);
123
126
  await this.connectToServer(channel, port, { bufferSub, buffered });
124
127
  this.logger.info(`[${this.logComponent}] Connected to ${this.serverName} on port ${port}.`);
125
128
  } catch (error) {
126
129
  bufferSub.dispose();
130
+ if (closed.signal.aborted) {
131
+ this.logger.info(`[${this.logComponent}] Stopped connecting to ${this.serverName}: the frontend closed the channel.`);
132
+ return;
133
+ }
127
134
  const message = error && typeof error === 'object' && 'message' in error ? String(error.message) : String(error);
128
135
  this.logger.error(`[${this.logComponent}] Could not connect to ${this.serverName}: ${message}`);
129
- this.messageService.error(`Could not connect to ${this.serverName}: ` + message);
136
+ this.reportConnectFailure(message);
137
+ // Closed so the frontend learns of it: left open, the channel has no
138
+ // server behind it, and every request on it waits for good.
139
+ channel.close();
140
+ } finally {
141
+ closeSub.dispose();
130
142
  }
131
143
  }
132
144
 
133
- protected async findPort(): Promise<number> {
145
+ /** Tell the user this handler gave up; `message` is the reason it logged. */
146
+ protected reportConnectFailure(message: string): void {
147
+ this.messageService.error(`Could not connect to ${this.serverName}: ` + message);
148
+ }
149
+
150
+ /** Poll the port command until it answers, its attempts run out, or `signal` aborts. */
151
+ protected async findPort(signal?: AbortSignal): Promise<number> {
134
152
  const pendingContent = new Deferred<number>();
135
153
  let counter = 0;
154
+ let timer: ReturnType<typeof setTimeout> | undefined;
155
+ const stop = (): void => {
156
+ clearTimeout(timer);
157
+ pendingContent.reject(signal?.reason);
158
+ };
159
+ if (signal?.aborted) {
160
+ stop();
161
+ }
162
+ signal?.addEventListener('abort', stop, { once: true });
136
163
  const tryQueryingPort = (): void => {
137
- setTimeout(async () => {
164
+ if (signal?.aborted) {
165
+ return;
166
+ }
167
+ timer = setTimeout(async () => {
138
168
  try {
139
169
  const port = await this.commandService.executeCommand<number>(this.portCommand);
140
- if (port) {
141
- pendingContent.resolve(port);
170
+ // An empty answer fails the attempt like a throw does: left
171
+ // unanswered, it neither resolves nor re-queues, and the lookup
172
+ // stays pending with nothing logged.
173
+ if (!port) {
174
+ throw new Error(`Port command '${this.portCommand}' answered without a port.`);
142
175
  }
176
+ pendingContent.resolve(port);
143
177
  } catch (error) {
144
178
  counter++;
179
+ const message = error instanceof Error ? error.message : String(error);
180
+ this.logger.debug(`[${this.logComponent}] Port command '${this.portCommand}' attempt ${counter} failed: ${message}`);
145
181
  if (this.findPortAttempts >= 0 && counter > this.findPortAttempts) {
146
182
  pendingContent.reject(error);
147
183
  } else {