@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 +7 -1
- package/lib/browser/connection-reporter.d.ts +57 -0
- package/lib/browser/connection-reporter.d.ts.map +1 -0
- package/lib/browser/connection-reporter.js +104 -0
- package/lib/browser/connection-reporter.js.map +1 -0
- package/lib/browser/index.d.ts +1 -0
- package/lib/browser/index.d.ts.map +1 -1
- package/lib/browser/index.js +1 -0
- package/lib/browser/index.js.map +1 -1
- package/lib/node/abstract-socket-forwarding-connection-handler.d.ts +4 -1
- package/lib/node/abstract-socket-forwarding-connection-handler.d.ts.map +1 -1
- package/lib/node/abstract-socket-forwarding-connection-handler.js +42 -6
- package/lib/node/abstract-socket-forwarding-connection-handler.js.map +1 -1
- package/package.json +3 -3
- package/src/browser/connection-reporter.ts +121 -0
- package/src/browser/index.ts +1 -0
- package/src/node/abstract-socket-forwarding-connection-handler.ts +42 -6
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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"}
|
package/lib/browser/index.d.ts
CHANGED
|
@@ -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"}
|
package/lib/browser/index.js
CHANGED
|
@@ -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
|
package/lib/browser/index.js.map
CHANGED
|
@@ -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
|
-
|
|
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;
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
142
|
-
|
|
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":";AAAA;;;;;;;kFAOkF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAElF,kDAAkD;AAClD,sCAQqB;AACrB,wEAA+E;AAC/E,sEAA+D;AAC/D,4DAA4E;AAC5E,8CAAgC;AAChC,2CAAwC;AACxC,yEAAoE;AA8BpE;;;;;;;;;;;;;;;;GAgBG;AAEI,IAAe,yCAAyC,GAAxD,MAAe,yCAAyC;IAC1B,cAAc,CAAkB;IAChC,cAAc,CAAkB;IAClE,4EAA4E;IAC5E,wEAAwE;IACxE,sEAAsE;IAClC,MAAM,CAAW;IACrD,4HAA4H;IAC9E,KAAK,GAAU,IAAI,sBAAW,EAAE,CAAC;IAEtE,IAAI,CAAS;IAEH,WAAW,CAAS;IACpB,YAAY,CAAS;IACrB,UAAU,CAAS;IACnB,eAAe,CAAS;IACxB,gBAAgB,CAAS;IACzB,gBAAgB,CAAS;IACzB,eAAe,CAAgC;IAElE,YAAY,OAAiD;QAC1D,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACvC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;QACzC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,GAAG,CAAC;QACtD,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,CAAC,CAAC,CAAC;QACvD,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,KAAK,CAAC;QAC1D,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;IAClD,CAAC;IAED,YAAY,CAAC,UAAmB;QAC7B,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,CAAC;IAC/C,CAAC;IAES,KAAK,CAAC,0BAA0B,CAAC,OAAgB;QACxD,sEAAsE;QACtE,gEAAgE;QAChE,0EAA0E;QAC1E,0EAA0E;QAC1E,uEAAuE;QACvE,qEAAqE;QACrE,qEAAqE;QACrE,sEAAsE;QACtE,UAAU;QACV,MAAM,QAAQ,GAAsB,EAAE,CAAC;QACvC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACzE,IAAI,CAAC;YACF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
+
}
|
package/src/browser/index.ts
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
141
|
-
|
|
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 {
|