@hydranium/data-client-theia 1.0.0-next.123 → 1.0.0-next.127

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.
Files changed (32) hide show
  1. package/README.md +9 -4
  2. package/lib/browser/data-session-stop-contribution.d.ts +64 -0
  3. package/lib/browser/data-session-stop-contribution.d.ts.map +1 -0
  4. package/lib/browser/data-session-stop-contribution.js +117 -0
  5. package/lib/browser/data-session-stop-contribution.js.map +1 -0
  6. package/lib/browser/index.d.ts +1 -0
  7. package/lib/browser/index.d.ts.map +1 -1
  8. package/lib/browser/index.js +1 -0
  9. package/lib/browser/index.js.map +1 -1
  10. package/lib/common/emitter-data-client.d.ts +5 -1
  11. package/lib/common/emitter-data-client.d.ts.map +1 -1
  12. package/lib/common/emitter-data-client.js +6 -0
  13. package/lib/common/emitter-data-client.js.map +1 -1
  14. package/lib/node/data-server-connection-handler.d.ts +4 -9
  15. package/lib/node/data-server-connection-handler.d.ts.map +1 -1
  16. package/lib/node/data-server-connection-handler.js +4 -11
  17. package/lib/node/data-server-connection-handler.js.map +1 -1
  18. package/lib/node/index.d.ts +0 -1
  19. package/lib/node/index.d.ts.map +1 -1
  20. package/lib/node/index.js +0 -1
  21. package/lib/node/index.js.map +1 -1
  22. package/package.json +7 -7
  23. package/src/browser/data-session-stop-contribution.ts +117 -0
  24. package/src/browser/index.ts +1 -0
  25. package/src/common/emitter-data-client.ts +9 -0
  26. package/src/node/data-server-connection-handler.ts +4 -13
  27. package/src/node/index.ts +0 -1
  28. package/lib/node/socket-channel-forwarder.d.ts +0 -34
  29. package/lib/node/socket-channel-forwarder.d.ts.map +0 -1
  30. package/lib/node/socket-channel-forwarder.js +0 -68
  31. package/lib/node/socket-channel-forwarder.js.map +0 -1
  32. package/src/node/socket-channel-forwarder.ts +0 -69
package/README.md CHANGED
@@ -41,8 +41,13 @@ text edits.
41
41
  - **`whenWorkspaceOpen`** — resolves once Theia reports a workspace root. The data
42
42
  server only starts once the LSP launches for a workspace, so connecting earlier
43
43
  would hang in port discovery; pass this as `whenReady`.
44
- - **Backend (`./node`)** — `DataServerConnectionHandler` (the socket bridge, with
45
- its own GLSP-free `SocketChannelForwarder`),
44
+ - **`DataSessionStopContribution`** — ends a frontend's data sessions with its
45
+ page, so the server ends them as closed rather than waiting for the connection
46
+ to go, and holds the page while one of them is saving. Bind it as a
47
+ `FrontendApplicationContribution` and call its `track(connection)` once, where
48
+ the connection is bound.
49
+ - **Backend (`./node`)** — `DataServerConnectionHandler` (the socket bridge,
50
+ over `@hydranium/client-theia`'s `SocketChannelForwarder`),
46
51
  `createDataServerConnectionContainerModule(...handlers)` for the
47
52
  frontend-scoped module boilerplate, and `HostDiagnosticsServer` with
48
53
  `createHostDiagnosticsBackendModule()` — which, paired with the browser-side
@@ -102,8 +107,8 @@ its loading state indefinitely with nothing in the server log to say why.
102
107
  | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
103
108
  | `.` | Nothing — the surface is split by environment, so the root barrel stays empty. | browser-neutral (gated) |
104
109
  | `./common` | `EmitterDataClient` — a module lands here only when values, types and relative imports are all neutral or Theia COMMON tier. No error-reconstruction bridge, and that is a property of the transport: the direct `vscode-jsonrpc` connection carries a typed error across the relay natively. | browser-neutral (gated) |
105
- | `./browser` | `openChannelConnection`, `createChannelConnection`, the three `Abstract*DataServiceFrontend` bases, `bindHostDiagnostics`, `whenWorkspaceOpen` | browser / Theia frontend (gated) |
106
- | `./node` | `DataServerConnectionHandler`, `createDataServerConnectionContainerModule`, `SocketChannelForwarder`, `HostDiagnosticsServer`, `createHostDiagnosticsBackendModule` | Node / Theia backend |
110
+ | `./browser` | `openChannelConnection`, `createChannelConnection`, the three `Abstract*DataServiceFrontend` bases, `bindHostDiagnostics`, `whenWorkspaceOpen`, `DataSessionStopContribution` | browser / Theia frontend (gated) |
111
+ | `./node` | `DataServerConnectionHandler`, `createDataServerConnectionContainerModule`, `HostDiagnosticsServer`, `createHostDiagnosticsBackendModule` | Node / Theia backend |
107
112
 
108
113
  Every subpath also has a `./lib/<name>` twin for consumers on
109
114
  `moduleResolution: "Node"`. "Gated" means the repository's neutral-bundle check
@@ -0,0 +1,64 @@
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 type { DataConnection, DataServerProtocol, DataSession, DiagnosticOf, TransferElement } from '@hydranium/protocol';
10
+ import type { FrontendApplicationContribution, OnWillStopAction } from '@theia/core/lib/browser';
11
+ /**
12
+ * Ends a Theia frontend's data sessions with its page, and holds the page
13
+ * while one of them is still saving. Bind it as a
14
+ * `FrontendApplicationContribution` in singleton scope and {@link track} each
15
+ * data connection the frontend binds, once.
16
+ *
17
+ * **Stop.** {@link onStop} disposes every tracked session, which sends its
18
+ * `closeSession`, so the server ends it as closed: each document it was the
19
+ * last to hold reverts at once. Otherwise the server learns of the page only
20
+ * when its connection goes, which Theia may hold open for its reconnect
21
+ * timeout, and then ends the sessions as lost, so their documents wait out the
22
+ * revert grace. The close is best effort: under load the page's last frames
23
+ * can be lost on the way, and the server then ends the sessions as lost. A
24
+ * session with calls still in flight at the stop closes after them, which is
25
+ * too late for a page going away, and is left to the server's connection-close
26
+ * cleanup. A call a session makes in the same tick as the stop is refused and
27
+ * never sent.
28
+ *
29
+ * **Veto.** While a tracked session has a save in flight, {@link onWillStop}
30
+ * vetoes, including a session disposed meanwhile. In Electron, Theia awaits
31
+ * the veto's action, which waits for the saves to answer, a save started during
32
+ * the wait included, up to {@link maxSettlePasses} times. The browser runs no
33
+ * action before unload, so there the veto shows the leave-page prompt and
34
+ * nothing more. Theia ignores every veto under
35
+ * `application.confirmExit: 'never'`, so there a save in flight at the stop
36
+ * may be cut off.
37
+ */
38
+ export declare class DataSessionStopContribution implements FrontendApplicationContribution {
39
+ /** The sessions to end at the stop, and to wait for while they save. */
40
+ protected readonly sessions: Set<DataSession<TransferElement, DataServerProtocol<TransferElement, import("@hydranium/protocol").TransferDiagnostic, import("@hydranium/protocol").Project>>>;
41
+ /**
42
+ * How many times the veto's action waits for the saves in flight before it
43
+ * lets the stop go. Each wait ends by the session's own bound, so a save
44
+ * that never answers holds the stop for a few bounds, not for good.
45
+ */
46
+ protected readonly maxSettlePasses: number;
47
+ /**
48
+ * Take every session of `connection` into the stop: the ones it has
49
+ * started already, and each one it starts from now on. Generic, so a
50
+ * connection of any transfer root, server and client fits.
51
+ */
52
+ track<TTransfer extends TransferElement, TServer extends DataServerProtocol<TTransfer, DiagnosticOf<TServer>>, TClient extends object>(connection: DataConnection<TTransfer, TServer, TClient>): void;
53
+ /**
54
+ * Take `session` into the stop. It leaves once it has ended and its saves
55
+ * have answered, so a session disposed with a save in flight still holds
56
+ * the page.
57
+ */
58
+ protected trackSession(session: DataSession<TransferElement>): void;
59
+ onWillStop(): OnWillStopAction | undefined;
60
+ /** The tracked sessions with a save in flight. */
61
+ protected savingSessions(): DataSession<TransferElement>[];
62
+ onStop(): void;
63
+ }
64
+ //# sourceMappingURL=data-session-stop-contribution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-session-stop-contribution.d.ts","sourceRoot":"","sources":["../../src/browser/data-session-stop-contribution.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAG1H,OAAO,KAAK,EAAE,+BAA+B,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGjG;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBACa,2BAA4B,YAAW,+BAA+B;IAChF,wEAAwE;IACxE,SAAS,CAAC,QAAQ,CAAC,QAAQ,kKAA2C;IACtE;;;;OAIG;IACH,SAAS,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAK;IAE/C;;;;OAIG;IACH,KAAK,CAAC,SAAS,SAAS,eAAe,EAAE,OAAO,SAAS,kBAAkB,CAAC,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,SAAS,MAAM,EAClI,UAAU,EAAE,cAAc,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,GACvD,IAAI;IAKP;;;;OAIG;IACH,SAAS,CAAC,YAAY,CAAC,OAAO,EAAE,WAAW,CAAC,eAAe,CAAC,GAAG,IAAI;IAcnE,UAAU,IAAI,gBAAgB,GAAG,SAAS;IAsB1C,kDAAkD;IAClD,SAAS,CAAC,cAAc,IAAI,WAAW,CAAC,eAAe,CAAC,EAAE;IAI1D,MAAM,IAAI,IAAI;CAKhB"}
@@ -0,0 +1,117 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.DataSessionStopContribution = void 0;
18
+ const inversify_1 = require("@theia/core/shared/inversify");
19
+ /**
20
+ * Ends a Theia frontend's data sessions with its page, and holds the page
21
+ * while one of them is still saving. Bind it as a
22
+ * `FrontendApplicationContribution` in singleton scope and {@link track} each
23
+ * data connection the frontend binds, once.
24
+ *
25
+ * **Stop.** {@link onStop} disposes every tracked session, which sends its
26
+ * `closeSession`, so the server ends it as closed: each document it was the
27
+ * last to hold reverts at once. Otherwise the server learns of the page only
28
+ * when its connection goes, which Theia may hold open for its reconnect
29
+ * timeout, and then ends the sessions as lost, so their documents wait out the
30
+ * revert grace. The close is best effort: under load the page's last frames
31
+ * can be lost on the way, and the server then ends the sessions as lost. A
32
+ * session with calls still in flight at the stop closes after them, which is
33
+ * too late for a page going away, and is left to the server's connection-close
34
+ * cleanup. A call a session makes in the same tick as the stop is refused and
35
+ * never sent.
36
+ *
37
+ * **Veto.** While a tracked session has a save in flight, {@link onWillStop}
38
+ * vetoes, including a session disposed meanwhile. In Electron, Theia awaits
39
+ * the veto's action, which waits for the saves to answer, a save started during
40
+ * the wait included, up to {@link maxSettlePasses} times. The browser runs no
41
+ * action before unload, so there the veto shows the leave-page prompt and
42
+ * nothing more. Theia ignores every veto under
43
+ * `application.confirmExit: 'never'`, so there a save in flight at the stop
44
+ * may be cut off.
45
+ */
46
+ let DataSessionStopContribution = class DataSessionStopContribution {
47
+ /** The sessions to end at the stop, and to wait for while they save. */
48
+ sessions = new Set();
49
+ /**
50
+ * How many times the veto's action waits for the saves in flight before it
51
+ * lets the stop go. Each wait ends by the session's own bound, so a save
52
+ * that never answers holds the stop for a few bounds, not for good.
53
+ */
54
+ maxSettlePasses = 3;
55
+ /**
56
+ * Take every session of `connection` into the stop: the ones it has
57
+ * started already, and each one it starts from now on. Generic, so a
58
+ * connection of any transfer root, server and client fits.
59
+ */
60
+ track(connection) {
61
+ connection.liveSessions.forEach(session => this.trackSession(session));
62
+ connection.onDidCreateSession(session => this.trackSession(session));
63
+ }
64
+ /**
65
+ * Take `session` into the stop. It leaves once it has ended and its saves
66
+ * have answered, so a session disposed with a save in flight still holds
67
+ * the page.
68
+ */
69
+ trackSession(session) {
70
+ this.sessions.add(session);
71
+ // A save can outlast the wait's bound, so the session stays until none
72
+ // is left in flight.
73
+ const release = () => void session.whenSavesSettled().then(() => (session.hasSavesInFlight ? release() : this.sessions.delete(session)));
74
+ // An ended session has fired its event already, and never fires it again.
75
+ if (session.isDisposed) {
76
+ release();
77
+ }
78
+ else {
79
+ session.onDidDispose(release);
80
+ }
81
+ }
82
+ onWillStop() {
83
+ if (this.savingSessions().length === 0) {
84
+ return undefined;
85
+ }
86
+ return {
87
+ // Theia logs the reason and shows it nowhere, so it is not translated.
88
+ reason: 'Data sessions are saving',
89
+ action: async () => {
90
+ // The page stays usable while Theia awaits this, so a save can
91
+ // start during a wait; each pass waits for the saves then in flight.
92
+ for (let pass = 0; pass < this.maxSettlePasses; pass++) {
93
+ const saving = this.savingSessions();
94
+ if (saving.length === 0) {
95
+ break;
96
+ }
97
+ await Promise.all(saving.map(session => session.whenSavesSettled()));
98
+ }
99
+ return true;
100
+ }
101
+ };
102
+ }
103
+ /** The tracked sessions with a save in flight. */
104
+ savingSessions() {
105
+ return [...this.sessions].filter(session => session.hasSavesInFlight);
106
+ }
107
+ onStop() {
108
+ for (const session of this.sessions) {
109
+ session.dispose();
110
+ }
111
+ }
112
+ };
113
+ exports.DataSessionStopContribution = DataSessionStopContribution;
114
+ exports.DataSessionStopContribution = DataSessionStopContribution = __decorate([
115
+ (0, inversify_1.injectable)()
116
+ ], DataSessionStopContribution);
117
+ //# sourceMappingURL=data-session-stop-contribution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-session-stop-contribution.js","sourceRoot":"","sources":["../../src/browser/data-session-stop-contribution.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;;;;;;;AAMlF,4DAA0D;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEI,IAAM,2BAA2B,GAAjC,MAAM,2BAA2B;IACrC,wEAAwE;IACrD,QAAQ,GAAG,IAAI,GAAG,EAAgC,CAAC;IACtE;;;;OAIG;IACgB,eAAe,GAAW,CAAC,CAAC;IAE/C;;;;OAIG;IACH,KAAK,CACF,UAAuD;QAEvD,UAAU,CAAC,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;QACvE,UAAU,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IACxE,CAAC;IAED;;;;OAIG;IACO,YAAY,CAAC,OAAqC;QACzD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC3B,uEAAuE;QACvE,qBAAqB;QACrB,MAAM,OAAO,GAAG,GAAS,EAAE,CACxB,KAAK,OAAO,CAAC,gBAAgB,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACtH,0EAA0E;QAC1E,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACtB,OAAO,EAAE,CAAC;QACb,CAAC;aAAM,CAAC;YACL,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC;IACJ,CAAC;IAED,UAAU;QACP,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtC,OAAO,SAAS,CAAC;QACpB,CAAC;QACD,OAAO;YACJ,uEAAuE;YACvE,MAAM,EAAE,0BAA0B;YAClC,MAAM,EAAE,KAAK,IAAI,EAAE;gBAChB,+DAA+D;gBAC/D,qEAAqE;gBACrE,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC,eAAe,EAAE,IAAI,EAAE,EAAE,CAAC;oBACtD,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;oBACrC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;wBACvB,MAAM;oBACT,CAAC;oBACD,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC;gBACxE,CAAC;gBACD,OAAO,IAAI,CAAC;YACf,CAAC;SACH,CAAC;IACL,CAAC;IAED,kDAAkD;IACxC,cAAc;QACrB,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzE,CAAC;IAED,MAAM;QACH,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnC,OAAO,CAAC,OAAO,EAAE,CAAC;QACrB,CAAC;IACJ,CAAC;CACH,CAAA;AAzEY,kEAA2B;sCAA3B,2BAA2B;IADvC,IAAA,sBAAU,GAAE;GACA,2BAA2B,CAyEvC"}
@@ -8,6 +8,7 @@
8
8
  ********************************************************************************/
9
9
  export * from './channel-connection';
10
10
  export * from './channel-data-port';
11
+ export * from './data-session-stop-contribution';
11
12
  export * from './host-diagnostics-frontend';
12
13
  export * from './workspace-gate';
13
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kCAAkC,CAAC;AACjD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kBAAkB,CAAC"}
@@ -24,6 +24,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  __exportStar(require("./channel-connection"), exports);
26
26
  __exportStar(require("./channel-data-port"), exports);
27
+ __exportStar(require("./data-session-stop-contribution"), exports);
27
28
  __exportStar(require("./host-diagnostics-frontend"), exports);
28
29
  __exportStar(require("./workspace-gate"), exports);
29
30
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;;;;;;;;;;;;;;AAElF,uDAAqC;AACrC,sDAAoC;AACpC,8DAA4C;AAC5C,mDAAiC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;;;;;;;;;;;;;;AAElF,uDAAqC;AACrC,sDAAoC;AACpC,mEAAiD;AACjD,8DAA4C;AAC5C,mDAAiC"}
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
- import type { DataClientProtocol, Project, ProjectsChangedEvent, TransferDiagnostic, TransferDocumentDeletedEvent, TransferDocumentSavedEvent, TransferDocumentsBuiltEvent, TransferDocumentUpdatedEvent, TransferElement } from '@hydranium/protocol';
9
+ import type { DataClientProtocol, Project, ProjectsChangedEvent, TransferDiagnostic, TransferDocumentDeletedEvent, TransferDocumentDirtyChangedEvent, TransferDocumentSavedEvent, TransferDocumentsBuiltEvent, TransferDocumentUpdatedEvent, TransferElement } from '@hydranium/protocol';
10
10
  import { Emitter, type Event } from '@theia/core';
11
11
  /**
12
12
  * Default {@link DataClientProtocol} implementation for a Theia frontend:
@@ -30,6 +30,9 @@ export declare class EmitterDataClient<TTransfer extends TransferElement, TDiagn
30
30
  protected readonly onDocumentSavedEmitter: Emitter<TransferDocumentSavedEvent<TTransfer, TDiagnostic>>;
31
31
  /** Fires for each inbound {@link onDocumentSaved} notification. */
32
32
  readonly onDidSaveDocument: Event<TransferDocumentSavedEvent<TTransfer, TDiagnostic>>;
33
+ protected readonly onDocumentDirtyChangedEmitter: Emitter<TransferDocumentDirtyChangedEvent>;
34
+ /** Fires for each inbound {@link onDocumentDirtyChanged} notification. */
35
+ readonly onDidChangeDocumentDirty: Event<TransferDocumentDirtyChangedEvent>;
33
36
  protected readonly onDocumentDeletedEmitter: Emitter<TransferDocumentDeletedEvent>;
34
37
  /** Fires for each inbound {@link onDocumentDeleted} notification. */
35
38
  readonly onDidDeleteDocument: Event<TransferDocumentDeletedEvent>;
@@ -41,6 +44,7 @@ export declare class EmitterDataClient<TTransfer extends TransferElement, TDiagn
41
44
  readonly onDidChangeProjects: Event<ProjectsChangedEvent<TProject>>;
42
45
  onDocumentUpdated(event: TransferDocumentUpdatedEvent<TTransfer, TDiagnostic>): void;
43
46
  onDocumentSaved(event: TransferDocumentSavedEvent<TTransfer, TDiagnostic>): void;
47
+ onDocumentDirtyChanged(event: TransferDocumentDirtyChangedEvent): void;
44
48
  onDocumentDeleted(event: TransferDocumentDeletedEvent): void;
45
49
  onDocumentsBuilt(event: TransferDocumentsBuiltEvent): void;
46
50
  onProjectsChanged(event: ProjectsChangedEvent<TProject>): void;
@@ -1 +1 @@
1
- {"version":3,"file":"emitter-data-client.d.ts","sourceRoot":"","sources":["../../src/common/emitter-data-client.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,KAAK,EACT,kBAAkB,EAClB,OAAO,EACP,oBAAoB,EACpB,kBAAkB,EAClB,4BAA4B,EAC5B,0BAA0B,EAC1B,2BAA2B,EAC3B,4BAA4B,EAC5B,eAAe,EACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAElD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,iBAAiB,CAC3B,SAAS,SAAS,eAAe,EACjC,WAAW,SAAS,kBAAkB,GAAG,kBAAkB,EAC3D,QAAQ,SAAS,OAAO,GAAG,OAAO,CACnC,YAAW,kBAAkB,CAAC,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAC;IAC9D,SAAS,CAAC,QAAQ,CAAC,wBAAwB,gEAAuE;IAClH,qEAAqE;IACrE,QAAQ,CAAC,mBAAmB,EAAE,KAAK,CAAC,4BAA4B,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAuC;IAEhI,SAAS,CAAC,QAAQ,CAAC,sBAAsB,8DAAqE;IAC9G,mEAAmE;IACnE,QAAQ,CAAC,iBAAiB,EAAE,KAAK,CAAC,0BAA0B,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAqC;IAE1H,SAAS,CAAC,QAAQ,CAAC,wBAAwB,wCAA+C;IAC1F,qEAAqE;IACrE,QAAQ,CAAC,mBAAmB,EAAE,KAAK,CAAC,4BAA4B,CAAC,CAAuC;IAExG,SAAS,CAAC,QAAQ,CAAC,uBAAuB,uCAA8C;IACxF,oEAAoE;IACpE,QAAQ,CAAC,mBAAmB,EAAE,KAAK,CAAC,2BAA2B,CAAC,CAAsC;IAEtG,SAAS,CAAC,QAAQ,CAAC,wBAAwB,0CAAiD;IAC5F,qEAAqE;IACrE,QAAQ,CAAC,mBAAmB,EAAE,KAAK,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAAuC;IAE1G,iBAAiB,CAAC,KAAK,EAAE,4BAA4B,CAAC,SAAS,EAAE,WAAW,CAAC,GAAG,IAAI;IAIpF,eAAe,CAAC,KAAK,EAAE,0BAA0B,CAAC,SAAS,EAAE,WAAW,CAAC,GAAG,IAAI;IAIhF,iBAAiB,CAAC,KAAK,EAAE,4BAA4B,GAAG,IAAI;IAI5D,gBAAgB,CAAC,KAAK,EAAE,2BAA2B,GAAG,IAAI;IAI1D,iBAAiB,CAAC,KAAK,EAAE,oBAAoB,CAAC,QAAQ,CAAC,GAAG,IAAI;CAGhE"}
1
+ {"version":3,"file":"emitter-data-client.d.ts","sourceRoot":"","sources":["../../src/common/emitter-data-client.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,KAAK,EACT,kBAAkB,EAClB,OAAO,EACP,oBAAoB,EACpB,kBAAkB,EAClB,4BAA4B,EAC5B,iCAAiC,EACjC,0BAA0B,EAC1B,2BAA2B,EAC3B,4BAA4B,EAC5B,eAAe,EACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAElD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,iBAAiB,CAC3B,SAAS,SAAS,eAAe,EACjC,WAAW,SAAS,kBAAkB,GAAG,kBAAkB,EAC3D,QAAQ,SAAS,OAAO,GAAG,OAAO,CACnC,YAAW,kBAAkB,CAAC,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAC;IAC9D,SAAS,CAAC,QAAQ,CAAC,wBAAwB,gEAAuE;IAClH,qEAAqE;IACrE,QAAQ,CAAC,mBAAmB,EAAE,KAAK,CAAC,4BAA4B,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAuC;IAEhI,SAAS,CAAC,QAAQ,CAAC,sBAAsB,8DAAqE;IAC9G,mEAAmE;IACnE,QAAQ,CAAC,iBAAiB,EAAE,KAAK,CAAC,0BAA0B,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAqC;IAE1H,SAAS,CAAC,QAAQ,CAAC,6BAA6B,6CAAoD;IACpG,0EAA0E;IAC1E,QAAQ,CAAC,wBAAwB,EAAE,KAAK,CAAC,iCAAiC,CAAC,CAA4C;IAEvH,SAAS,CAAC,QAAQ,CAAC,wBAAwB,wCAA+C;IAC1F,qEAAqE;IACrE,QAAQ,CAAC,mBAAmB,EAAE,KAAK,CAAC,4BAA4B,CAAC,CAAuC;IAExG,SAAS,CAAC,QAAQ,CAAC,uBAAuB,uCAA8C;IACxF,oEAAoE;IACpE,QAAQ,CAAC,mBAAmB,EAAE,KAAK,CAAC,2BAA2B,CAAC,CAAsC;IAEtG,SAAS,CAAC,QAAQ,CAAC,wBAAwB,0CAAiD;IAC5F,qEAAqE;IACrE,QAAQ,CAAC,mBAAmB,EAAE,KAAK,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAAuC;IAE1G,iBAAiB,CAAC,KAAK,EAAE,4BAA4B,CAAC,SAAS,EAAE,WAAW,CAAC,GAAG,IAAI;IAIpF,eAAe,CAAC,KAAK,EAAE,0BAA0B,CAAC,SAAS,EAAE,WAAW,CAAC,GAAG,IAAI;IAIhF,sBAAsB,CAAC,KAAK,EAAE,iCAAiC,GAAG,IAAI;IAItE,iBAAiB,CAAC,KAAK,EAAE,4BAA4B,GAAG,IAAI;IAI5D,gBAAgB,CAAC,KAAK,EAAE,2BAA2B,GAAG,IAAI;IAI1D,iBAAiB,CAAC,KAAK,EAAE,oBAAoB,CAAC,QAAQ,CAAC,GAAG,IAAI;CAGhE"}
@@ -32,6 +32,9 @@ class EmitterDataClient {
32
32
  onDocumentSavedEmitter = new core_1.Emitter();
33
33
  /** Fires for each inbound {@link onDocumentSaved} notification. */
34
34
  onDidSaveDocument = this.onDocumentSavedEmitter.event;
35
+ onDocumentDirtyChangedEmitter = new core_1.Emitter();
36
+ /** Fires for each inbound {@link onDocumentDirtyChanged} notification. */
37
+ onDidChangeDocumentDirty = this.onDocumentDirtyChangedEmitter.event;
35
38
  onDocumentDeletedEmitter = new core_1.Emitter();
36
39
  /** Fires for each inbound {@link onDocumentDeleted} notification. */
37
40
  onDidDeleteDocument = this.onDocumentDeletedEmitter.event;
@@ -47,6 +50,9 @@ class EmitterDataClient {
47
50
  onDocumentSaved(event) {
48
51
  this.onDocumentSavedEmitter.fire(event);
49
52
  }
53
+ onDocumentDirtyChanged(event) {
54
+ this.onDocumentDirtyChangedEmitter.fire(event);
55
+ }
50
56
  onDocumentDeleted(event) {
51
57
  this.onDocumentDeletedEmitter.fire(event);
52
58
  }
@@ -1 +1 @@
1
- {"version":3,"file":"emitter-data-client.js","sourceRoot":"","sources":["../../src/common/emitter-data-client.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;AAalF,sCAAkD;AAElD;;;;;;;;;;;;;;GAcG;AACH,MAAa,iBAAiB;IAKR,wBAAwB,GAAG,IAAI,cAAO,EAAwD,CAAC;IAClH,qEAAqE;IAC5D,mBAAmB,GAAgE,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC;IAE7G,sBAAsB,GAAG,IAAI,cAAO,EAAsD,CAAC;IAC9G,mEAAmE;IAC1D,iBAAiB,GAA8D,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC;IAEvG,wBAAwB,GAAG,IAAI,cAAO,EAAgC,CAAC;IAC1F,qEAAqE;IAC5D,mBAAmB,GAAwC,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC;IAErF,uBAAuB,GAAG,IAAI,cAAO,EAA+B,CAAC;IACxF,oEAAoE;IAC3D,mBAAmB,GAAuC,IAAI,CAAC,uBAAuB,CAAC,KAAK,CAAC;IAEnF,wBAAwB,GAAG,IAAI,cAAO,EAAkC,CAAC;IAC5F,qEAAqE;IAC5D,mBAAmB,GAA0C,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC;IAE1G,iBAAiB,CAAC,KAA2D;QAC1E,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC;IAED,eAAe,CAAC,KAAyD;QACtE,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC;IAED,iBAAiB,CAAC,KAAmC;QAClD,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC;IAED,gBAAgB,CAAC,KAAkC;QAChD,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC;IAED,iBAAiB,CAAC,KAAqC;QACpD,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC;CACH;AA5CD,8CA4CC"}
1
+ {"version":3,"file":"emitter-data-client.js","sourceRoot":"","sources":["../../src/common/emitter-data-client.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;AAclF,sCAAkD;AAElD;;;;;;;;;;;;;;GAcG;AACH,MAAa,iBAAiB;IAKR,wBAAwB,GAAG,IAAI,cAAO,EAAwD,CAAC;IAClH,qEAAqE;IAC5D,mBAAmB,GAAgE,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC;IAE7G,sBAAsB,GAAG,IAAI,cAAO,EAAsD,CAAC;IAC9G,mEAAmE;IAC1D,iBAAiB,GAA8D,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC;IAEvG,6BAA6B,GAAG,IAAI,cAAO,EAAqC,CAAC;IACpG,0EAA0E;IACjE,wBAAwB,GAA6C,IAAI,CAAC,6BAA6B,CAAC,KAAK,CAAC;IAEpG,wBAAwB,GAAG,IAAI,cAAO,EAAgC,CAAC;IAC1F,qEAAqE;IAC5D,mBAAmB,GAAwC,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC;IAErF,uBAAuB,GAAG,IAAI,cAAO,EAA+B,CAAC;IACxF,oEAAoE;IAC3D,mBAAmB,GAAuC,IAAI,CAAC,uBAAuB,CAAC,KAAK,CAAC;IAEnF,wBAAwB,GAAG,IAAI,cAAO,EAAkC,CAAC;IAC5F,qEAAqE;IAC5D,mBAAmB,GAA0C,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC;IAE1G,iBAAiB,CAAC,KAA2D;QAC1E,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC;IAED,eAAe,CAAC,KAAyD;QACtE,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC;IAED,sBAAsB,CAAC,KAAwC;QAC5D,IAAI,CAAC,6BAA6B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC;IAED,iBAAiB,CAAC,KAAmC;QAClD,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC;IAED,gBAAgB,CAAC,KAAkC;QAChD,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC;IAED,iBAAiB,CAAC,KAAqC;QACpD,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC;CACH;AApDD,8CAoDC"}
@@ -7,7 +7,6 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
  import { AbstractSocketForwardingConnectionHandler } from '@hydranium/client-theia/lib/node';
10
- import { type Channel, type Disposable } from '@theia/core';
11
10
  import type * as net from 'node:net';
12
11
  /** Options for {@link DataServerConnectionHandler}. `servicePath` is the Theia
13
12
  * service path the browser frontend opens a channel to; `portCommand` is the
@@ -51,16 +50,12 @@ export interface DataServerConnectionHandlerOptions {
51
50
  * Bridges a Theia browser-frontend channel to a data-server's TCP socket — the
52
51
  * backend half of the data-server head's transport.
53
52
  *
54
- * The port-discovery + buffer-and-replay race fix + connect orchestration live
55
- * on the cross-head {@link AbstractSocketForwardingConnectionHandler} base; this
56
- * subclass supplies only the data-server defaults (service path / port command /
57
- * log labels) and plugs in {@link SocketChannelForwarder} as the byte relay —
58
- * its own forwarder, so the data head carries no GLSP dependency. Sibling of
59
- * `@hydranium/glsp-client-theia`'s `GlspServerConnectionHandler`, which
60
- * subclasses the same base with `@eclipse-glsp`'s `SocketConnectionForwarder`.
53
+ * The port discovery, the buffer-and-replay race fix, the connect orchestration
54
+ * and the byte forwarder live on the cross-head
55
+ * {@link AbstractSocketForwardingConnectionHandler} base; this subclass supplies
56
+ * only the data-server defaults (service path / port command / log labels).
61
57
  */
62
58
  export declare class DataServerConnectionHandler extends AbstractSocketForwardingConnectionHandler {
63
59
  constructor(options?: DataServerConnectionHandlerOptions);
64
- protected forwardToSocketConnection(clientChannel: Channel, socket: net.Socket): Disposable;
65
60
  }
66
61
  //# 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;AAE7F,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAE5D,OAAO,KAAK,KAAK,GAAG,MAAM,UAAU,CAAC;AAGrC;;;;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;;;;;;;;;;;GAWG;AACH,qBACa,2BAA4B,SAAQ,yCAAyC;gBAC9D,OAAO,GAAE,kCAAuC;IAazE,SAAS,CAAC,yBAAyB,CAAC,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,UAAU;CAG7F"}
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"}
@@ -24,18 +24,14 @@ exports.DataServerConnectionHandler = void 0;
24
24
  const node_1 = require("@hydranium/client-theia/lib/node");
25
25
  const protocol_1 = require("@hydranium/protocol");
26
26
  const inversify_1 = require("@theia/core/shared/inversify");
27
- const socket_channel_forwarder_1 = require("./socket-channel-forwarder");
28
27
  /**
29
28
  * Bridges a Theia browser-frontend channel to a data-server's TCP socket — the
30
29
  * backend half of the data-server head's transport.
31
30
  *
32
- * The port-discovery + buffer-and-replay race fix + connect orchestration live
33
- * on the cross-head {@link AbstractSocketForwardingConnectionHandler} base; this
34
- * subclass supplies only the data-server defaults (service path / port command /
35
- * log labels) and plugs in {@link SocketChannelForwarder} as the byte relay —
36
- * its own forwarder, so the data head carries no GLSP dependency. Sibling of
37
- * `@hydranium/glsp-client-theia`'s `GlspServerConnectionHandler`, which
38
- * subclasses the same base with `@eclipse-glsp`'s `SocketConnectionForwarder`.
31
+ * The port discovery, the buffer-and-replay race fix, the connect orchestration
32
+ * and the byte forwarder live on the cross-head
33
+ * {@link AbstractSocketForwardingConnectionHandler} base; this subclass supplies
34
+ * only the data-server defaults (service path / port command / log labels).
39
35
  */
40
36
  let DataServerConnectionHandler = class DataServerConnectionHandler extends node_1.AbstractSocketForwardingConnectionHandler {
41
37
  constructor(options = {}) {
@@ -50,9 +46,6 @@ let DataServerConnectionHandler = class DataServerConnectionHandler extends node
50
46
  onSocketCreated: options.onSocketCreated
51
47
  });
52
48
  }
53
- forwardToSocketConnection(clientChannel, socket) {
54
- return new socket_channel_forwarder_1.SocketChannelForwarder(clientChannel, socket);
55
- }
56
49
  };
57
50
  exports.DataServerConnectionHandler = DataServerConnectionHandler;
58
51
  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;AAEjF,4DAAqE;AAErE,yEAAoE;AAyCpE;;;;;;;;;;;GAWG;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;IAES,yBAAyB,CAAC,aAAsB,EAAE,MAAkB;QAC3E,OAAO,IAAI,iDAAsB,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IAC5D,CAAC;CACH,CAAA;AAjBY,kEAA2B;sCAA3B,2BAA2B;IADvC,IAAA,sBAAU,GAAE;IAEG,WAAA,IAAA,qBAAS,GAAE,CAAA;;GADd,2BAA2B,CAiBvC"}
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"}
@@ -9,5 +9,4 @@
9
9
  export * from './connection-container-module';
10
10
  export * from './data-server-connection-handler';
11
11
  export * from './host-diagnostics-server';
12
- export * from './socket-channel-forwarder';
13
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,2BAA2B,CAAC"}
package/lib/node/index.js CHANGED
@@ -25,5 +25,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
25
25
  __exportStar(require("./connection-container-module"), exports);
26
26
  __exportStar(require("./data-server-connection-handler"), exports);
27
27
  __exportStar(require("./host-diagnostics-server"), exports);
28
- __exportStar(require("./socket-channel-forwarder"), exports);
29
28
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;;;;;;;;;;;;;;AAElF,gEAA8C;AAC9C,mEAAiD;AACjD,4DAA0C;AAC1C,6DAA2C"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;;;;;;;;;;;;;;AAElF,gEAA8C;AAC9C,mEAAiD;AACjD,4DAA0C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hydranium/data-client-theia",
3
- "version": "1.0.0-next.123",
3
+ "version": "1.0.0-next.127",
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.123",
74
- "@hydranium/core": "1.0.0-next.123",
75
- "@hydranium/protocol": "1.0.0-next.123",
73
+ "@hydranium/client-theia": "1.0.0-next.127",
74
+ "@hydranium/core": "1.0.0-next.127",
75
+ "@hydranium/protocol": "1.0.0-next.127",
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.123",
87
- "@hydranium/core": "1.0.0-next.123",
88
- "@hydranium/protocol": "1.0.0-next.123",
86
+ "@hydranium/client-theia": "1.0.0-next.127",
87
+ "@hydranium/core": "1.0.0-next.127",
88
+ "@hydranium/protocol": "1.0.0-next.127",
89
89
  "@theia/core": "^1.70.0",
90
90
  "@theia/workspace": "^1.70.0",
91
91
  "inversify": "^6.0.0",
@@ -0,0 +1,117 @@
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 type { DataConnection, DataServerProtocol, DataSession, DiagnosticOf, TransferElement } from '@hydranium/protocol';
11
+ // Type-only: the `@theia/core/lib/browser` barrel touches DOM globals at module
12
+ // load, which the node-environment unit tests cannot provide.
13
+ import type { FrontendApplicationContribution, OnWillStopAction } from '@theia/core/lib/browser';
14
+ import { injectable } from '@theia/core/shared/inversify';
15
+
16
+ /**
17
+ * Ends a Theia frontend's data sessions with its page, and holds the page
18
+ * while one of them is still saving. Bind it as a
19
+ * `FrontendApplicationContribution` in singleton scope and {@link track} each
20
+ * data connection the frontend binds, once.
21
+ *
22
+ * **Stop.** {@link onStop} disposes every tracked session, which sends its
23
+ * `closeSession`, so the server ends it as closed: each document it was the
24
+ * last to hold reverts at once. Otherwise the server learns of the page only
25
+ * when its connection goes, which Theia may hold open for its reconnect
26
+ * timeout, and then ends the sessions as lost, so their documents wait out the
27
+ * revert grace. The close is best effort: under load the page's last frames
28
+ * can be lost on the way, and the server then ends the sessions as lost. A
29
+ * session with calls still in flight at the stop closes after them, which is
30
+ * too late for a page going away, and is left to the server's connection-close
31
+ * cleanup. A call a session makes in the same tick as the stop is refused and
32
+ * never sent.
33
+ *
34
+ * **Veto.** While a tracked session has a save in flight, {@link onWillStop}
35
+ * vetoes, including a session disposed meanwhile. In Electron, Theia awaits
36
+ * the veto's action, which waits for the saves to answer, a save started during
37
+ * the wait included, up to {@link maxSettlePasses} times. The browser runs no
38
+ * action before unload, so there the veto shows the leave-page prompt and
39
+ * nothing more. Theia ignores every veto under
40
+ * `application.confirmExit: 'never'`, so there a save in flight at the stop
41
+ * may be cut off.
42
+ */
43
+ @injectable()
44
+ export class DataSessionStopContribution implements FrontendApplicationContribution {
45
+ /** The sessions to end at the stop, and to wait for while they save. */
46
+ protected readonly sessions = new Set<DataSession<TransferElement>>();
47
+ /**
48
+ * How many times the veto's action waits for the saves in flight before it
49
+ * lets the stop go. Each wait ends by the session's own bound, so a save
50
+ * that never answers holds the stop for a few bounds, not for good.
51
+ */
52
+ protected readonly maxSettlePasses: number = 3;
53
+
54
+ /**
55
+ * Take every session of `connection` into the stop: the ones it has
56
+ * started already, and each one it starts from now on. Generic, so a
57
+ * connection of any transfer root, server and client fits.
58
+ */
59
+ track<TTransfer extends TransferElement, TServer extends DataServerProtocol<TTransfer, DiagnosticOf<TServer>>, TClient extends object>(
60
+ connection: DataConnection<TTransfer, TServer, TClient>
61
+ ): void {
62
+ connection.liveSessions.forEach(session => this.trackSession(session));
63
+ connection.onDidCreateSession(session => this.trackSession(session));
64
+ }
65
+
66
+ /**
67
+ * Take `session` into the stop. It leaves once it has ended and its saves
68
+ * have answered, so a session disposed with a save in flight still holds
69
+ * the page.
70
+ */
71
+ protected trackSession(session: DataSession<TransferElement>): void {
72
+ this.sessions.add(session);
73
+ // A save can outlast the wait's bound, so the session stays until none
74
+ // is left in flight.
75
+ const release = (): void =>
76
+ void session.whenSavesSettled().then(() => (session.hasSavesInFlight ? release() : this.sessions.delete(session)));
77
+ // An ended session has fired its event already, and never fires it again.
78
+ if (session.isDisposed) {
79
+ release();
80
+ } else {
81
+ session.onDidDispose(release);
82
+ }
83
+ }
84
+
85
+ onWillStop(): OnWillStopAction | undefined {
86
+ if (this.savingSessions().length === 0) {
87
+ return undefined;
88
+ }
89
+ return {
90
+ // Theia logs the reason and shows it nowhere, so it is not translated.
91
+ reason: 'Data sessions are saving',
92
+ action: async () => {
93
+ // The page stays usable while Theia awaits this, so a save can
94
+ // start during a wait; each pass waits for the saves then in flight.
95
+ for (let pass = 0; pass < this.maxSettlePasses; pass++) {
96
+ const saving = this.savingSessions();
97
+ if (saving.length === 0) {
98
+ break;
99
+ }
100
+ await Promise.all(saving.map(session => session.whenSavesSettled()));
101
+ }
102
+ return true;
103
+ }
104
+ };
105
+ }
106
+
107
+ /** The tracked sessions with a save in flight. */
108
+ protected savingSessions(): DataSession<TransferElement>[] {
109
+ return [...this.sessions].filter(session => session.hasSavesInFlight);
110
+ }
111
+
112
+ onStop(): void {
113
+ for (const session of this.sessions) {
114
+ session.dispose();
115
+ }
116
+ }
117
+ }
@@ -9,5 +9,6 @@
9
9
 
10
10
  export * from './channel-connection';
11
11
  export * from './channel-data-port';
12
+ export * from './data-session-stop-contribution';
12
13
  export * from './host-diagnostics-frontend';
13
14
  export * from './workspace-gate';
@@ -13,6 +13,7 @@ import type {
13
13
  ProjectsChangedEvent,
14
14
  TransferDiagnostic,
15
15
  TransferDocumentDeletedEvent,
16
+ TransferDocumentDirtyChangedEvent,
16
17
  TransferDocumentSavedEvent,
17
18
  TransferDocumentsBuiltEvent,
18
19
  TransferDocumentUpdatedEvent,
@@ -48,6 +49,10 @@ export class EmitterDataClient<
48
49
  /** Fires for each inbound {@link onDocumentSaved} notification. */
49
50
  readonly onDidSaveDocument: Event<TransferDocumentSavedEvent<TTransfer, TDiagnostic>> = this.onDocumentSavedEmitter.event;
50
51
 
52
+ protected readonly onDocumentDirtyChangedEmitter = new Emitter<TransferDocumentDirtyChangedEvent>();
53
+ /** Fires for each inbound {@link onDocumentDirtyChanged} notification. */
54
+ readonly onDidChangeDocumentDirty: Event<TransferDocumentDirtyChangedEvent> = this.onDocumentDirtyChangedEmitter.event;
55
+
51
56
  protected readonly onDocumentDeletedEmitter = new Emitter<TransferDocumentDeletedEvent>();
52
57
  /** Fires for each inbound {@link onDocumentDeleted} notification. */
53
58
  readonly onDidDeleteDocument: Event<TransferDocumentDeletedEvent> = this.onDocumentDeletedEmitter.event;
@@ -68,6 +73,10 @@ export class EmitterDataClient<
68
73
  this.onDocumentSavedEmitter.fire(event);
69
74
  }
70
75
 
76
+ onDocumentDirtyChanged(event: TransferDocumentDirtyChangedEvent): void {
77
+ this.onDocumentDirtyChangedEmitter.fire(event);
78
+ }
79
+
71
80
  onDocumentDeleted(event: TransferDocumentDeletedEvent): void {
72
81
  this.onDocumentDeletedEmitter.fire(event);
73
82
  }
@@ -9,10 +9,8 @@
9
9
 
10
10
  import { AbstractSocketForwardingConnectionHandler } from '@hydranium/client-theia/lib/node';
11
11
  import { DATA_SERVER_PATH, DATA_SERVER_PORT_COMMAND } from '@hydranium/protocol';
12
- import { type Channel, type Disposable } from '@theia/core';
13
12
  import { injectable, unmanaged } from '@theia/core/shared/inversify';
14
13
  import type * as net from 'node:net';
15
- import { SocketChannelForwarder } from './socket-channel-forwarder';
16
14
 
17
15
  /** Options for {@link DataServerConnectionHandler}. `servicePath` is the Theia
18
16
  * service path the browser frontend opens a channel to; `portCommand` is the
@@ -57,13 +55,10 @@ export interface DataServerConnectionHandlerOptions {
57
55
  * Bridges a Theia browser-frontend channel to a data-server's TCP socket — the
58
56
  * backend half of the data-server head's transport.
59
57
  *
60
- * The port-discovery + buffer-and-replay race fix + connect orchestration live
61
- * on the cross-head {@link AbstractSocketForwardingConnectionHandler} base; this
62
- * subclass supplies only the data-server defaults (service path / port command /
63
- * log labels) and plugs in {@link SocketChannelForwarder} as the byte relay —
64
- * its own forwarder, so the data head carries no GLSP dependency. Sibling of
65
- * `@hydranium/glsp-client-theia`'s `GlspServerConnectionHandler`, which
66
- * subclasses the same base with `@eclipse-glsp`'s `SocketConnectionForwarder`.
58
+ * The port discovery, the buffer-and-replay race fix, the connect orchestration
59
+ * and the byte forwarder live on the cross-head
60
+ * {@link AbstractSocketForwardingConnectionHandler} base; this subclass supplies
61
+ * only the data-server defaults (service path / port command / log labels).
67
62
  */
68
63
  @injectable()
69
64
  export class DataServerConnectionHandler extends AbstractSocketForwardingConnectionHandler {
@@ -79,8 +74,4 @@ export class DataServerConnectionHandler extends AbstractSocketForwardingConnect
79
74
  onSocketCreated: options.onSocketCreated
80
75
  });
81
76
  }
82
-
83
- protected forwardToSocketConnection(clientChannel: Channel, socket: net.Socket): Disposable {
84
- return new SocketChannelForwarder(clientChannel, socket);
85
- }
86
77
  }
package/src/node/index.ts CHANGED
@@ -10,4 +10,3 @@
10
10
  export * from './connection-container-module';
11
11
  export * from './data-server-connection-handler';
12
12
  export * from './host-diagnostics-server';
13
- export * from './socket-channel-forwarder';
@@ -1,34 +0,0 @@
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 { type Channel, Disposable, DisposableCollection, type MessageProvider } from '@theia/core';
10
- import type * as net from 'node:net';
11
- import { type Message } from 'vscode-jsonrpc/node';
12
- /**
13
- * Relays JSON-RPC messages between a Theia browser-frontend {@link Channel} and
14
- * a data-server's TCP socket — the backend half of the data-server head's
15
- * transport. The model-server speaks vscode-jsonrpc (Content-Length framed) on
16
- * the socket; the frontend speaks the same protocol over the channel (via
17
- * `createChannelConnection`). This forwarder decodes each side and re-emits on
18
- * the other, so the two ends share one protocol with no semantic re-proxy in
19
- * between.
20
- *
21
- * Framework-owned equivalent of `@eclipse-glsp/theia-integration`'s
22
- * `SocketConnectionForwarder` — reproduced here (rather than imported) so the
23
- * data-server head carries no GLSP dependency.
24
- */
25
- export declare class SocketChannelForwarder implements Disposable {
26
- protected readonly channel: Channel;
27
- protected readonly socket: net.Socket;
28
- protected readonly toDispose: DisposableCollection;
29
- constructor(channel: Channel, socket: net.Socket);
30
- protected decodeChannelMessage(provider: MessageProvider): Message;
31
- protected writeToChannel(message: Message): void;
32
- dispose(): void;
33
- }
34
- //# sourceMappingURL=socket-channel-forwarder.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"socket-channel-forwarder.d.ts","sourceRoot":"","sources":["../../src/node/socket-channel-forwarder.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,KAAK,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACnG,OAAO,KAAK,KAAK,GAAG,MAAM,UAAU,CAAC;AACrC,OAAO,EAA2B,KAAK,OAAO,EAA4C,MAAM,qBAAqB,CAAC;AAEtH;;;;;;;;;;;;GAYG;AACH,qBAAa,sBAAuB,YAAW,UAAU;IAInD,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO;IACnC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM;IAJxC,SAAS,CAAC,QAAQ,CAAC,SAAS,uBAA8B;gBAGpC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,GAAG,CAAC,MAAM;IAuBxC,SAAS,CAAC,oBAAoB,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO;IAKlE,SAAS,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAMhD,OAAO,IAAI,IAAI;CAGjB"}
@@ -1,68 +0,0 @@
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
- Object.defineProperty(exports, "__esModule", { value: true });
11
- exports.SocketChannelForwarder = void 0;
12
- const core_1 = require("@theia/core");
13
- const node_1 = require("vscode-jsonrpc/node");
14
- /**
15
- * Relays JSON-RPC messages between a Theia browser-frontend {@link Channel} and
16
- * a data-server's TCP socket — the backend half of the data-server head's
17
- * transport. The model-server speaks vscode-jsonrpc (Content-Length framed) on
18
- * the socket; the frontend speaks the same protocol over the channel (via
19
- * `createChannelConnection`). This forwarder decodes each side and re-emits on
20
- * the other, so the two ends share one protocol with no semantic re-proxy in
21
- * between.
22
- *
23
- * Framework-owned equivalent of `@eclipse-glsp/theia-integration`'s
24
- * `SocketConnectionForwarder` — reproduced here (rather than imported) so the
25
- * data-server head carries no GLSP dependency.
26
- */
27
- class SocketChannelForwarder {
28
- channel;
29
- socket;
30
- toDispose = new core_1.DisposableCollection();
31
- constructor(channel, socket) {
32
- this.channel = channel;
33
- this.socket = socket;
34
- const reader = new node_1.SocketMessageReader(socket);
35
- const writer = new node_1.SocketMessageWriter(socket);
36
- const connection = (0, node_1.createMessageConnection)(reader, writer);
37
- // Nothing here destroys the socket, and adding it back would be dead
38
- // code in both directions. `SocketMessageWriter.dispose()` destroys it
39
- // itself, which covers the dispose path; and `connection.onClose` fires
40
- // only from the reader's or writer's own close, which for a socket means
41
- // the socket has already gone — so a destroy handler there is downstream
42
- // of the effect it would be trying to cause.
43
- this.toDispose.pushAll([
44
- reader.listen(message => this.writeToChannel(message)),
45
- channel.onMessage(provider => void writer.write(this.decodeChannelMessage(provider))),
46
- channel.onClose(() => connection.dispose()),
47
- connection.onClose(() => channel.close()),
48
- core_1.Disposable.create(() => {
49
- channel.close();
50
- connection.dispose();
51
- })
52
- ]);
53
- }
54
- decodeChannelMessage(provider) {
55
- const buffer = provider().readBytes();
56
- return JSON.parse(new TextDecoder().decode(buffer));
57
- }
58
- writeToChannel(message) {
59
- const writeBuffer = this.channel.getWriteBuffer();
60
- writeBuffer.writeBytes(new TextEncoder().encode(JSON.stringify(message)));
61
- writeBuffer.commit();
62
- }
63
- dispose() {
64
- this.toDispose.dispose();
65
- }
66
- }
67
- exports.SocketChannelForwarder = SocketChannelForwarder;
68
- //# sourceMappingURL=socket-channel-forwarder.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"socket-channel-forwarder.js","sourceRoot":"","sources":["../../src/node/socket-channel-forwarder.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;AAElF,sCAAmG;AAEnG,8CAAsH;AAEtH;;;;;;;;;;;;GAYG;AACH,MAAa,sBAAsB;IAIV;IACA;IAJH,SAAS,GAAG,IAAI,2BAAoB,EAAE,CAAC;IAE1D,YACsB,OAAgB,EAChB,MAAkB;QADlB,YAAO,GAAP,OAAO,CAAS;QAChB,WAAM,GAAN,MAAM,CAAY;QAErC,MAAM,MAAM,GAAG,IAAI,0BAAmB,CAAC,MAAM,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,0BAAmB,CAAC,MAAM,CAAC,CAAC;QAC/C,MAAM,UAAU,GAAG,IAAA,8BAAuB,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC3D,qEAAqE;QACrE,uEAAuE;QACvE,wEAAwE;QACxE,yEAAyE;QACzE,yEAAyE;QACzE,6CAA6C;QAC7C,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;YACpB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;YACtD,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAAC;YACrF,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;YAC3C,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACzC,iBAAU,CAAC,MAAM,CAAC,GAAG,EAAE;gBACpB,OAAO,CAAC,KAAK,EAAE,CAAC;gBAChB,UAAU,CAAC,OAAO,EAAE,CAAC;YACxB,CAAC,CAAC;SACJ,CAAC,CAAC;IACN,CAAC;IAES,oBAAoB,CAAC,QAAyB;QACrD,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAC,SAAS,EAAE,CAAC;QACtC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAY,CAAC;IAClE,CAAC;IAES,cAAc,CAAC,OAAgB;QACtC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC;QAClD,WAAW,CAAC,UAAU,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC1E,WAAW,CAAC,MAAM,EAAE,CAAC;IACxB,CAAC;IAED,OAAO;QACJ,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;IAC5B,CAAC;CACH;AA1CD,wDA0CC"}
@@ -1,69 +0,0 @@
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 { type Channel, Disposable, DisposableCollection, type MessageProvider } from '@theia/core';
11
- import type * as net from 'node:net';
12
- import { createMessageConnection, type Message, SocketMessageReader, SocketMessageWriter } from 'vscode-jsonrpc/node';
13
-
14
- /**
15
- * Relays JSON-RPC messages between a Theia browser-frontend {@link Channel} and
16
- * a data-server's TCP socket — the backend half of the data-server head's
17
- * transport. The model-server speaks vscode-jsonrpc (Content-Length framed) on
18
- * the socket; the frontend speaks the same protocol over the channel (via
19
- * `createChannelConnection`). This forwarder decodes each side and re-emits on
20
- * the other, so the two ends share one protocol with no semantic re-proxy in
21
- * between.
22
- *
23
- * Framework-owned equivalent of `@eclipse-glsp/theia-integration`'s
24
- * `SocketConnectionForwarder` — reproduced here (rather than imported) so the
25
- * data-server head carries no GLSP dependency.
26
- */
27
- export class SocketChannelForwarder implements Disposable {
28
- protected readonly toDispose = new DisposableCollection();
29
-
30
- constructor(
31
- protected readonly channel: Channel,
32
- protected readonly socket: net.Socket
33
- ) {
34
- const reader = new SocketMessageReader(socket);
35
- const writer = new SocketMessageWriter(socket);
36
- const connection = createMessageConnection(reader, writer);
37
- // Nothing here destroys the socket, and adding it back would be dead
38
- // code in both directions. `SocketMessageWriter.dispose()` destroys it
39
- // itself, which covers the dispose path; and `connection.onClose` fires
40
- // only from the reader's or writer's own close, which for a socket means
41
- // the socket has already gone — so a destroy handler there is downstream
42
- // of the effect it would be trying to cause.
43
- this.toDispose.pushAll([
44
- reader.listen(message => this.writeToChannel(message)),
45
- channel.onMessage(provider => void writer.write(this.decodeChannelMessage(provider))),
46
- channel.onClose(() => connection.dispose()),
47
- connection.onClose(() => channel.close()),
48
- Disposable.create(() => {
49
- channel.close();
50
- connection.dispose();
51
- })
52
- ]);
53
- }
54
-
55
- protected decodeChannelMessage(provider: MessageProvider): Message {
56
- const buffer = provider().readBytes();
57
- return JSON.parse(new TextDecoder().decode(buffer)) as Message;
58
- }
59
-
60
- protected writeToChannel(message: Message): void {
61
- const writeBuffer = this.channel.getWriteBuffer();
62
- writeBuffer.writeBytes(new TextEncoder().encode(JSON.stringify(message)));
63
- writeBuffer.commit();
64
- }
65
-
66
- dispose(): void {
67
- this.toDispose.dispose();
68
- }
69
- }