@hydranium/data-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
@@ -29,8 +29,10 @@ text edits.
29
29
  with a `servicePath`; it supplies the channel, the workspace gate, the
30
30
  reconnect signal and the `MessageService` error sink. Bind one per service path
31
31
  in singleton scope. Hand it to `DataConnectionWithEvents` (from
32
- `@hydranium/protocol`) and the connection, its sessions and its event fan-out
33
- are the host-neutral ones every other shell uses.
32
+ `@hydranium/protocol`) and each connection reports through the
33
+ `ConnectionReporter`, a data server not ready after 30 s included; the
34
+ connection, its sessions and its event fan-out are the host-neutral ones every
35
+ other shell uses.
34
36
  - **`EmitterDataClient`** (on `./common`, not `./browser`) — the default
35
37
  client-side implementation of the data protocol's inbound notifications,
36
38
  fanning each one out to a Theia `Event`: `onDidUpdateDocument`,
@@ -83,8 +85,10 @@ extension builds on. That extension's `package.json` declares the entries, and
83
85
  each entry names one frontend/backend module pair:
84
86
 
85
87
  - the **frontend** module binds your `ChannelDataPort` subclass and the
86
- connection over it, both in singleton scope (and, for the diagnostics commands,
87
- calls `bindHostDiagnostics`);
88
+ connection over it, both in singleton scope, and calls
89
+ `bindConnectionReporter` and `bindChannelLogger` from `@hydranium/client-theia`
90
+ (the port logs the connection failures it leaves to the reporter there), and,
91
+ for the diagnostics commands, `bindHostDiagnostics`;
88
92
  - the **backend** module is typically a one-liner:
89
93
  `export default createDataServerConnectionContainerModule(MyHandler)`, where
90
94
  `MyHandler` extends `DataServerConnectionHandler`.
@@ -6,7 +6,8 @@
6
6
  *
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
- import { type DataPort, type ResolvedMessage } from '@hydranium/protocol';
9
+ import { ChannelLogger, ConnectionReporter, type ConnectionAttempt, type ConnectionTarget } from '@hydranium/client-theia/lib/browser';
10
+ import { type DataPort, type ResolvedMessage, type RpcConnectionLifecycle } from '@hydranium/protocol';
10
11
  import { Emitter, MessageService, type Event } from '@theia/core';
11
12
  import { type ServiceConnectionProvider } from '@theia/core/lib/browser';
12
13
  import { WorkspaceService } from '@theia/workspace/lib/browser';
@@ -28,6 +29,8 @@ export declare abstract class ChannelDataPort implements DataPort {
28
29
  protected readonly connectionProvider: ServiceConnectionProvider;
29
30
  protected readonly workspaceService: WorkspaceService;
30
31
  protected readonly messageService: MessageService;
32
+ protected readonly connectionReporter: ConnectionReporter;
33
+ protected readonly logger: ChannelLogger;
31
34
  /** Frontend service path the backend forwarder for this head is registered under. */
32
35
  protected abstract readonly servicePath: string;
33
36
  /**
@@ -40,6 +43,14 @@ export declare abstract class ChannelDataPort implements DataPort {
40
43
  protected readonly disposeEmitter: Emitter<void>;
41
44
  readonly onDispose: Event<void>;
42
45
  protected handle?: ChannelConnectionHandle;
46
+ /** How long a connection may take before it is reported as failed; it keeps trying after. */
47
+ protected readonly connectFailureNoticeMs: number;
48
+ protected attempt?: ConnectionAttempt;
49
+ protected attemptTimer?: ReturnType<typeof setTimeout>;
50
+ /** Set once {@link connectionLifecycle} reports, which then owns the connection failures. */
51
+ protected reportsConnections: boolean;
52
+ /** Reports each connection generation through the {@link ConnectionReporter}. */
53
+ readonly connectionLifecycle: RpcConnectionLifecycle;
43
54
  /**
44
55
  * Open the workspace-gated channel and hand back its listening connection.
45
56
  *
@@ -60,6 +71,13 @@ export declare abstract class ChannelDataPort implements DataPort {
60
71
  * inside another's and leave no translator in control of the whole.
61
72
  */
62
73
  reportError(_error: unknown, reported: ResolvedMessage): void;
74
+ /** End the current attempt through `settle`, or cancel it. */
75
+ protected settleAttempt(settle?: (attempt: ConnectionAttempt) => void): void;
76
+ protected get connectionTarget(): ConnectionTarget;
77
+ /** One object per port: the reporter keys what it has shown on it. */
78
+ protected cachedConnectionTarget?: ConnectionTarget;
79
+ protected unreachableMessage(): string;
80
+ protected connectTimeoutMessage(): string;
63
81
  dispose(): void;
64
82
  }
65
83
  //# sourceMappingURL=channel-data-port.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"channel-data-port.d.ts","sourceRoot":"","sources":["../../src/browser/channel-data-port.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAA0B,KAAK,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAClG,OAAO,EAAE,OAAO,EAAE,cAAc,EAAO,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAGzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,KAAK,uBAAuB,EAAyB,MAAM,sBAAsB,CAAC;AAG3F;;;;;;;;;;;GAWG;AACH,8BACsB,eAAgB,YAAW,QAAQ;IACpB,SAAS,CAAC,QAAQ,CAAC,kBAAkB,EAAG,yBAAyB,CAAC;IAC1E,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAG,gBAAgB,CAAC;IACzD,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAG,cAAc,CAAC;IAE3E,qFAAqF;IACrF,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAEhD;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,yBAAyB,EAAE,OAAO,CAAQ;IAE7D,SAAS,CAAC,QAAQ,CAAC,cAAc,gBAAuB;IACxD,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAA6B;IAE5D,SAAS,CAAC,MAAM,CAAC,EAAE,uBAAuB,CAAC;IAE3C;;;;;;;OAOG;IACH,OAAO,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAkBrC;;;;;;;;;OASG;IACH,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI;IAI7D,OAAO,IAAI,IAAI;CAMjB"}
1
+ {"version":3,"file":"channel-data-port.d.ts","sourceRoot":"","sources":["../../src/browser/channel-data-port.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AACvI,OAAO,EAIJ,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC7B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,cAAc,EAAO,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAGzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,KAAK,uBAAuB,EAAyB,MAAM,sBAAsB,CAAC;AAG3F;;;;;;;;;;;GAWG;AACH,8BACsB,eAAgB,YAAW,QAAQ;IACpB,SAAS,CAAC,QAAQ,CAAC,kBAAkB,EAAG,yBAAyB,CAAC;IAC1E,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAG,gBAAgB,CAAC;IACzD,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAG,cAAc,CAAC;IAC/C,SAAS,CAAC,QAAQ,CAAC,kBAAkB,EAAG,kBAAkB,CAAC;IAChE,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAG,aAAa,CAAC;IAEjE,qFAAqF;IACrF,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAEhD;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,yBAAyB,EAAE,OAAO,CAAQ;IAE7D,SAAS,CAAC,QAAQ,CAAC,cAAc,gBAAuB;IACxD,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAA6B;IAE5D,SAAS,CAAC,MAAM,CAAC,EAAE,uBAAuB,CAAC;IAE3C,6FAA6F;IAC7F,SAAS,CAAC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAU;IAC3D,SAAS,CAAC,OAAO,CAAC,EAAE,iBAAiB,CAAC;IACtC,SAAS,CAAC,YAAY,CAAC,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;IACvD,6FAA6F;IAC7F,SAAS,CAAC,kBAAkB,UAAS;IAErC,iFAAiF;IACjF,QAAQ,CAAC,mBAAmB,EAAE,sBAAsB,CAclD;IAEF;;;;;;;OAOG;IACH,OAAO,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAkBrC;;;;;;;;;OASG;IACH,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI;IAW7D,8DAA8D;IAC9D,SAAS,CAAC,aAAa,CAAC,MAAM,GAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAqC,GAAG,IAAI;IAS5G,SAAS,KAAK,gBAAgB,IAAI,gBAAgB,CAKjD;IACD,sEAAsE;IACtE,SAAS,CAAC,sBAAsB,CAAC,EAAE,gBAAgB,CAAC;IAEpD,SAAS,CAAC,kBAAkB,IAAI,MAAM;IAItC,SAAS,CAAC,qBAAqB,IAAI,MAAM;IAQzC,OAAO,IAAI,IAAI;CAOjB"}
@@ -18,11 +18,12 @@ var __metadata = (this && this.__metadata) || function (k, v) {
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
20
  exports.ChannelDataPort = void 0;
21
+ const browser_1 = require("@hydranium/client-theia/lib/browser");
21
22
  const protocol_1 = require("@hydranium/protocol");
22
23
  const core_1 = require("@theia/core");
23
24
  const service_connection_provider_1 = require("@theia/core/lib/browser/messaging/service-connection-provider");
24
25
  const inversify_1 = require("@theia/core/shared/inversify");
25
- const browser_1 = require("@theia/workspace/lib/browser");
26
+ const browser_2 = require("@theia/workspace/lib/browser");
26
27
  const channel_connection_1 = require("./channel-connection");
27
28
  const workspace_gate_1 = require("./workspace-gate");
28
29
  /**
@@ -41,6 +42,8 @@ let ChannelDataPort = class ChannelDataPort {
41
42
  connectionProvider;
42
43
  workspaceService;
43
44
  messageService;
45
+ connectionReporter;
46
+ logger;
44
47
  /**
45
48
  * Re-open the channel when the current connection is lost. Default `true` —
46
49
  * re-opening is the only thing that recovers a restarted language server,
@@ -51,6 +54,28 @@ let ChannelDataPort = class ChannelDataPort {
51
54
  disposeEmitter = new core_1.Emitter();
52
55
  onDispose = this.disposeEmitter.event;
53
56
  handle;
57
+ /** How long a connection may take before it is reported as failed; it keeps trying after. */
58
+ connectFailureNoticeMs = 30_000;
59
+ attempt;
60
+ attemptTimer;
61
+ /** Set once {@link connectionLifecycle} reports, which then owns the connection failures. */
62
+ reportsConnections = false;
63
+ /** Reports each connection generation through the {@link ConnectionReporter}. */
64
+ connectionLifecycle = {
65
+ onConnecting: () => {
66
+ this.reportsConnections = true;
67
+ this.settleAttempt();
68
+ const attempt = this.connectionReporter.connecting(this.connectionTarget);
69
+ this.attempt = attempt;
70
+ this.attemptTimer = setTimeout(() => {
71
+ attempt.failed(this.connectTimeoutMessage());
72
+ // The generation keeps waiting; a follow-up attempt reports it connecting.
73
+ this.attempt = this.connectionReporter.connecting(this.connectionTarget);
74
+ }, this.connectFailureNoticeMs);
75
+ },
76
+ onReady: () => this.settleAttempt(attempt => attempt.connected()),
77
+ onFailed: () => this.settleAttempt(attempt => attempt.failed(this.unreachableMessage()))
78
+ };
54
79
  /**
55
80
  * Open the workspace-gated channel and hand back its listening connection.
56
81
  *
@@ -87,9 +112,40 @@ let ChannelDataPort = class ChannelDataPort {
87
112
  * inside another's and leave no translator in control of the whole.
88
113
  */
89
114
  reportError(_error, reported) {
90
- this.messageService.error((0, protocol_1.renderFrameworkMessage)(reported, core_1.nls.localization?.translations));
115
+ const connectionFailure = reported.code === protocol_1.DATA_SERVER_CONNECT_FAILED.code || reported.code === protocol_1.DATA_SERVER_NOT_READY.code;
116
+ const message = (0, protocol_1.renderFrameworkMessage)(reported, core_1.nls.localization?.translations);
117
+ if (connectionFailure && this.reportsConnections) {
118
+ // The reporter shows the failure without its detail, so it is kept here.
119
+ this.logger.warn(message);
120
+ return;
121
+ }
122
+ this.messageService.error(message);
123
+ }
124
+ /** End the current attempt through `settle`, or cancel it. */
125
+ settleAttempt(settle = attempt => attempt.cancelled()) {
126
+ clearTimeout(this.attemptTimer);
127
+ const attempt = this.attempt;
128
+ this.attempt = undefined;
129
+ if (attempt) {
130
+ settle(attempt);
131
+ }
132
+ }
133
+ get connectionTarget() {
134
+ return (this.cachedConnectionTarget ??= {
135
+ connectingMessage: core_1.nls.localize('hydranium/data-client-theia/data-server-connecting', 'Connecting to the data server…'),
136
+ connectedMessage: core_1.nls.localize('hydranium/data-client-theia/data-server-connected', 'Connected to the data server.')
137
+ });
138
+ }
139
+ /** One object per port: the reporter keys what it has shown on it. */
140
+ cachedConnectionTarget;
141
+ unreachableMessage() {
142
+ return core_1.nls.localize('hydranium/data-client-theia/data-server-unreachable', 'Could not connect to the data server.');
143
+ }
144
+ connectTimeoutMessage() {
145
+ return core_1.nls.localize('hydranium/data-client-theia/data-server-timeout', 'The data server did not answer within {0} seconds.', String(Math.round(this.connectFailureNoticeMs / 1000)));
91
146
  }
92
147
  dispose() {
148
+ this.settleAttempt();
93
149
  this.handle?.dispose();
94
150
  this.handle = undefined;
95
151
  this.disposeEmitter.fire(undefined);
@@ -102,13 +158,21 @@ __decorate([
102
158
  __metadata("design:type", Function)
103
159
  ], ChannelDataPort.prototype, "connectionProvider", void 0);
104
160
  __decorate([
105
- (0, inversify_1.inject)(browser_1.WorkspaceService),
106
- __metadata("design:type", browser_1.WorkspaceService)
161
+ (0, inversify_1.inject)(browser_2.WorkspaceService),
162
+ __metadata("design:type", browser_2.WorkspaceService)
107
163
  ], ChannelDataPort.prototype, "workspaceService", void 0);
108
164
  __decorate([
109
165
  (0, inversify_1.inject)(core_1.MessageService),
110
166
  __metadata("design:type", core_1.MessageService)
111
167
  ], ChannelDataPort.prototype, "messageService", void 0);
168
+ __decorate([
169
+ (0, inversify_1.inject)(browser_1.ConnectionReporter),
170
+ __metadata("design:type", Object)
171
+ ], ChannelDataPort.prototype, "connectionReporter", void 0);
172
+ __decorate([
173
+ (0, inversify_1.inject)(browser_1.ChannelLogger),
174
+ __metadata("design:type", browser_1.ChannelLogger)
175
+ ], ChannelDataPort.prototype, "logger", void 0);
112
176
  exports.ChannelDataPort = ChannelDataPort = __decorate([
113
177
  (0, inversify_1.injectable)()
114
178
  ], ChannelDataPort);
@@ -1 +1 @@
1
- {"version":3,"file":"channel-data-port.js","sourceRoot":"","sources":["../../src/browser/channel-data-port.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;;;;;;;;;;AAElF,kDAAkG;AAClG,sCAAuE;AAEvE,+GAAyG;AACzG,4DAAkE;AAClE,0DAAgE;AAEhE,6DAA2F;AAC3F,qDAAqD;AAErD;;;;;;;;;;;GAWG;AAEI,IAAe,eAAe,GAA9B,MAAe,eAAe;IACmB,kBAAkB,CAA6B;IACvD,gBAAgB,CAAoB;IACtC,cAAc,CAAkB;IAK3E;;;;;OAKG;IACgB,yBAAyB,GAAY,IAAI,CAAC;IAE1C,cAAc,GAAG,IAAI,cAAO,EAAQ,CAAC;IAC/C,SAAS,GAAgB,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;IAElD,MAAM,CAA2B;IAE3C;;;;;;;OAOG;IACH,OAAO;QACJ,sEAAsE;QACtE,sEAAsE;QACtE,iEAAiE;QACjE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,GAAG,IAAA,0CAAqB,EAAC,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,WAAW,EAAE;gBAC5E,SAAS,EAAE,IAAA,kCAAiB,EAAC,IAAI,CAAC,gBAAgB,CAAC;gBACnD,SAAS,EAAE,IAAI,CAAC,yBAAyB;aAC3C,CAAC,CAAC;YACH,qEAAqE;YACrE,iEAAiE;YACjE,oEAAoE;YACpE,sCAAsC;YACtC,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QAC9E,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;IAC9B,CAAC;IAED;;;;;;;;;OASG;IACH,WAAW,CAAC,MAAe,EAAE,QAAyB;QACnD,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,IAAA,iCAAsB,EAAC,QAAQ,EAAE,UAAG,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;IAC/F,CAAC;IAED,OAAO;QACJ,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACpC,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;IACjC,CAAC;CACH,CAAA;AAnEqB,0CAAe;AACmB;IAApD,IAAA,kBAAM,EAAC,sDAAwB,CAAC;;2DAAmE;AACvD;IAA5C,IAAA,kBAAM,EAAC,0BAAgB,CAAC;8BAAuC,0BAAgB;yDAAC;AACtC;IAA1C,IAAA,kBAAM,EAAC,qBAAc,CAAC;8BAAqC,qBAAc;uDAAC;0BAHxD,eAAe;IADpC,IAAA,sBAAU,GAAE;GACS,eAAe,CAmEpC"}
1
+ {"version":3,"file":"channel-data-port.js","sourceRoot":"","sources":["../../src/browser/channel-data-port.ts"],"names":[],"mappings":"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"}
@@ -24,15 +24,7 @@ export interface DataServerConnectionHandlerOptions {
24
24
  * Defaults to the framework `DATA_SERVER_PORT_COMMAND`; override to match
25
25
  * an adopter's established id. */
26
26
  readonly portCommand?: string;
27
- /**
28
- * Product name for the connect-failure dialog this handler raises.
29
- *
30
- * It reaches the adopter's UI verbatim, so the framework default leaks a
31
- * framework noun into a product that is not ours. Not a translation concern —
32
- * routing it through a catalogue would ask an adopter to "translate" English
33
- * into their own product name, and would make their branding
34
- * locale-dependent.
35
- */
27
+ /** Name of the server in this handler's log lines. */
36
28
  readonly serverName?: string;
37
29
  readonly findPortTimeout?: number;
38
30
  readonly findPortAttempts?: number;
@@ -57,5 +49,8 @@ export interface DataServerConnectionHandlerOptions {
57
49
  */
58
50
  export declare class DataServerConnectionHandler extends AbstractSocketForwardingConnectionHandler {
59
51
  constructor(options?: DataServerConnectionHandlerOptions);
52
+ /** Logged only: the frontend port reports the connection, and a second
53
+ * notification would duplicate it. */
54
+ protected reportConnectFailure(): void;
60
55
  }
61
56
  //# sourceMappingURL=data-server-connection-handler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"data-server-connection-handler.d.ts","sourceRoot":"","sources":["../../src/node/data-server-connection-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,yCAAyC,EAAE,MAAM,kCAAkC,CAAC;AAG7F,OAAO,KAAK,KAAK,GAAG,MAAM,UAAU,CAAC;AAErC;;;;kBAIkB;AAClB,MAAM,WAAW,kCAAkC;IAChD;;;;yDAIqD;IACrD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;uCAEmC;IACnC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;OAQG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,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;;;;;;;;GAQG;AACH,qBACa,2BAA4B,SAAQ,yCAAyC;gBAC9D,OAAO,GAAE,kCAAuC;CAY3E"}
1
+ {"version":3,"file":"data-server-connection-handler.d.ts","sourceRoot":"","sources":["../../src/node/data-server-connection-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,yCAAyC,EAAE,MAAM,kCAAkC,CAAC;AAG7F,OAAO,KAAK,KAAK,GAAG,MAAM,UAAU,CAAC;AAErC;;;;kBAIkB;AAClB,MAAM,WAAW,kCAAkC;IAChD;;;;yDAIqD;IACrD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;uCAEmC;IACnC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,sDAAsD;IACtD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,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;;;;;;;;GAQG;AACH,qBACa,2BAA4B,SAAQ,yCAAyC;gBAC9D,OAAO,GAAE,kCAAuC;IAazE;2CACuC;cACpB,oBAAoB,IAAI,IAAI;CACjD"}
@@ -39,13 +39,16 @@ let DataServerConnectionHandler = class DataServerConnectionHandler extends node
39
39
  path: options.servicePath ?? protocol_1.DATA_SERVER_PATH,
40
40
  portCommand: options.portCommand ?? protocol_1.DATA_SERVER_PORT_COMMAND,
41
41
  logComponent: 'DataServer',
42
- serverName: options.serverName ?? 'Model Server',
42
+ serverName: options.serverName ?? 'Data Server',
43
43
  findPortTimeout: options.findPortTimeout,
44
44
  findPortAttempts: options.findPortAttempts,
45
45
  connectTimeoutMs: options.connectTimeoutMs,
46
46
  onSocketCreated: options.onSocketCreated
47
47
  });
48
48
  }
49
+ /** Logged only: the frontend port reports the connection, and a second
50
+ * notification would duplicate it. */
51
+ reportConnectFailure() { }
49
52
  };
50
53
  exports.DataServerConnectionHandler = DataServerConnectionHandler;
51
54
  exports.DataServerConnectionHandler = DataServerConnectionHandler = __decorate([
@@ -1 +1 @@
1
- {"version":3,"file":"data-server-connection-handler.js","sourceRoot":"","sources":["../../src/node/data-server-connection-handler.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;;;;;;;;;;;;;AAElF,2DAA6F;AAC7F,kDAAiF;AACjF,4DAAqE;AA0CrE;;;;;;;;GAQG;AAEI,IAAM,2BAA2B,GAAjC,MAAM,2BAA4B,SAAQ,gDAAyC;IACvF,YAAyB,UAA8C,EAAE;QACtE,KAAK,CAAC;YACH,IAAI,EAAE,OAAO,CAAC,WAAW,IAAI,2BAAgB;YAC7C,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,mCAAwB;YAC5D,YAAY,EAAE,YAAY;YAC1B,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,cAAc;YAChD,eAAe,EAAE,OAAO,CAAC,eAAe;YACxC,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;YAC1C,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;YAC1C,eAAe,EAAE,OAAO,CAAC,eAAe;SAC1C,CAAC,CAAC;IACN,CAAC;CACH,CAAA;AAbY,kEAA2B;sCAA3B,2BAA2B;IADvC,IAAA,sBAAU,GAAE;IAEG,WAAA,IAAA,qBAAS,GAAE,CAAA;;GADd,2BAA2B,CAavC"}
1
+ {"version":3,"file":"data-server-connection-handler.js","sourceRoot":"","sources":["../../src/node/data-server-connection-handler.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;;;;;;;;;;;;;AAElF,2DAA6F;AAC7F,kDAAiF;AACjF,4DAAqE;AAkCrE;;;;;;;;GAQG;AAEI,IAAM,2BAA2B,GAAjC,MAAM,2BAA4B,SAAQ,gDAAyC;IACvF,YAAyB,UAA8C,EAAE;QACtE,KAAK,CAAC;YACH,IAAI,EAAE,OAAO,CAAC,WAAW,IAAI,2BAAgB;YAC7C,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,mCAAwB;YAC5D,YAAY,EAAE,YAAY;YAC1B,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,aAAa;YAC/C,eAAe,EAAE,OAAO,CAAC,eAAe;YACxC,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;YAC1C,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;YAC1C,eAAe,EAAE,OAAO,CAAC,eAAe;SAC1C,CAAC,CAAC;IACN,CAAC;IAED;2CACuC;IACpB,oBAAoB,KAAU,CAAC;CACpD,CAAA;AAjBY,kEAA2B;sCAA3B,2BAA2B;IADvC,IAAA,sBAAU,GAAE;IAEG,WAAA,IAAA,qBAAS,GAAE,CAAA;;GADd,2BAA2B,CAiBvC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hydranium/data-client-theia",
3
- "version": "1.0.0-next.160",
3
+ "version": "1.0.0-next.168",
4
4
  "description": "Theia-coupled client primitives for the hydranium data-server protocol head; companion of @hydranium/data-server. Bridges framework typed errors (e.g. ConflictError) across Theia's RPC boundary so their code + data survive the wire.",
5
5
  "keywords": [
6
6
  "hydranium",
@@ -70,9 +70,9 @@
70
70
  "watch": "tsc -b -w --preserveWatchOutput"
71
71
  },
72
72
  "devDependencies": {
73
- "@hydranium/client-theia": "1.0.0-next.160",
74
- "@hydranium/core": "1.0.0-next.160",
75
- "@hydranium/protocol": "1.0.0-next.160",
73
+ "@hydranium/client-theia": "1.0.0-next.168",
74
+ "@hydranium/core": "1.0.0-next.168",
75
+ "@hydranium/protocol": "1.0.0-next.168",
76
76
  "@theia/core": "^1.70.0",
77
77
  "@theia/workspace": "^1.70.0",
78
78
  "@types/node": "^22.0.0",
@@ -83,9 +83,9 @@
83
83
  "vscode-jsonrpc": "9.0.1"
84
84
  },
85
85
  "peerDependencies": {
86
- "@hydranium/client-theia": "1.0.0-next.160",
87
- "@hydranium/core": "1.0.0-next.160",
88
- "@hydranium/protocol": "1.0.0-next.160",
86
+ "@hydranium/client-theia": "1.0.0-next.168",
87
+ "@hydranium/core": "1.0.0-next.168",
88
+ "@hydranium/protocol": "1.0.0-next.168",
89
89
  "@theia/core": "^1.70.0",
90
90
  "@theia/workspace": "^1.70.0",
91
91
  "inversify": "^6.0.0",
@@ -7,7 +7,15 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
 
10
- import { renderFrameworkMessage, type DataPort, type ResolvedMessage } from '@hydranium/protocol';
10
+ import { ChannelLogger, ConnectionReporter, type ConnectionAttempt, type ConnectionTarget } from '@hydranium/client-theia/lib/browser';
11
+ import {
12
+ DATA_SERVER_CONNECT_FAILED,
13
+ DATA_SERVER_NOT_READY,
14
+ renderFrameworkMessage,
15
+ type DataPort,
16
+ type ResolvedMessage,
17
+ type RpcConnectionLifecycle
18
+ } from '@hydranium/protocol';
11
19
  import { Emitter, MessageService, nls, type Event } from '@theia/core';
12
20
  import { type ServiceConnectionProvider } from '@theia/core/lib/browser';
13
21
  import { RemoteConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider';
@@ -34,6 +42,8 @@ export abstract class ChannelDataPort implements DataPort {
34
42
  @inject(RemoteConnectionProvider) protected readonly connectionProvider!: ServiceConnectionProvider;
35
43
  @inject(WorkspaceService) protected readonly workspaceService!: WorkspaceService;
36
44
  @inject(MessageService) protected readonly messageService!: MessageService;
45
+ @inject(ConnectionReporter) protected readonly connectionReporter!: ConnectionReporter;
46
+ @inject(ChannelLogger) protected readonly logger!: ChannelLogger;
37
47
 
38
48
  /** Frontend service path the backend forwarder for this head is registered under. */
39
49
  protected abstract readonly servicePath: string;
@@ -51,6 +61,30 @@ export abstract class ChannelDataPort implements DataPort {
51
61
 
52
62
  protected handle?: ChannelConnectionHandle;
53
63
 
64
+ /** How long a connection may take before it is reported as failed; it keeps trying after. */
65
+ protected readonly connectFailureNoticeMs: number = 30_000;
66
+ protected attempt?: ConnectionAttempt;
67
+ protected attemptTimer?: ReturnType<typeof setTimeout>;
68
+ /** Set once {@link connectionLifecycle} reports, which then owns the connection failures. */
69
+ protected reportsConnections = false;
70
+
71
+ /** Reports each connection generation through the {@link ConnectionReporter}. */
72
+ readonly connectionLifecycle: RpcConnectionLifecycle = {
73
+ onConnecting: () => {
74
+ this.reportsConnections = true;
75
+ this.settleAttempt();
76
+ const attempt = this.connectionReporter.connecting(this.connectionTarget);
77
+ this.attempt = attempt;
78
+ this.attemptTimer = setTimeout(() => {
79
+ attempt.failed(this.connectTimeoutMessage());
80
+ // The generation keeps waiting; a follow-up attempt reports it connecting.
81
+ this.attempt = this.connectionReporter.connecting(this.connectionTarget);
82
+ }, this.connectFailureNoticeMs);
83
+ },
84
+ onReady: () => this.settleAttempt(attempt => attempt.connected()),
85
+ onFailed: () => this.settleAttempt(attempt => attempt.failed(this.unreachableMessage()))
86
+ };
87
+
54
88
  /**
55
89
  * Open the workspace-gated channel and hand back its listening connection.
56
90
  *
@@ -88,10 +122,49 @@ export abstract class ChannelDataPort implements DataPort {
88
122
  * inside another's and leave no translator in control of the whole.
89
123
  */
90
124
  reportError(_error: unknown, reported: ResolvedMessage): void {
91
- this.messageService.error(renderFrameworkMessage(reported, nls.localization?.translations));
125
+ const connectionFailure = reported.code === DATA_SERVER_CONNECT_FAILED.code || reported.code === DATA_SERVER_NOT_READY.code;
126
+ const message = renderFrameworkMessage(reported, nls.localization?.translations);
127
+ if (connectionFailure && this.reportsConnections) {
128
+ // The reporter shows the failure without its detail, so it is kept here.
129
+ this.logger.warn(message);
130
+ return;
131
+ }
132
+ this.messageService.error(message);
133
+ }
134
+
135
+ /** End the current attempt through `settle`, or cancel it. */
136
+ protected settleAttempt(settle: (attempt: ConnectionAttempt) => void = attempt => attempt.cancelled()): void {
137
+ clearTimeout(this.attemptTimer);
138
+ const attempt = this.attempt;
139
+ this.attempt = undefined;
140
+ if (attempt) {
141
+ settle(attempt);
142
+ }
143
+ }
144
+
145
+ protected get connectionTarget(): ConnectionTarget {
146
+ return (this.cachedConnectionTarget ??= {
147
+ connectingMessage: nls.localize('hydranium/data-client-theia/data-server-connecting', 'Connecting to the data server…'),
148
+ connectedMessage: nls.localize('hydranium/data-client-theia/data-server-connected', 'Connected to the data server.')
149
+ });
150
+ }
151
+ /** One object per port: the reporter keys what it has shown on it. */
152
+ protected cachedConnectionTarget?: ConnectionTarget;
153
+
154
+ protected unreachableMessage(): string {
155
+ return nls.localize('hydranium/data-client-theia/data-server-unreachable', 'Could not connect to the data server.');
156
+ }
157
+
158
+ protected connectTimeoutMessage(): string {
159
+ return nls.localize(
160
+ 'hydranium/data-client-theia/data-server-timeout',
161
+ 'The data server did not answer within {0} seconds.',
162
+ String(Math.round(this.connectFailureNoticeMs / 1000))
163
+ );
92
164
  }
93
165
 
94
166
  dispose(): void {
167
+ this.settleAttempt();
95
168
  this.handle?.dispose();
96
169
  this.handle = undefined;
97
170
  this.disposeEmitter.fire(undefined);
@@ -28,15 +28,7 @@ export interface DataServerConnectionHandlerOptions {
28
28
  * Defaults to the framework `DATA_SERVER_PORT_COMMAND`; override to match
29
29
  * an adopter's established id. */
30
30
  readonly portCommand?: string;
31
- /**
32
- * Product name for the connect-failure dialog this handler raises.
33
- *
34
- * It reaches the adopter's UI verbatim, so the framework default leaks a
35
- * framework noun into a product that is not ours. Not a translation concern —
36
- * routing it through a catalogue would ask an adopter to "translate" English
37
- * into their own product name, and would make their branding
38
- * locale-dependent.
39
- */
31
+ /** Name of the server in this handler's log lines. */
40
32
  readonly serverName?: string;
41
33
  readonly findPortTimeout?: number;
42
34
  readonly findPortAttempts?: number;
@@ -67,11 +59,15 @@ export class DataServerConnectionHandler extends AbstractSocketForwardingConnect
67
59
  path: options.servicePath ?? DATA_SERVER_PATH,
68
60
  portCommand: options.portCommand ?? DATA_SERVER_PORT_COMMAND,
69
61
  logComponent: 'DataServer',
70
- serverName: options.serverName ?? 'Model Server',
62
+ serverName: options.serverName ?? 'Data Server',
71
63
  findPortTimeout: options.findPortTimeout,
72
64
  findPortAttempts: options.findPortAttempts,
73
65
  connectTimeoutMs: options.connectTimeoutMs,
74
66
  onSocketCreated: options.onSocketCreated
75
67
  });
76
68
  }
69
+
70
+ /** Logged only: the frontend port reports the connection, and a second
71
+ * notification would duplicate it. */
72
+ protected override reportConnectFailure(): void {}
77
73
  }