@hydranium/client-theia 1.0.0-next.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +113 -0
- package/lib/browser/browser-capture.d.ts +21 -0
- package/lib/browser/browser-capture.d.ts.map +1 -0
- package/lib/browser/browser-capture.js +61 -0
- package/lib/browser/browser-capture.js.map +1 -0
- package/lib/browser/channel-logger.d.ts +87 -0
- package/lib/browser/channel-logger.d.ts.map +1 -0
- package/lib/browser/channel-logger.js +149 -0
- package/lib/browser/channel-logger.js.map +1 -0
- package/lib/browser/index.d.ts +13 -0
- package/lib/browser/index.d.ts.map +1 -0
- package/lib/browser/index.js +30 -0
- package/lib/browser/index.js.map +1 -0
- package/lib/browser/log-level-preference.d.ts +82 -0
- package/lib/browser/log-level-preference.d.ts.map +1 -0
- package/lib/browser/log-level-preference.js +146 -0
- package/lib/browser/log-level-preference.js.map +1 -0
- package/lib/browser/memory-diagnostics-contribution.d.ts +105 -0
- package/lib/browser/memory-diagnostics-contribution.d.ts.map +1 -0
- package/lib/browser/memory-diagnostics-contribution.js +229 -0
- package/lib/browser/memory-diagnostics-contribution.js.map +1 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +11 -0
- package/lib/index.js.map +1 -0
- package/lib/node/abstract-socket-forwarding-connection-handler.d.ts +96 -0
- package/lib/node/abstract-socket-forwarding-connection-handler.d.ts.map +1 -0
- package/lib/node/abstract-socket-forwarding-connection-handler.js +224 -0
- package/lib/node/abstract-socket-forwarding-connection-handler.js.map +1 -0
- package/lib/node/index.d.ts +10 -0
- package/lib/node/index.d.ts.map +1 -0
- package/lib/node/index.js +26 -0
- package/lib/node/index.js.map +1 -0
- package/lib/testing/index.d.ts +11 -0
- package/lib/testing/index.d.ts.map +1 -0
- package/lib/testing/index.js +29 -0
- package/lib/testing/index.js.map +1 -0
- package/lib/testing/stub-inversify-context.d.ts +15 -0
- package/lib/testing/stub-inversify-context.d.ts.map +1 -0
- package/lib/testing/stub-inversify-context.js +32 -0
- package/lib/testing/stub-inversify-context.js.map +1 -0
- package/lib/testing/stub-output-channel.d.ts +25 -0
- package/lib/testing/stub-output-channel.d.ts.map +1 -0
- package/lib/testing/stub-output-channel.js +33 -0
- package/lib/testing/stub-output-channel.js.map +1 -0
- package/package.json +95 -0
- package/src/browser/browser-capture.ts +75 -0
- package/src/browser/channel-logger.ts +160 -0
- package/src/browser/index.ts +14 -0
- package/src/browser/log-level-preference.ts +127 -0
- package/src/browser/memory-diagnostics-contribution.ts +248 -0
- package/src/index.ts +19 -0
- package/src/node/abstract-socket-forwarding-connection-handler.ts +218 -0
- package/src/node/index.ts +10 -0
- package/src/testing/index.ts +14 -0
- package/src/testing/stub-inversify-context.ts +32 -0
- package/src/testing/stub-output-channel.ts +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# `@hydranium/client-theia`
|
|
2
|
+
|
|
3
|
+
Cross-head Theia client primitives for the
|
|
4
|
+
[Hydranium](https://github.com/eclipse-emfcloud/hydranium) framework.
|
|
5
|
+
|
|
6
|
+
This is the host-coupled layer every hydranium head shares when it runs inside a
|
|
7
|
+
Theia application: the Output-channel logger, the log-threshold preference
|
|
8
|
+
binding, the memory-diagnostics command set, and the backend base class that
|
|
9
|
+
bridges a Theia frontend channel to a server socket. It carries no head-specific
|
|
10
|
+
dependency — `@hydranium/data-client-theia` and `@hydranium/glsp-client-theia`
|
|
11
|
+
both build on it. Install it if you are assembling a Theia extension around a
|
|
12
|
+
hydranium server; a non-Theia host does not need it.
|
|
13
|
+
|
|
14
|
+
## What it gives you
|
|
15
|
+
|
|
16
|
+
- **`ChannelLogger`** with `bindChannelLogger` — writes frontend log lines into a
|
|
17
|
+
Theia Output channel, formatted to interleave with the server's own log so both
|
|
18
|
+
sides of the conversation read as one transcript. The same call also binds
|
|
19
|
+
`ChannelTracer`, the `Tracer` token frontend services inject when they time
|
|
20
|
+
something.
|
|
21
|
+
- **`LogLevelPreferenceContribution`** with `bindLogLevelPreference` — applies the
|
|
22
|
+
framework log threshold from a Theia preference once per application, and keeps
|
|
23
|
+
it in sync on change. It is a `FrontendApplicationContribution` on purpose: the
|
|
24
|
+
threshold is process-global, so applying it from a per-diagram container would
|
|
25
|
+
leak one preference listener per container.
|
|
26
|
+
- **`MemoryDiagnosticsContribution`** with `bindMemoryDiagnostics` — the whole
|
|
27
|
+
diagnostics command set, parameterised by three branding strings
|
|
28
|
+
(`MemoryDiagnosticsOptions`: `commandIdPrefix`, `category`, `channelName`):
|
|
29
|
+
Dump Server State, Dump Pod Memory, Dump Frontend State, Write Heap Snapshot
|
|
30
|
+
(Server), Start / Stop Profiling (Server), Record Performance Profile, and Dump
|
|
31
|
+
RPC/LSP Latency. Two more — Dump Backend State and Write Heap Snapshot
|
|
32
|
+
(Backend) — register only when the optional `HostMemoryDiagnosticsService` is
|
|
33
|
+
bound. You bind `MemoryDiagnosticsService` to your own connected data-server
|
|
34
|
+
frontend; the framework cannot, because that class is yours.
|
|
35
|
+
- **`captureBrowserRuntime` / `formatBrowserRuntime`** — the renderer's own memory
|
|
36
|
+
reading as a `BrowserRuntimeReport`, preferring the standardized
|
|
37
|
+
`performance.measureUserAgentSpecificMemory()` and falling back to Chromium's
|
|
38
|
+
coarse `performance.memory`.
|
|
39
|
+
- **`AbstractSocketForwardingConnectionHandler`** (Node side) — the abstract base for the
|
|
40
|
+
Theia _backend_ half of a head's transport. It discovers the server's listening
|
|
41
|
+
port by executing a registered command, opens a `net.Socket`, and relays bytes
|
|
42
|
+
between the frontend channel and it. The one abstract hook is which byte
|
|
43
|
+
forwarder bridges the two; port discovery, connect orchestration, and the
|
|
44
|
+
buffer-and-replay fix for frontend writes that arrive before the forwarder is
|
|
45
|
+
wired all live here.
|
|
46
|
+
- **`./testing`** — `makeStubOutputChannelManager` and `makeStubInversifyContext`,
|
|
47
|
+
the doubles that let the pieces above be unit-tested without a Theia
|
|
48
|
+
application.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install @hydranium/client-theia
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
You must already have a Theia application (or a Theia extension inside one). The
|
|
57
|
+
declared peer dependencies are:
|
|
58
|
+
|
|
59
|
+
| Peer | Range |
|
|
60
|
+
| --------------------- | ------------- |
|
|
61
|
+
| `@hydranium/protocol` | `^1.0.0-next` |
|
|
62
|
+
| `@theia/core` | `^1.71.0` |
|
|
63
|
+
| `@theia/output` | `^1.71.0` |
|
|
64
|
+
| `inversify` | `^6.0.0` |
|
|
65
|
+
|
|
66
|
+
`@theia/output` is easy to miss: the memory-diagnostics commands and the channel
|
|
67
|
+
logger both write to an Output channel, so a host that does not already depend on
|
|
68
|
+
it has to add it.
|
|
69
|
+
|
|
70
|
+
## Wiring
|
|
71
|
+
|
|
72
|
+
This package is a **library, not a Theia extension** — it declares no
|
|
73
|
+
`theiaExtensions`, so dropping it into an application wires nothing on its own.
|
|
74
|
+
Your own extension package declares the `theiaExtensions` entries and imports
|
|
75
|
+
from here:
|
|
76
|
+
|
|
77
|
+
- a **frontend** module (a `ContainerModule` in the extension's `browser/` tier)
|
|
78
|
+
calls the `bind*` helpers — `bindChannelLogger`, `bindLogLevelPreference`,
|
|
79
|
+
`bindMemoryDiagnostics` — and binds `MemoryDiagnosticsService` to your
|
|
80
|
+
data-server frontend;
|
|
81
|
+
- a **backend** module (the extension's `node/` tier) registers a
|
|
82
|
+
`AbstractSocketForwardingConnectionHandler` subclass as a Theia `ConnectionHandler`. In
|
|
83
|
+
practice you subclass one of the head-specific subclasses shipped by
|
|
84
|
+
`@hydranium/data-client-theia` or `@hydranium/glsp-client-theia` rather than
|
|
85
|
+
this base directly.
|
|
86
|
+
|
|
87
|
+
## Entry points
|
|
88
|
+
|
|
89
|
+
| Subpath | Holds | Environment |
|
|
90
|
+
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
|
|
91
|
+
| `.` | Nothing. Deliberately empty, so an environment-specific import cannot reach the wrong bundle through a barrel. | browser-neutral (gated) |
|
|
92
|
+
| `./browser` | `ChannelLogger`, `ChannelTracer`, `LogLevelPreferenceContribution`, `MemoryDiagnosticsContribution`, the `bind*` helpers, `captureBrowserRuntime` | browser / Theia frontend (gated) |
|
|
93
|
+
| `./node` | `AbstractSocketForwardingConnectionHandler` and its options — imports `node:net` | Node / Theia backend |
|
|
94
|
+
| `./testing` | `makeStubOutputChannelManager`, `makeStubInversifyContext` | browser-neutral (gated) |
|
|
95
|
+
|
|
96
|
+
Every subpath also has a `./lib/<name>` twin, so a consumer on
|
|
97
|
+
`moduleResolution: "Node"` can reach it. "Gated" means the entry is enforced
|
|
98
|
+
browser-neutral by the repository's neutral-bundle check: it must bundle for the
|
|
99
|
+
browser with no `node:*` import, including transitive ones — and [what "gated neutral" does and does not promise](../../docs/concepts/browser-hosting.md#a-note-on-what-gated-neutral-does-and-does-not-promise).
|
|
100
|
+
`./node` is deliberately not gated — that tier is where Node-only code belongs.
|
|
101
|
+
|
|
102
|
+
## Status
|
|
103
|
+
|
|
104
|
+
Alpha — pre-v0, not yet published. The API is not stable and may change without a
|
|
105
|
+
deprecation cycle. See [`docs/concepts/architecture.md`](../../docs/concepts/architecture.md) for
|
|
106
|
+
how the Theia client tier relates to the heads, and the
|
|
107
|
+
[repository README](../../README.md) for current status and known limitations.
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
`MIT` — see this package's [`LICENSE`](./LICENSE). Third-party notices for the
|
|
112
|
+
runtime dependency closure are collected in the repository
|
|
113
|
+
[`NOTICE.md`](../../NOTICE.md).
|
|
@@ -0,0 +1,21 @@
|
|
|
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 { BrowserRuntimeReport } from '@hydranium/protocol';
|
|
10
|
+
/**
|
|
11
|
+
* Capture the renderer runtime as a single-point {@link BrowserRuntimeReport}
|
|
12
|
+
* (`after`-only — the live gauge read). Prefers the standardized
|
|
13
|
+
* `performance.measureUserAgentSpecificMemory()` (precise, by type + container, but
|
|
14
|
+
* requires cross-origin isolation and is rate-limited), falling back to Chrome's
|
|
15
|
+
* coarse `performance.memory` (JS-heap gauge), then to `unavailable`. The two-point
|
|
16
|
+
* CDP source (`nodes`/`jsEventListeners`/… + delta) is the e2e bridge's job.
|
|
17
|
+
*/
|
|
18
|
+
export declare function captureBrowserRuntime(): Promise<BrowserRuntimeReport>;
|
|
19
|
+
/** Render a {@link BrowserRuntimeReport} as a one-line summary for a channel / toast. */
|
|
20
|
+
export declare function formatBrowserRuntime(report: BrowserRuntimeReport): string;
|
|
21
|
+
//# sourceMappingURL=browser-capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-capture.d.ts","sourceRoot":"","sources":["../../src/browser/browser-capture.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAYlF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAOhE;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAqB3E;AAMD,yFAAyF;AACzF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAYzE"}
|
|
@@ -0,0 +1,61 @@
|
|
|
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.captureBrowserRuntime = captureBrowserRuntime;
|
|
12
|
+
exports.formatBrowserRuntime = formatBrowserRuntime;
|
|
13
|
+
/**
|
|
14
|
+
* Capture the renderer runtime as a single-point {@link BrowserRuntimeReport}
|
|
15
|
+
* (`after`-only — the live gauge read). Prefers the standardized
|
|
16
|
+
* `performance.measureUserAgentSpecificMemory()` (precise, by type + container, but
|
|
17
|
+
* requires cross-origin isolation and is rate-limited), falling back to Chrome's
|
|
18
|
+
* coarse `performance.memory` (JS-heap gauge), then to `unavailable`. The two-point
|
|
19
|
+
* CDP source (`nodes`/`jsEventListeners`/… + delta) is the e2e bridge's job.
|
|
20
|
+
*/
|
|
21
|
+
async function captureBrowserRuntime() {
|
|
22
|
+
const perf = globalThis.performance;
|
|
23
|
+
if (perf?.measureUserAgentSpecificMemory) {
|
|
24
|
+
try {
|
|
25
|
+
const result = await perf.measureUserAgentSpecificMemory();
|
|
26
|
+
return { source: 'measureUserAgentSpecificMemory', after: { totalBytes: result.bytes, breakdown: result.breakdown } };
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// COI missing or rate-limited — fall through to the coarse fallback.
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (perf?.memory) {
|
|
33
|
+
return {
|
|
34
|
+
source: 'performance.memory',
|
|
35
|
+
after: {
|
|
36
|
+
jsHeapUsedBytes: perf.memory.usedJSHeapSize,
|
|
37
|
+
jsHeapTotalBytes: perf.memory.totalJSHeapSize,
|
|
38
|
+
jsHeapLimitBytes: perf.memory.jsHeapSizeLimit
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return { source: 'unavailable', after: {} };
|
|
43
|
+
}
|
|
44
|
+
function mb(bytes) {
|
|
45
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
46
|
+
}
|
|
47
|
+
/** Render a {@link BrowserRuntimeReport} as a one-line summary for a channel / toast. */
|
|
48
|
+
function formatBrowserRuntime(report) {
|
|
49
|
+
const { after } = report;
|
|
50
|
+
if (report.source === 'measureUserAgentSpecificMemory' && after.totalBytes !== undefined) {
|
|
51
|
+
return `Browser renderer memory (precise): ${mb(after.totalBytes)} total across JS/DOM/workers.`;
|
|
52
|
+
}
|
|
53
|
+
if (report.source === 'performance.memory' && after.jsHeapUsedBytes !== undefined) {
|
|
54
|
+
const used = after.jsHeapUsedBytes;
|
|
55
|
+
const total = after.jsHeapTotalBytes ?? 0;
|
|
56
|
+
const limit = after.jsHeapLimitBytes ?? 0;
|
|
57
|
+
return `Browser JS heap: ${mb(used)} used / ${mb(total)} total / ${mb(limit)} limit (JS heap only, not the full renderer).`;
|
|
58
|
+
}
|
|
59
|
+
return 'Browser memory info unavailable (non-Chromium, or performance.memory disabled / cross-origin isolation off).';
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=browser-capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-capture.js","sourceRoot":"","sources":["../../src/browser/browser-capture.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;AA2BlF,sDAqBC;AAOD,oDAYC;AAhDD;;;;;;;GAOG;AACI,KAAK,UAAU,qBAAqB;IACxC,MAAM,IAAI,GAAG,UAAU,CAAC,WAAmE,CAAC;IAC5F,IAAI,IAAI,EAAE,8BAA8B,EAAE,CAAC;QACxC,IAAI,CAAC;YACF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,8BAA8B,EAAE,CAAC;YAC3D,OAAO,EAAE,MAAM,EAAE,gCAAgC,EAAE,KAAK,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,EAAE,CAAC;QACzH,CAAC;QAAC,MAAM,CAAC;YACN,qEAAqE;QACxE,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;QAChB,OAAO;YACJ,MAAM,EAAE,oBAAoB;YAC5B,KAAK,EAAE;gBACJ,eAAe,EAAE,IAAI,CAAC,MAAM,CAAC,cAAc;gBAC3C,gBAAgB,EAAE,IAAI,CAAC,MAAM,CAAC,eAAe;gBAC7C,gBAAgB,EAAE,IAAI,CAAC,MAAM,CAAC,eAAe;aAC/C;SACH,CAAC;IACL,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;AAC/C,CAAC;AAED,SAAS,EAAE,CAAC,KAAa;IACtB,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;AACrD,CAAC;AAED,yFAAyF;AACzF,SAAgB,oBAAoB,CAAC,MAA4B;IAC9D,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;IACzB,IAAI,MAAM,CAAC,MAAM,KAAK,gCAAgC,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACxF,OAAO,sCAAsC,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,+BAA+B,CAAC;IACpG,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,oBAAoB,IAAI,KAAK,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;QACjF,MAAM,IAAI,GAAG,KAAK,CAAC,eAAe,CAAC;QACnC,MAAM,KAAK,GAAG,KAAK,CAAC,gBAAgB,IAAI,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,gBAAgB,IAAI,CAAC,CAAC;QAC1C,OAAO,oBAAoB,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,+CAA+C,CAAC;IAC/H,CAAC;IACD,OAAO,8GAA8G,CAAC;AACzH,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
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 { AbstractLogger, type LogLevel } from '@hydranium/protocol';
|
|
10
|
+
import { type interfaces } from '@theia/core/shared/inversify';
|
|
11
|
+
import { type OutputChannel, OutputChannelManager } from '@theia/output/lib/browser/output-channel';
|
|
12
|
+
/**
|
|
13
|
+
* Configuration for a {@link ChannelLogger}, supplied once at bind time via
|
|
14
|
+
* {@link bindChannelLogger} (the framework's `(deps, options)` constructor
|
|
15
|
+
* shape — the only piece an adopter must provide is the channel name).
|
|
16
|
+
*
|
|
17
|
+
* Note there is no log-threshold option here: the threshold is not a per-logger
|
|
18
|
+
* concern. See {@link ChannelLogger} for why, and
|
|
19
|
+
* `LogLevelPreferenceContribution` for the owner.
|
|
20
|
+
*/
|
|
21
|
+
export interface ChannelLoggerOptions {
|
|
22
|
+
/** Name of the Theia Output channel lines are written to. */
|
|
23
|
+
readonly channelName: string;
|
|
24
|
+
/** Initial logger component prefix (see `Logger.for`). */
|
|
25
|
+
readonly component?: string;
|
|
26
|
+
}
|
|
27
|
+
/** Inversify token for the {@link ChannelLoggerOptions} constant (merged with the
|
|
28
|
+
* interface name — the standard Theia "type + token under one identifier" idiom). */
|
|
29
|
+
export declare const ChannelLoggerOptions: unique symbol;
|
|
30
|
+
/**
|
|
31
|
+
* Writes client-side lines to a Theia Output channel, formatted to align with
|
|
32
|
+
* the server-side `LspLogger` so both sides of the conversation interleave
|
|
33
|
+
* cleanly under the same Output panel entry. Cross-head: the data, LSP, and
|
|
34
|
+
* GLSP Theia integrations can all reuse it — nothing here is GLSP-specific.
|
|
35
|
+
*
|
|
36
|
+
* Concrete and options-configured: adopters wire it with
|
|
37
|
+
* {@link bindChannelLogger}, supplying {@link ChannelLoggerOptions}, rather
|
|
38
|
+
* than subclassing to pin a channel name.
|
|
39
|
+
*
|
|
40
|
+
* It deliberately owns **no** threshold logic. The threshold is a process-global
|
|
41
|
+
* shared by every `AbstractLogger`, whereas this class's singleton scope is
|
|
42
|
+
* whichever container bound it (for a GLSP head, one per diagram) — so applying a
|
|
43
|
+
* global from here would subscribe a preference listener per container and never
|
|
44
|
+
* dispose one. `LogLevelPreferenceContribution` owns it instead, at application
|
|
45
|
+
* scope. This class only formats and appends.
|
|
46
|
+
*/
|
|
47
|
+
export declare class ChannelLogger extends AbstractLogger {
|
|
48
|
+
protected readonly outputChannels: OutputChannelManager;
|
|
49
|
+
protected readonly options: ChannelLoggerOptions;
|
|
50
|
+
protected _channel?: OutputChannel;
|
|
51
|
+
constructor(outputChannels: OutputChannelManager, options: ChannelLoggerOptions, component?: string | undefined);
|
|
52
|
+
protected get channel(): OutputChannel;
|
|
53
|
+
protected emit(_level: LogLevel, label: string, message: string, args: readonly unknown[]): void;
|
|
54
|
+
/**
|
|
55
|
+
* Allocates a free instance bypassing Inversify, so a derived logger inherits
|
|
56
|
+
* the same OutputChannelManager + options without going through the container
|
|
57
|
+
* singleton. Subclasses with additional constructor dependencies must
|
|
58
|
+
* override and pass the extra args along.
|
|
59
|
+
*/
|
|
60
|
+
protected derive(component: string): this;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Inversify token for the browser `Tracer` — a {@link DefaultTracer} over
|
|
64
|
+
* the {@link ChannelLogger} (emit sink) + a {@link SystemClock} + a browser
|
|
65
|
+
* heap reader. The browser head has no Langium `services.Tracer` slot, so this
|
|
66
|
+
* is how frontend services that time obtain a tracer: `@inject(ChannelTracer)`.
|
|
67
|
+
* Bound by {@link bindChannelLogger}.
|
|
68
|
+
*/
|
|
69
|
+
export declare const ChannelTracer: unique symbol;
|
|
70
|
+
/** Reads the requesting class's name from an Inversify context, by walking the
|
|
71
|
+
* current request up to its parent binding. */
|
|
72
|
+
export declare function getRequestParentName(context: interfaces.Context): string | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* Bind the {@link ChannelLogger} for an adopter's Theia frontend:
|
|
75
|
+
*
|
|
76
|
+
* - `bind(ChannelLoggerOptions).toConstantValue(options)` — the config
|
|
77
|
+
* - the un-tagged singleton base (private token), constructed by Inversify
|
|
78
|
+
* - `bind(ChannelLogger).toDynamicValue(...)` — class-scoped per request, so
|
|
79
|
+
* every `@inject(ChannelLogger)` site gets a logger pre-tagged with the
|
|
80
|
+
* requesting class name
|
|
81
|
+
*
|
|
82
|
+
* Adopters that compose their own module (bypassing a head's module helper)
|
|
83
|
+
* call this directly; head module helpers (`createGlspClientTheiaModule`)
|
|
84
|
+
* forward to it.
|
|
85
|
+
*/
|
|
86
|
+
export declare function bindChannelLogger(bind: interfaces.Bind, options: ChannelLoggerOptions): void;
|
|
87
|
+
//# sourceMappingURL=channel-logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-logger.d.ts","sourceRoot":"","sources":["../../src/browser/channel-logger.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,cAAc,EAAiB,KAAK,QAAQ,EAAkC,MAAM,qBAAqB,CAAC;AACnH,OAAO,EAAc,KAAK,UAAU,EAAqB,MAAM,8BAA8B,CAAC;AAC9F,OAAO,EAAE,KAAK,aAAa,EAAE,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AAEpG;;;;;;;;GAQG;AACH,MAAM,WAAW,oBAAoB;IAClC,6DAA6D;IAC7D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;sFACsF;AACtF,eAAO,MAAM,oBAAoB,eAAiC,CAAC;AAEnE;;;;;;;;;;;;;;;;GAgBG;AACH,qBACa,aAAc,SAAQ,cAAc;IAIb,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,oBAAoB;IACvD,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,oBAAoB;IAJjF,SAAS,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC;gBAGiB,cAAc,EAAE,oBAAoB,EACpC,OAAO,EAAE,oBAAoB,EACjE,SAAS,GAAE,MAAM,GAAG,SAA6B;IAKjE,SAAS,KAAK,OAAO,IAAI,aAAa,CAErC;IAED,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI;IAOhG;;;;;OAKG;IACH,SAAS,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;CAQ3C;AAOD;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,eAA0B,CAAC;AAErD;gDACgD;AAChD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,GAAG,MAAM,GAAG,SAAS,CAOpF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAW5F"}
|
|
@@ -0,0 +1,149 @@
|
|
|
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
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
20
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
21
|
+
};
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.ChannelTracer = exports.ChannelLogger = exports.ChannelLoggerOptions = void 0;
|
|
24
|
+
exports.getRequestParentName = getRequestParentName;
|
|
25
|
+
exports.bindChannelLogger = bindChannelLogger;
|
|
26
|
+
const protocol_1 = require("@hydranium/protocol");
|
|
27
|
+
const inversify_1 = require("@theia/core/shared/inversify");
|
|
28
|
+
const output_channel_1 = require("@theia/output/lib/browser/output-channel");
|
|
29
|
+
/** Inversify token for the {@link ChannelLoggerOptions} constant (merged with the
|
|
30
|
+
* interface name — the standard Theia "type + token under one identifier" idiom). */
|
|
31
|
+
exports.ChannelLoggerOptions = Symbol('ChannelLoggerOptions');
|
|
32
|
+
/**
|
|
33
|
+
* Writes client-side lines to a Theia Output channel, formatted to align with
|
|
34
|
+
* the server-side `LspLogger` so both sides of the conversation interleave
|
|
35
|
+
* cleanly under the same Output panel entry. Cross-head: the data, LSP, and
|
|
36
|
+
* GLSP Theia integrations can all reuse it — nothing here is GLSP-specific.
|
|
37
|
+
*
|
|
38
|
+
* Concrete and options-configured: adopters wire it with
|
|
39
|
+
* {@link bindChannelLogger}, supplying {@link ChannelLoggerOptions}, rather
|
|
40
|
+
* than subclassing to pin a channel name.
|
|
41
|
+
*
|
|
42
|
+
* It deliberately owns **no** threshold logic. The threshold is a process-global
|
|
43
|
+
* shared by every `AbstractLogger`, whereas this class's singleton scope is
|
|
44
|
+
* whichever container bound it (for a GLSP head, one per diagram) — so applying a
|
|
45
|
+
* global from here would subscribe a preference listener per container and never
|
|
46
|
+
* dispose one. `LogLevelPreferenceContribution` owns it instead, at application
|
|
47
|
+
* scope. This class only formats and appends.
|
|
48
|
+
*/
|
|
49
|
+
let ChannelLogger = class ChannelLogger extends protocol_1.AbstractLogger {
|
|
50
|
+
outputChannels;
|
|
51
|
+
options;
|
|
52
|
+
_channel;
|
|
53
|
+
constructor(outputChannels, options, component = options.component) {
|
|
54
|
+
super(component);
|
|
55
|
+
this.outputChannels = outputChannels;
|
|
56
|
+
this.options = options;
|
|
57
|
+
}
|
|
58
|
+
get channel() {
|
|
59
|
+
return (this._channel ??= this.outputChannels.getChannel(this.options.channelName));
|
|
60
|
+
}
|
|
61
|
+
emit(_level, label, message, args) {
|
|
62
|
+
const componentSegment = this.component ? ` [${this.component}]` : '';
|
|
63
|
+
const formattedArgs = args.length > 0 ? ' ' + args.map(stringifyArg).join(' ') : '';
|
|
64
|
+
// Pad to match the server's 5-char label width so timestamps align across sources.
|
|
65
|
+
this.channel.appendLine(`[${label.padEnd(5)} - ${this.timestamp()}]${componentSegment} ${message}${formattedArgs}`);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Allocates a free instance bypassing Inversify, so a derived logger inherits
|
|
69
|
+
* the same OutputChannelManager + options without going through the container
|
|
70
|
+
* singleton. Subclasses with additional constructor dependencies must
|
|
71
|
+
* override and pass the extra args along.
|
|
72
|
+
*/
|
|
73
|
+
derive(component) {
|
|
74
|
+
const Ctor = this.constructor;
|
|
75
|
+
return new Ctor(this.outputChannels, this.options, component);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
exports.ChannelLogger = ChannelLogger;
|
|
79
|
+
exports.ChannelLogger = ChannelLogger = __decorate([
|
|
80
|
+
(0, inversify_1.injectable)(),
|
|
81
|
+
__param(0, (0, inversify_1.inject)(output_channel_1.OutputChannelManager)),
|
|
82
|
+
__param(1, (0, inversify_1.inject)(exports.ChannelLoggerOptions)),
|
|
83
|
+
__param(2, (0, inversify_1.unmanaged)()),
|
|
84
|
+
__metadata("design:paramtypes", [output_channel_1.OutputChannelManager, Object, Object])
|
|
85
|
+
], ChannelLogger);
|
|
86
|
+
/** Private token for the un-tagged singleton base that the per-class dynamic
|
|
87
|
+
* binding derives from. Kept module-internal so the only public token is the
|
|
88
|
+
* {@link ChannelLogger} class itself. */
|
|
89
|
+
const ChannelLoggerBase = Symbol('ChannelLoggerBase');
|
|
90
|
+
/**
|
|
91
|
+
* Inversify token for the browser `Tracer` — a {@link DefaultTracer} over
|
|
92
|
+
* the {@link ChannelLogger} (emit sink) + a {@link SystemClock} + a browser
|
|
93
|
+
* heap reader. The browser head has no Langium `services.Tracer` slot, so this
|
|
94
|
+
* is how frontend services that time obtain a tracer: `@inject(ChannelTracer)`.
|
|
95
|
+
* Bound by {@link bindChannelLogger}.
|
|
96
|
+
*/
|
|
97
|
+
exports.ChannelTracer = Symbol('ChannelTracer');
|
|
98
|
+
/** Reads the requesting class's name from an Inversify context, by walking the
|
|
99
|
+
* current request up to its parent binding. */
|
|
100
|
+
function getRequestParentName(context) {
|
|
101
|
+
const parent = context.currentRequest.parentRequest;
|
|
102
|
+
if (!parent || parent.bindings.length === 0) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
const binding = parent.bindings[0];
|
|
106
|
+
return binding.implementationType?.name;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Bind the {@link ChannelLogger} for an adopter's Theia frontend:
|
|
110
|
+
*
|
|
111
|
+
* - `bind(ChannelLoggerOptions).toConstantValue(options)` — the config
|
|
112
|
+
* - the un-tagged singleton base (private token), constructed by Inversify
|
|
113
|
+
* - `bind(ChannelLogger).toDynamicValue(...)` — class-scoped per request, so
|
|
114
|
+
* every `@inject(ChannelLogger)` site gets a logger pre-tagged with the
|
|
115
|
+
* requesting class name
|
|
116
|
+
*
|
|
117
|
+
* Adopters that compose their own module (bypassing a head's module helper)
|
|
118
|
+
* call this directly; head module helpers (`createGlspClientTheiaModule`)
|
|
119
|
+
* forward to it.
|
|
120
|
+
*/
|
|
121
|
+
function bindChannelLogger(bind, options) {
|
|
122
|
+
bind(exports.ChannelLoggerOptions).toConstantValue(options);
|
|
123
|
+
bind(ChannelLoggerBase).to(ChannelLogger).inSingletonScope();
|
|
124
|
+
bind(ChannelLogger).toDynamicValue(ctx => {
|
|
125
|
+
const base = ctx.container.get(ChannelLoggerBase);
|
|
126
|
+
const parentName = getRequestParentName(ctx);
|
|
127
|
+
return parentName ? base.for(parentName) : base;
|
|
128
|
+
});
|
|
129
|
+
bind(exports.ChannelTracer)
|
|
130
|
+
.toDynamicValue(ctx => new protocol_1.DefaultTracer(ctx.container.get(ChannelLoggerBase), new protocol_1.SystemClock(), readBrowserMemory))
|
|
131
|
+
.inSingletonScope();
|
|
132
|
+
}
|
|
133
|
+
/** Browser heap reader for {@link ChannelTracer}, backed by Chromium's non-standard `performance.memory`. */
|
|
134
|
+
const readBrowserMemory = () => {
|
|
135
|
+
const mem = performance.memory;
|
|
136
|
+
return mem ? { usedBytes: mem.usedJSHeapSize, totalBytes: mem.totalJSHeapSize } : undefined;
|
|
137
|
+
};
|
|
138
|
+
function stringifyArg(arg) {
|
|
139
|
+
if (typeof arg === 'string') {
|
|
140
|
+
return arg;
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
return JSON.stringify(arg);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return String(arg);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=channel-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-logger.js","sourceRoot":"","sources":["../../src/browser/channel-logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
export * from './channel-logger';
|
|
10
|
+
export * from './log-level-preference';
|
|
11
|
+
export * from './browser-capture';
|
|
12
|
+
export * from './memory-diagnostics-contribution';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
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 __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
22
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
23
|
+
};
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
// Browser-side cross-head Theia client primitives shared by the protocol heads.
|
|
26
|
+
__exportStar(require("./channel-logger"), exports);
|
|
27
|
+
__exportStar(require("./log-level-preference"), exports);
|
|
28
|
+
__exportStar(require("./browser-capture"), exports);
|
|
29
|
+
__exportStar(require("./memory-diagnostics-contribution"), exports);
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
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 { DisposableCollection } from '@hydranium/protocol';
|
|
10
|
+
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
|
|
11
|
+
import { ILogger } from '@theia/core/lib/common/logger';
|
|
12
|
+
import { PreferenceService } from '@theia/core/lib/common/preferences/preference-service';
|
|
13
|
+
import { type interfaces } from '@theia/core/shared/inversify';
|
|
14
|
+
/** Inversify token for the preference id {@link LogLevelPreferenceContribution} watches. */
|
|
15
|
+
export declare const LogLevelPreference: unique symbol;
|
|
16
|
+
/**
|
|
17
|
+
* Applies the framework log threshold from a Theia preference, once per
|
|
18
|
+
* application.
|
|
19
|
+
*
|
|
20
|
+
* **Why this is a `FrontendApplicationContribution` and not a logger concern.**
|
|
21
|
+
* The threshold is a process-global (`Logger.setLevel`, read by every
|
|
22
|
+
* `AbstractLogger` at emit time), so applying it is an *application* lifecycle
|
|
23
|
+
* event. Applying it from a logger's `@postConstruct` instead puts a global side
|
|
24
|
+
* effect on the construction of an object whose singleton scope is the
|
|
25
|
+
* per-diagram container, which costs two things:
|
|
26
|
+
*
|
|
27
|
+
* - the `onPreferenceChanged` subscription is never disposed AND is re-created
|
|
28
|
+
* for every diagram container, so opening N diagrams leaves N live listeners
|
|
29
|
+
* holding N loggers;
|
|
30
|
+
* - with no diagram open the preference is never applied at all, so anything
|
|
31
|
+
* logging before the first diagram runs at the default threshold.
|
|
32
|
+
*
|
|
33
|
+
* Bound by {@link bindLogLevelPreference} in the **frontend** container — a
|
|
34
|
+
* diagram container is a child and cannot contribute to the parent's
|
|
35
|
+
* multi-binding.
|
|
36
|
+
*/
|
|
37
|
+
export declare class LogLevelPreferenceContribution implements FrontendApplicationContribution {
|
|
38
|
+
protected readonly preferenceName: string;
|
|
39
|
+
protected readonly preferences: PreferenceService;
|
|
40
|
+
protected readonly logger: ILogger;
|
|
41
|
+
/** Holds the single preference subscription. `DisposableCollection` disposes a
|
|
42
|
+
* push-after-dispose immediately, so a subscription that lands after
|
|
43
|
+
* {@link onStop} cannot outlive the application either. */
|
|
44
|
+
protected readonly toDispose: DisposableCollection;
|
|
45
|
+
/** Guards against a second subscription if `onStart` is ever invoked twice —
|
|
46
|
+
* a duplicated listener is exactly the failure this contribution exists to
|
|
47
|
+
* prevent. */
|
|
48
|
+
protected subscribed: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Returns `void` rather than the promise, so `FrontendApplication.start` does
|
|
51
|
+
* NOT await it.
|
|
52
|
+
*
|
|
53
|
+
* This is load-bearing: `PreferenceService.ready` resolves only once the
|
|
54
|
+
* preference providers are initialised, which happens as part of the same
|
|
55
|
+
* startup sequence that runs the contributions, so awaiting it here deadlocks
|
|
56
|
+
* the frontend on its preload splash. The threshold is therefore applied as
|
|
57
|
+
* soon as preferences are ready, which is early but not synchronously before
|
|
58
|
+
* the first possible log line.
|
|
59
|
+
*/
|
|
60
|
+
onStart(): void;
|
|
61
|
+
/** The awaited body of {@link onStart}, separated so it can be driven directly
|
|
62
|
+
* in tests without going through the non-awaited lifecycle hook. */
|
|
63
|
+
protected applyWhenReady(): Promise<void>;
|
|
64
|
+
onStop(): void;
|
|
65
|
+
/**
|
|
66
|
+
* Read the preference and apply it. An unparseable or unset value leaves the
|
|
67
|
+
* current threshold alone rather than resetting it to a default — the value may
|
|
68
|
+
* legitimately have been set by another source (an env baseline, a test).
|
|
69
|
+
*
|
|
70
|
+
* Override for a custom preference→level mapping.
|
|
71
|
+
*/
|
|
72
|
+
protected applyLevel(): void;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Bind {@link LogLevelPreferenceContribution} for `preferenceName`.
|
|
76
|
+
*
|
|
77
|
+
* Call from a **frontend** container module. Adopters using
|
|
78
|
+
* `AbstractHydraniumGlspTheiaFrontendModule` set its `logLevelPreference` field
|
|
79
|
+
* instead and the base calls this for them.
|
|
80
|
+
*/
|
|
81
|
+
export declare function bindLogLevelPreference(bind: interfaces.Bind, preferenceName: string): void;
|
|
82
|
+
//# sourceMappingURL=log-level-preference.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-level-preference.d.ts","sourceRoot":"","sources":["../../src/browser/log-level-preference.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,oBAAoB,EAAyB,MAAM,qBAAqB,CAAC;AAIlF,OAAO,EAAE,+BAA+B,EAAE,MAAM,2DAA2D,CAAC;AAC5G,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uDAAuD,CAAC;AAC1F,OAAO,EAAsB,KAAK,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAEnF,4FAA4F;AAC5F,eAAO,MAAM,kBAAkB,eAA+B,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBACa,8BAA+B,YAAW,+BAA+B;IACvD,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAG,MAAM,CAAC;IAC5C,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAG,iBAAiB,CAAC;IAC7D,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAG,OAAO,CAAC;IAErD;;gEAE4D;IAC5D,SAAS,CAAC,QAAQ,CAAC,SAAS,uBAA8B;IAC1D;;mBAEe;IACf,SAAS,CAAC,UAAU,UAAS;IAE7B;;;;;;;;;;OAUG;IACH,OAAO,IAAI,IAAI;IAIf;yEACqE;cACrD,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB/C,MAAM,IAAI,IAAI;IAId;;;;;;OAMG;IACH,SAAS,CAAC,UAAU,IAAI,IAAI;CAM9B;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,CAI1F"}
|