@hydranium/core 1.0.0-next.320 → 1.0.0-next.321
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/lib/langium/diagnostics/console-tee.d.ts +27 -0
- package/lib/langium/diagnostics/console-tee.d.ts.map +1 -0
- package/lib/langium/diagnostics/console-tee.js +142 -0
- package/lib/langium/diagnostics/console-tee.js.map +1 -0
- package/lib/langium/diagnostics/format-log-arg.d.ts +18 -0
- package/lib/langium/diagnostics/format-log-arg.d.ts.map +1 -0
- package/lib/langium/diagnostics/format-log-arg.js +36 -0
- package/lib/langium/diagnostics/format-log-arg.js.map +1 -0
- package/lib/langium/diagnostics/logger.d.ts +7 -3
- package/lib/langium/diagnostics/logger.d.ts.map +1 -1
- package/lib/langium/diagnostics/logger.js +13 -6
- package/lib/langium/diagnostics/logger.js.map +1 -1
- package/lib/langium/diagnostics/lsp-logger.d.ts.map +1 -1
- package/lib/langium/diagnostics/lsp-logger.js +12 -30
- package/lib/langium/diagnostics/lsp-logger.js.map +1 -1
- package/lib/lsp/start-language-server.d.ts +8 -5
- package/lib/lsp/start-language-server.d.ts.map +1 -1
- package/lib/lsp/start-language-server.js +10 -5
- package/lib/lsp/start-language-server.js.map +1 -1
- package/lib/node/log-file-sink.d.ts +3 -2
- package/lib/node/log-file-sink.d.ts.map +1 -1
- package/lib/node/log-file-sink.js +6 -2
- package/lib/node/log-file-sink.js.map +1 -1
- package/lib/util/connection-liveness.d.ts +3 -0
- package/lib/util/connection-liveness.d.ts.map +1 -1
- package/lib/util/connection-liveness.js +15 -2
- package/lib/util/connection-liveness.js.map +1 -1
- package/lib/util/environment.d.ts +7 -0
- package/lib/util/environment.d.ts.map +1 -1
- package/lib/util/environment.js +15 -1
- package/lib/util/environment.js.map +1 -1
- package/package.json +5 -5
- package/src/langium/diagnostics/console-tee.ts +151 -0
- package/src/langium/diagnostics/format-log-arg.ts +36 -0
- package/src/langium/diagnostics/logger.ts +14 -7
- package/src/langium/diagnostics/lsp-logger.ts +11 -30
- package/src/lsp/start-language-server.ts +10 -5
- package/src/node/log-file-sink.ts +6 -2
- package/src/util/connection-liveness.ts +15 -2
- package/src/util/environment.ts +15 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 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
|
+
/** Render console lines with `render`, as `installNodeLogFileSink` does with Node's `util.format`. */
|
|
10
|
+
export declare function setConsoleFormat(render: (...args: unknown[]) => string): void;
|
|
11
|
+
/**
|
|
12
|
+
* Write each line sent through a {@link CONSOLE_TEE} method, as Langium
|
|
13
|
+
* reports a linker or validator failure, to this copy's log file under a
|
|
14
|
+
* `console` component, then hand it to the method it replaced, even when
|
|
15
|
+
* writing the file fails. The file goes first because the method a stdio
|
|
16
|
+
* connection installs throws once the connection is closed, unless its remote
|
|
17
|
+
* console catches that, as `withHydraniumLspFeatures`'s does.
|
|
18
|
+
*
|
|
19
|
+
* Node only: in a browser no file is written, and a wrapper would only stand
|
|
20
|
+
* between devtools and the call site. Called by the first `LspLogger`, and by
|
|
21
|
+
* `startLanguageServer`, which wraps again where a logger existed before a
|
|
22
|
+
* stdio connection replaced `error`, `warn`, `log` and `debug`. A console
|
|
23
|
+
* replaced after that is not wrapped again, and each copy of this package
|
|
24
|
+
* wraps the console itself, so a file two copies share gets a line twice.
|
|
25
|
+
*/
|
|
26
|
+
export declare function installConsoleTee(): void;
|
|
27
|
+
//# sourceMappingURL=console-tee.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console-tee.d.ts","sourceRoot":"","sources":["../../../src/langium/diagnostics/console-tee.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAoClF,sGAAsG;AACtG,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,MAAM,GAAG,IAAI,CAE7E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAWxC"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 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 { Format, LEVEL_LABELS, SystemClock } from '@hydranium/protocol';
|
|
10
|
+
import { formatLogArg } from './format-log-arg.js';
|
|
11
|
+
import { teeLogLine } from './logger.js';
|
|
12
|
+
import { hasProcessStderr } from '../../util/environment.js';
|
|
13
|
+
import { isConnectionGoneError } from '../../util/connection-liveness.js';
|
|
14
|
+
/** The `console` methods {@link installConsoleTee} wraps, with the level and label each line is written at. */
|
|
15
|
+
const CONSOLE_TEE = [
|
|
16
|
+
['error', 'error', LEVEL_LABELS.error],
|
|
17
|
+
['warn', 'warn', LEVEL_LABELS.warn],
|
|
18
|
+
['info', 'info', LEVEL_LABELS.info],
|
|
19
|
+
['log', 'info', 'Log'],
|
|
20
|
+
['debug', 'debug', LEVEL_LABELS.debug]
|
|
21
|
+
];
|
|
22
|
+
/** The wrappers this copy installed, so installing again wraps only a method that is none of them. */
|
|
23
|
+
const tees = new WeakSet();
|
|
24
|
+
/**
|
|
25
|
+
* Whether a line is being written or handed on: a console call made meanwhile,
|
|
26
|
+
* by a sink or a library wrapper between two installs, is only handed on.
|
|
27
|
+
*/
|
|
28
|
+
let handling = false;
|
|
29
|
+
/**
|
|
30
|
+
* What a line is stamped with: not a logger's injected clock, whose service a
|
|
31
|
+
* console call made while another service is built could resolve into a cycle.
|
|
32
|
+
*/
|
|
33
|
+
const systemClock = new SystemClock();
|
|
34
|
+
/**
|
|
35
|
+
* How a line's arguments are rendered, Node's `util.format` where the `./node`
|
|
36
|
+
* entry set it: unlike JSON, it runs no ordinary getter and no proxy trap of an
|
|
37
|
+
* argument it inspects, the traps a services container builds a service in.
|
|
38
|
+
*/
|
|
39
|
+
let format;
|
|
40
|
+
/** Render console lines with `render`, as `installNodeLogFileSink` does with Node's `util.format`. */
|
|
41
|
+
export function setConsoleFormat(render) {
|
|
42
|
+
format = render;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Write each line sent through a {@link CONSOLE_TEE} method, as Langium
|
|
46
|
+
* reports a linker or validator failure, to this copy's log file under a
|
|
47
|
+
* `console` component, then hand it to the method it replaced, even when
|
|
48
|
+
* writing the file fails. The file goes first because the method a stdio
|
|
49
|
+
* connection installs throws once the connection is closed, unless its remote
|
|
50
|
+
* console catches that, as `withHydraniumLspFeatures`'s does.
|
|
51
|
+
*
|
|
52
|
+
* Node only: in a browser no file is written, and a wrapper would only stand
|
|
53
|
+
* between devtools and the call site. Called by the first `LspLogger`, and by
|
|
54
|
+
* `startLanguageServer`, which wraps again where a logger existed before a
|
|
55
|
+
* stdio connection replaced `error`, `warn`, `log` and `debug`. A console
|
|
56
|
+
* replaced after that is not wrapped again, and each copy of this package
|
|
57
|
+
* wraps the console itself, so a file two copies share gets a line twice.
|
|
58
|
+
*/
|
|
59
|
+
export function installConsoleTee() {
|
|
60
|
+
if (!hasProcessStderr()) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
for (const [method, level, label] of CONSOLE_TEE) {
|
|
64
|
+
try {
|
|
65
|
+
wrap(method, level, label);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// A host whose console is read-only, or throws when read, keeps its methods, unwritten.
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Replace `console[method]` by a tee writing its lines at `level` under `label`, unless it is already one of this copy's tees. */
|
|
73
|
+
function wrap(method, level, label) {
|
|
74
|
+
const passOn = console[method];
|
|
75
|
+
if (tees.has(passOn)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const tee = (...args) => {
|
|
79
|
+
if (handling) {
|
|
80
|
+
Reflect.apply(passOn, console, args);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
handling = true;
|
|
84
|
+
try {
|
|
85
|
+
try {
|
|
86
|
+
const [lineLevel, lineLabel] = isDebugLine(args)
|
|
87
|
+
? ['debug', LEVEL_LABELS.debug]
|
|
88
|
+
: level === 'error' && isNodeWarning(args)
|
|
89
|
+
? ['warn', LEVEL_LABELS.warn]
|
|
90
|
+
: [level, label];
|
|
91
|
+
// As `AbstractLogger.formatLinePrefix` starts a line.
|
|
92
|
+
teeLogLine(lineLevel, () => `[${lineLabel.padEnd(5)} - ${Format.timestamp(new Date(systemClock.now()))}] [console] ${formatArgs(args)}`);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// A failing log sink must not propagate, nor keep the line from `passOn`.
|
|
96
|
+
}
|
|
97
|
+
Reflect.apply(passOn, console, args);
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
handling = false;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
console[method] = tee;
|
|
104
|
+
tees.add(tee);
|
|
105
|
+
}
|
|
106
|
+
/** `args` as the console prints them, or by {@link formatLogArg} where that throws, as for an error whose stack throws when read. */
|
|
107
|
+
function formatArgs(args) {
|
|
108
|
+
if (format) {
|
|
109
|
+
try {
|
|
110
|
+
return format(...args);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Described below instead.
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return args.map(formatLogArg).join(' ');
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* How Node's default warning handler starts what it prints through
|
|
120
|
+
* `console.error`: the runtime's release name and pid, the warning's code if
|
|
121
|
+
* it has one, and its type, as `(node:42) [DEP0005] DeprecationWarning: `.
|
|
122
|
+
*/
|
|
123
|
+
const NODE_WARNING = /^\([\w-]+:\d+\) (\[\w+\] )?\w*Warning: /;
|
|
124
|
+
/** Whether `args` is a Node process warning, a deprecation say, which is written at `warn`. */
|
|
125
|
+
function isNodeWarning(args) {
|
|
126
|
+
return args.length === 1 && typeof args[0] === 'string' && NODE_WARNING.test(args[0]);
|
|
127
|
+
}
|
|
128
|
+
/** What Langium's `initialized` handler logs when a service's start rejects. */
|
|
129
|
+
const LANGIUM_START_FAILURES = new Set([
|
|
130
|
+
'Error in ConfigurationProvider initialization:',
|
|
131
|
+
'Error in WorkspaceManager initialization:'
|
|
132
|
+
]);
|
|
133
|
+
/**
|
|
134
|
+
* Whether `args` reports routine teardown, which is written at `debug`:
|
|
135
|
+
* Langium's failed configuration or workspace start whose connection has gone.
|
|
136
|
+
* `vscode-languageserver`'s `Sending log message failed` is not, since it
|
|
137
|
+
* carries no reason; `withHydraniumLspFeatures` keeps teardown from printing it.
|
|
138
|
+
*/
|
|
139
|
+
function isDebugLine(args) {
|
|
140
|
+
return args.length === 2 && LANGIUM_START_FAILURES.has(args[0]) && isConnectionGoneError(args[1]);
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=console-tee.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console-tee.js","sourceRoot":"","sources":["../../../src/langium/diagnostics/console-tee.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 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
|
+
* Render a log argument for the single string a line carries, to the LSP
|
|
11
|
+
* `window/logMessage` channel and the log file: a string as it is, an error by
|
|
12
|
+
* its stack and causes, another object as JSON, and anything else, what JSON
|
|
13
|
+
* renders as nothing or cannot render included, by its description. Never
|
|
14
|
+
* throws, since a log call that throws loses the line and interrupts its
|
|
15
|
+
* caller. A `%s`-style specifier is not substituted.
|
|
16
|
+
*/
|
|
17
|
+
export declare function formatLogArg(value: unknown): string;
|
|
18
|
+
//# sourceMappingURL=format-log-arg.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-log-arg.d.ts","sourceRoot":"","sources":["../../../src/langium/diagnostics/format-log-arg.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAIlF;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAgBnD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 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 { describeErrorWithStack } from '@hydranium/protocol';
|
|
10
|
+
/**
|
|
11
|
+
* Render a log argument for the single string a line carries, to the LSP
|
|
12
|
+
* `window/logMessage` channel and the log file: a string as it is, an error by
|
|
13
|
+
* its stack and causes, another object as JSON, and anything else, what JSON
|
|
14
|
+
* renders as nothing or cannot render included, by its description. Never
|
|
15
|
+
* throws, since a log call that throws loses the line and interrupts its
|
|
16
|
+
* caller. A `%s`-style specifier is not substituted.
|
|
17
|
+
*/
|
|
18
|
+
export function formatLogArg(value) {
|
|
19
|
+
if (typeof value === 'string') {
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
if (typeof value === 'object' && value !== null) {
|
|
23
|
+
try {
|
|
24
|
+
// `undefined` where `toJSON` returns nothing.
|
|
25
|
+
const json = value instanceof Error ? undefined : JSON.stringify(value);
|
|
26
|
+
if (json !== undefined) {
|
|
27
|
+
return json;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Cyclic, not serialisable, or a revoked proxy; described below instead.
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return describeErrorWithStack(value);
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=format-log-arg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-log-arg.js","sourceRoot":"","sources":["../../../src/langium/diagnostics/format-log-arg.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAE7D;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc;IACxC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC;IAChB,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC/C,IAAI,CAAC;YACF,8CAA8C;YAC9C,MAAM,IAAI,GAAuB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YAC5F,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACtB,OAAO,IAAI,CAAC;YACf,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACN,yEAAyE;QAC5E,CAAC;IACJ,CAAC;IACD,OAAO,sBAAsB,CAAC,KAAK,CAAC,CAAC;AACxC,CAAC"}
|
|
@@ -70,11 +70,15 @@ export declare function getLogFilePath(): string | undefined;
|
|
|
70
70
|
* Fan a single already-formatted log line out to the file-tee target, if one is
|
|
71
71
|
* configured and `level` is within the file's threshold
|
|
72
72
|
* (`Logger.getFileLevel() ?? Logger.getLevel()`). Called by `LspLogger`'s emit in
|
|
73
|
-
* addition to the LSP/console sink
|
|
73
|
+
* addition to the LSP/console sink, and by the console tee before the console
|
|
74
|
+
* method it wraps — log sinks fan out, they don't replace.
|
|
74
75
|
* While the target is still pending (unresolved `{placeholder}`), the line is
|
|
75
|
-
* buffered instead of dropped.
|
|
76
|
+
* buffered instead of dropped. Where no sink is installed, as in a process
|
|
77
|
+
* that never loaded `@hydranium/core/node`, nothing is built or buffered,
|
|
78
|
+
* since nothing will write it. A caller whose line is costly to build passes
|
|
79
|
+
* a function, called only when the file takes the line.
|
|
76
80
|
*/
|
|
77
|
-
export declare function teeLogLine(level: LogLevel, formatted: string): void;
|
|
81
|
+
export declare function teeLogLine(level: LogLevel, formatted: string | (() => string)): void;
|
|
78
82
|
export { toLogFileWorkspaceToken } from './log-file-token.js';
|
|
79
83
|
/**
|
|
80
84
|
* Shared options-base for every framework-bound service that derives a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../src/langium/diagnostics/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAuB,KAAK,QAAQ,EAAU,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGpG,yGAAyG;AACzG,eAAO,MAAM,kBAAkB,QAAkB,CAAC;AAElD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,WAAW;IACzB,2EAA2E;IAC3E,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,6EAA6E;IAC7E,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAID,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,SAAS,GAAG,IAAI,CAElE;AA6CD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAElE;AA2CD;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,CAAC,EAAE,YAAY,GAAG,IAAI,CAOnF;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAG3E;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAO3C;AAaD,oFAAoF;AACpF,wBAAgB,cAAc,IAAI,MAAM,GAAG,SAAS,CAEnD;AAED
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../src/langium/diagnostics/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAuB,KAAK,QAAQ,EAAU,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGpG,yGAAyG;AACzG,eAAO,MAAM,kBAAkB,QAAkB,CAAC;AAElD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,WAAW;IACzB,2EAA2E;IAC3E,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,6EAA6E;IAC7E,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAID,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,SAAS,GAAG,IAAI,CAElE;AA6CD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAElE;AA2CD;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,CAAC,EAAE,YAAY,GAAG,IAAI,CAOnF;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAG3E;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAO3C;AAaD,oFAAoF;AACpF,wBAAgB,cAAc,IAAI,MAAM,GAAG,SAAS,CAEnD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,GAAG,IAAI,CAepF;AAKD,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAE9D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,cAAc;IAC5B;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAMrC"}
|
|
@@ -16,7 +16,7 @@ export function setLogFileSink(sink) {
|
|
|
16
16
|
fileSink = sink;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
|
-
* File-tee state for `LspLogger`'s emit. When
|
|
19
|
+
* File-tee state for `LspLogger`'s emit and the console tee. When
|
|
20
20
|
* non-empty, every formatted log line is appended to the path in addition to
|
|
21
21
|
* its LSP `window/logMessage` / console destination (see {@link teeLogLine}).
|
|
22
22
|
* Diagnostic surface only — once the LSP connection binds, server logs route
|
|
@@ -166,19 +166,26 @@ export function getLogFilePath() {
|
|
|
166
166
|
* Fan a single already-formatted log line out to the file-tee target, if one is
|
|
167
167
|
* configured and `level` is within the file's threshold
|
|
168
168
|
* (`Logger.getFileLevel() ?? Logger.getLevel()`). Called by `LspLogger`'s emit in
|
|
169
|
-
* addition to the LSP/console sink
|
|
169
|
+
* addition to the LSP/console sink, and by the console tee before the console
|
|
170
|
+
* method it wraps — log sinks fan out, they don't replace.
|
|
170
171
|
* While the target is still pending (unresolved `{placeholder}`), the line is
|
|
171
|
-
* buffered instead of dropped.
|
|
172
|
+
* buffered instead of dropped. Where no sink is installed, as in a process
|
|
173
|
+
* that never loaded `@hydranium/core/node`, nothing is built or buffered,
|
|
174
|
+
* since nothing will write it. A caller whose line is costly to build passes
|
|
175
|
+
* a function, called only when the file takes the line.
|
|
172
176
|
*/
|
|
173
177
|
export function teeLogLine(level, formatted) {
|
|
174
178
|
if (!logFileTemplate || LEVEL_ORDER[level] > LEVEL_ORDER[Logger.getFileLevel() ?? Logger.getLevel()]) {
|
|
175
179
|
return;
|
|
176
180
|
}
|
|
177
|
-
|
|
181
|
+
if (!fileSink) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const line = `${typeof formatted === 'string' ? formatted : formatted()}\n`;
|
|
178
185
|
if (resolvedLogFilePath) {
|
|
179
186
|
// The sink swallows its own errors; recursing into the logger here would
|
|
180
|
-
// re-enter emit and loop. The
|
|
181
|
-
fileSink
|
|
187
|
+
// re-enter emit and loop. The caller delivers the line to its other sink.
|
|
188
|
+
fileSink.append(resolvedLogFilePath, line);
|
|
182
189
|
}
|
|
183
190
|
else {
|
|
184
191
|
pendingLogLines.push(line);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../../src/langium/diagnostics/logger.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../../src/langium/diagnostics/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,MAAM,EAAE,WAAW,EAAiB,MAAM,EAAqB,MAAM,qBAAqB,CAAC;AACpG,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE3F,yGAAyG;AACzG,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAoBlD,IAAI,QAAiC,CAAC;AAEtC,0EAA0E;AAC1E,MAAM,UAAU,cAAc,CAAC,IAA6B;IACzD,QAAQ,GAAG,IAAI,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,IAAI,eAAmC,CAAC;AACxC,oFAAoF;AACpF,IAAI,mBAAuC,CAAC;AAC5C,gFAAgF;AAChF,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAkB,CAAC;AACtD;;;;GAIG;AACH,MAAM,eAAe,GAAa,EAAE,CAAC;AAErC,wDAAwD;AACxD,MAAM,mBAAmB,GAAG,WAAW,CAAC;AAExC;;;GAGG;AACH,SAAS,6BAA6B,CAAC,QAAgB;IACpD,IAAI,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,SAAS,CAAC,CAAC,CAAC;IAC3E,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,mBAAmB,EAAE,CAAC;QAC/C,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,IAAI,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,QAAgB;IACvD,OAAO,6BAA6B,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,mBAAmB,EAAE,GAAG,CAAC,EAAE,YAAY,UAAU,EAAE,IAAI,SAAS,EAAE,CAAC,CAAC;AACzI,CAAC;AAED;;;GAGG;AACH,SAAS,qBAAqB;IAC3B,IAAI,CAAC,eAAe,EAAE,CAAC;QACpB,mBAAmB,GAAG,SAAS,CAAC;QAChC,OAAO;IACV,CAAC;IACD,MAAM,IAAI,GAAG,6BAA6B,CAAC,eAAe,CAAC,CAAC;IAC5D,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,mBAAmB,GAAG,SAAS,CAAC;QAChC,0EAA0E;QAC1E,6DAA6D;QAC7D,qBAAqB,EAAE,CAAC;QACxB,OAAO;IACV,CAAC;IACD,mBAAmB,GAAG,IAAI,CAAC;IAC3B,YAAY,CAAC,IAAI,CAAC,CAAC;IACnB,oBAAoB,EAAE,CAAC;AAC1B,CAAC;AAED;;;;GAIG;AACH,SAAS,YAAY,CAAC,QAAgB;IACnC,oFAAoF;IACpF,QAAQ,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;AACjC,CAAC;AAED,+EAA+E;AAC/E,SAAS,oBAAoB;IAC1B,IAAI,CAAC,mBAAmB,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxD,OAAO;IACV,CAAC;IACD,QAAQ,EAAE,MAAM,CAAC,mBAAmB,EAAE,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAChE,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,cAAc,CAAC,IAAwB,EAAE,KAAoB;IAC1E,eAAe,GAAG,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7D,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACzD,mBAAmB,CAAC,KAAK,EAAE,CAAC;IAC5B,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC;IAC3B,mBAAmB,GAAG,SAAS,CAAC;IAChC,qBAAqB,EAAE,CAAC;AAC3B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,IAAY,EAAE,KAAa;IAClE,mBAAmB,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACrC,qBAAqB,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB;IACjC,IAAI,CAAC,eAAe,IAAI,mBAAmB,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3E,OAAO;IACV,CAAC;IACD,mBAAmB,GAAG,yBAAyB,CAAC,eAAe,CAAC,CAAC;IACjE,YAAY,CAAC,mBAAmB,CAAC,CAAC;IAClC,oBAAoB,EAAE,CAAC;AAC1B,CAAC;AAED,+EAA+E;AAC/E,IAAI,yBAAyB,GAAG,KAAK,CAAC;AACtC,SAAS,qBAAqB;IAC3B,IAAI,yBAAyB,EAAE,CAAC;QAC7B,OAAO;IACV,CAAC;IACD,yBAAyB,GAAG,IAAI,CAAC;IACjC,2EAA2E;IAC3E,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,oBAAoB,EAAE,CAAC,CAAC;AACxD,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,cAAc;IAC3B,OAAO,mBAAmB,CAAC;AAC9B,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CAAC,KAAe,EAAE,SAAkC;IAC3E,IAAI,CAAC,eAAe,IAAI,WAAW,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC,YAAY,EAAE,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC;QACpG,OAAO;IACV,CAAC;IACD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACb,OAAO;IACV,CAAC;IACD,MAAM,IAAI,GAAG,GAAG,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC;IAC5E,IAAI,mBAAmB,EAAE,CAAC;QACvB,yEAAyE;QACzE,0EAA0E;QAC1E,QAAQ,CAAC,MAAM,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAC;IAC9C,CAAC;SAAM,CAAC;QACL,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;AACJ,CAAC;AAED,gFAAgF;AAChF,8EAA8E;AAC9E,2EAA2E;AAC3E,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAqC9D;;;;GAIG;AACH,MAAM,UAAU,YAAY;IACzB,MAAM,GAAG,GAAG,kBAAkB,EAAE,CAAC;IACjC,IAAI,CAAC,GAAG,EAAE,CAAC;QACR,OAAO,EAAE,CAAC;IACb,CAAC;IACD,OAAO,QAAQ,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;AAC5G,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lsp-logger.d.ts","sourceRoot":"","sources":["../../../src/langium/diagnostics/lsp-logger.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EACJ,cAAc,EACd,KAAK,KAAK,EAKV,MAAM,EACN,KAAK,QAAQ,EACb,KAAK,YAAY,EAEjB,KAAK,oBAAoB,
|
|
1
|
+
{"version":3,"file":"lsp-logger.d.ts","sourceRoot":"","sources":["../../../src/langium/diagnostics/lsp-logger.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EACJ,cAAc,EACd,KAAK,KAAK,EAKV,MAAM,EACN,KAAK,QAAQ,EACb,KAAK,YAAY,EAEjB,KAAK,oBAAoB,EAE3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAgBzD;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,oBAAoB,CAAC,YAAY,GAAG,SAAS,CAAC,CAAC;CACzE;AAED;;;;;;;;;;GAUG;AACH,qBAAa,SAAU,SAAQ,cAAe,YAAW,MAAM;IAEzD,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,oBAAoB;IADpD,YACsB,QAAQ,EAAE,oBAAoB,EACjD,OAAO,GAAE,gBAAqB,EAwDhC;IAED;;;;;;;;;;;;;;;OAeG;IACH,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAc7D;IAED;;;;;;;OAOG;IACH,UAAmB,KAAK,IAAI,KAAK,CAEhC;IAED;;;;;;;;OAQG;IACM,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,IAAI,CAExC;IAED,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI,CAuC9F;IAED,SAAS,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAGxC;CAGH"}
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
*
|
|
7
7
|
* SPDX-License-Identifier: MIT
|
|
8
8
|
********************************************************************************/
|
|
9
|
-
import { AbstractLogger, DEFAULT_LOG_FILE_ENV, DEFAULT_LOG_FILE_LEVEL_ENV, DEFAULT_LOG_LEVEL_ENV, LEVEL_ORDER, Logger, ObservableValue,
|
|
9
|
+
import { AbstractLogger, DEFAULT_LOG_FILE_ENV, DEFAULT_LOG_FILE_LEVEL_ENV, DEFAULT_LOG_LEVEL_ENV, LEVEL_ORDER, Logger, ObservableValue, parseLogLevel } from '@hydranium/protocol';
|
|
10
|
+
import { installConsoleTee } from './console-tee.js';
|
|
11
|
+
import { formatLogArg } from './format-log-arg.js';
|
|
10
12
|
import { setLogFilePath, teeLogLine } from './logger.js';
|
|
11
13
|
import { processEnv, writeStderr } from '../../util/environment.js';
|
|
12
14
|
/**
|
|
@@ -16,6 +18,8 @@ import { processEnv, writeStderr } from '../../util/environment.js';
|
|
|
16
18
|
* live setting.
|
|
17
19
|
*/
|
|
18
20
|
let envBaselineApplied = false;
|
|
21
|
+
/** Whether the first logger has run the console tee's install, which does nothing in a browser. */
|
|
22
|
+
let consoleTeeInstalled = false;
|
|
19
23
|
/**
|
|
20
24
|
* LSP-bound implementation of {@link Logger}. Forwards messages to the
|
|
21
25
|
* language client via the connection bound in {@link ServerSharedServices}.
|
|
@@ -63,6 +67,13 @@ export class LspLogger extends AbstractLogger {
|
|
|
63
67
|
}
|
|
64
68
|
}
|
|
65
69
|
}
|
|
70
|
+
// Once, so a server without an LSP head writes console lines to the file
|
|
71
|
+
// too; `startLanguageServer` wraps again where a stdio connection created
|
|
72
|
+
// after this logger replaced them.
|
|
73
|
+
if (!consoleTeeInstalled) {
|
|
74
|
+
consoleTeeInstalled = true;
|
|
75
|
+
installConsoleTee();
|
|
76
|
+
}
|
|
66
77
|
// Apply the configured threshold from the root logger only — `derive`
|
|
67
78
|
// passes just `component`, so children never carry `logThreshold`. The
|
|
68
79
|
// setting overrides the env baseline above and re-applies on every change;
|
|
@@ -232,33 +243,4 @@ const LSP_CONSOLE = {
|
|
|
232
243
|
debug: c => c.console.debug.bind(c.console),
|
|
233
244
|
trace: c => c.console.log.bind(c.console)
|
|
234
245
|
};
|
|
235
|
-
/**
|
|
236
|
-
* Render a logger-argument value into the single combined string the LSP
|
|
237
|
-
* `window/logMessage` channel takes. Mirrors how raw `console.*` would
|
|
238
|
-
* format multiple args, but with explicit handling for Errors (stack) and
|
|
239
|
-
* objects (JSON).
|
|
240
|
-
*/
|
|
241
|
-
function formatLogArg(value) {
|
|
242
|
-
if (value === undefined) {
|
|
243
|
-
return 'undefined';
|
|
244
|
-
}
|
|
245
|
-
if (value === null) {
|
|
246
|
-
return 'null';
|
|
247
|
-
}
|
|
248
|
-
if (typeof value === 'string') {
|
|
249
|
-
return value;
|
|
250
|
-
}
|
|
251
|
-
if (value instanceof Error) {
|
|
252
|
-
return describeErrorWithStack(value);
|
|
253
|
-
}
|
|
254
|
-
if (typeof value === 'object') {
|
|
255
|
-
try {
|
|
256
|
-
return JSON.stringify(value);
|
|
257
|
-
}
|
|
258
|
-
catch {
|
|
259
|
-
return String(value);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
return String(value);
|
|
263
|
-
}
|
|
264
246
|
//# sourceMappingURL=lsp-logger.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lsp-logger.js","sourceRoot":"","sources":["../../../src/langium/diagnostics/lsp-logger.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EACJ,cAAc,EAEd,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,WAAW,EACX,MAAM,EAGN,eAAe,EAEf,
|
|
1
|
+
{"version":3,"file":"lsp-logger.js","sourceRoot":"","sources":["../../../src/langium/diagnostics/lsp-logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -34,11 +34,14 @@ import { type LangiumSharedServices, type ServiceRequirements } from '@hydranium
|
|
|
34
34
|
export declare function assertLspHeadComposed(services: LangiumSharedServices): void;
|
|
35
35
|
/**
|
|
36
36
|
* The framework's LSP server entry point: {@link assertLspHeadComposed}, then
|
|
37
|
-
* {@link guardDiagnosticsConnection} on the tree's connection, then
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
37
|
+
* {@link guardDiagnosticsConnection} on the tree's connection, then the
|
|
38
|
+
* console's wrapping for the log file, then Langium's `startLanguageServer`.
|
|
39
|
+
* Signature-compatible with Langium's, so it is a drop-in for adopters
|
|
40
|
+
* importing from `@hydranium/core/lsp`. Calling Langium's directly skips the
|
|
41
|
+
* composition check, the guard without which a publish racing teardown fails
|
|
42
|
+
* the build that validated the document, and the wrapping, which a stdio
|
|
43
|
+
* connection discards where a logger was built before it (see
|
|
44
|
+
* `installConsoleTee`).
|
|
42
45
|
*/
|
|
43
46
|
export declare function startLanguageServer(services: LangiumSharedServices, serviceRequirements?: Partial<ServiceRequirements>): void;
|
|
44
47
|
//# sourceMappingURL=start-language-server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start-language-server.d.ts","sourceRoot":"","sources":["../../src/lsp/start-language-server.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAEJ,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EAC1B,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"start-language-server.d.ts","sourceRoot":"","sources":["../../src/lsp/start-language-server.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAEJ,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EAC1B,MAAM,wBAAwB,CAAC;AAKhC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAe3E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,qBAAqB,EAAE,mBAAmB,GAAE,OAAO,CAAC,mBAAmB,CAAM,GAAG,IAAI,CAQjI"}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { startLanguageServer as startLangiumLanguageServer } from '@hydranium/langium/lsp';
|
|
10
10
|
import { HydraniumDocumentUpdateHandler } from './hydranium-document-update-handler.js';
|
|
11
11
|
import { guardDiagnosticsConnection } from './diagnostics-connection.js';
|
|
12
|
+
import { installConsoleTee } from '../langium/diagnostics/console-tee.js';
|
|
12
13
|
/**
|
|
13
14
|
* Fail the LSP head's start when its shared module was never composed.
|
|
14
15
|
*
|
|
@@ -49,11 +50,14 @@ export function assertLspHeadComposed(services) {
|
|
|
49
50
|
}
|
|
50
51
|
/**
|
|
51
52
|
* The framework's LSP server entry point: {@link assertLspHeadComposed}, then
|
|
52
|
-
* {@link guardDiagnosticsConnection} on the tree's connection, then
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
53
|
+
* {@link guardDiagnosticsConnection} on the tree's connection, then the
|
|
54
|
+
* console's wrapping for the log file, then Langium's `startLanguageServer`.
|
|
55
|
+
* Signature-compatible with Langium's, so it is a drop-in for adopters
|
|
56
|
+
* importing from `@hydranium/core/lsp`. Calling Langium's directly skips the
|
|
57
|
+
* composition check, the guard without which a publish racing teardown fails
|
|
58
|
+
* the build that validated the document, and the wrapping, which a stdio
|
|
59
|
+
* connection discards where a logger was built before it (see
|
|
60
|
+
* `installConsoleTee`).
|
|
57
61
|
*/
|
|
58
62
|
export function startLanguageServer(services, serviceRequirements = {}) {
|
|
59
63
|
assertLspHeadComposed(services);
|
|
@@ -61,6 +65,7 @@ export function startLanguageServer(services, serviceRequirements = {}) {
|
|
|
61
65
|
if (connection) {
|
|
62
66
|
guardDiagnosticsConnection(connection);
|
|
63
67
|
}
|
|
68
|
+
installConsoleTee();
|
|
64
69
|
startLangiumLanguageServer(services, serviceRequirements);
|
|
65
70
|
}
|
|
66
71
|
//# sourceMappingURL=start-language-server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start-language-server.js","sourceRoot":"","sources":["../../src/lsp/start-language-server.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EACJ,mBAAmB,IAAI,0BAA0B,EAGnD,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,8BAA8B,EAAE,MAAM,wCAAwC,CAAC;AACxF,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"start-language-server.js","sourceRoot":"","sources":["../../src/lsp/start-language-server.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EACJ,mBAAmB,IAAI,0BAA0B,EAGnD,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,8BAA8B,EAAE,MAAM,wCAAwC,CAAC;AACxF,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAA+B;IAClE,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,UAAU,EAAE,CAAC;QAC7B,OAAO;IACV,CAAC;IACD,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,qBAAqB,CAAC;IACnD,IAAI,CAAC,CAAC,OAAO,YAAY,8BAA8B,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CACZ,oGAAoG;YACjG,IAAI,OAAO,EAAE,WAAW,EAAE,IAAI,IAAI,SAAS,uEAAuE;YAClH,kHAAkH;YAClH,kGAAkG;YAClG,4GAA4G;YAC5G,4CAA4C,CACjD,CAAC;IACL,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAA+B,EAAE,mBAAmB,GAAiC,EAAE;IACxH,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IAChC,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC;IAC3C,IAAI,UAAU,EAAE,CAAC;QACd,0BAA0B,CAAC,UAAU,CAAC,CAAC;IAC1C,CAAC;IACD,iBAAiB,EAAE,CAAC;IACpB,0BAA0B,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;AAC7D,CAAC"}
|
|
@@ -15,8 +15,9 @@ import { type LogFileSink } from '../langium/diagnostics/logger.js';
|
|
|
15
15
|
export declare const nodeLogFileSink: LogFileSink;
|
|
16
16
|
/**
|
|
17
17
|
* Install the Node file-tee sink so a configured `HYDRANIUM_LOG_FILE` target
|
|
18
|
-
* is honoured
|
|
19
|
-
*
|
|
18
|
+
* is honoured, and render console lines in it with `util.format`, as the
|
|
19
|
+
* console prints them. Idempotent. Called once at `@hydranium/core/node` load
|
|
20
|
+
* so any Node host that imports the entry gets the file-tee for free.
|
|
20
21
|
*/
|
|
21
22
|
export declare function installNodeLogFileSink(): void;
|
|
22
23
|
//# sourceMappingURL=log-file-sink.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log-file-sink.d.ts","sourceRoot":"","sources":["../../src/node/log-file-sink.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;
|
|
1
|
+
{"version":3,"file":"log-file-sink.d.ts","sourceRoot":"","sources":["../../src/node/log-file-sink.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAMlF,OAAO,EAAE,KAAK,WAAW,EAAkB,MAAM,kCAAkC,CAAC;AAEpF;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,WAe7B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAG7C"}
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
import { appendFileSync, mkdirSync } from 'node:fs';
|
|
10
10
|
import { dirname } from 'node:path';
|
|
11
|
+
import { format } from 'node:util';
|
|
12
|
+
import { setConsoleFormat } from '../langium/diagnostics/console-tee.js';
|
|
11
13
|
import { setLogFileSink } from '../langium/diagnostics/logger.js';
|
|
12
14
|
/**
|
|
13
15
|
* `node:fs`-backed {@link LogFileSink}. Synchronous (`appendFileSync`) so log
|
|
@@ -34,10 +36,12 @@ export const nodeLogFileSink = {
|
|
|
34
36
|
};
|
|
35
37
|
/**
|
|
36
38
|
* Install the Node file-tee sink so a configured `HYDRANIUM_LOG_FILE` target
|
|
37
|
-
* is honoured
|
|
38
|
-
*
|
|
39
|
+
* is honoured, and render console lines in it with `util.format`, as the
|
|
40
|
+
* console prints them. Idempotent. Called once at `@hydranium/core/node` load
|
|
41
|
+
* so any Node host that imports the entry gets the file-tee for free.
|
|
39
42
|
*/
|
|
40
43
|
export function installNodeLogFileSink() {
|
|
41
44
|
setLogFileSink(nodeLogFileSink);
|
|
45
|
+
setConsoleFormat(format);
|
|
42
46
|
}
|
|
43
47
|
//# sourceMappingURL=log-file-sink.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log-file-sink.js","sourceRoot":"","sources":["../../src/node/log-file-sink.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAoB,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAEpF;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAgB;IACzC,MAAM,CAAC,IAAY,EAAE,IAAY;QAC9B,IAAI,CAAC;YACF,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACN,yCAAyC;QAC5C,CAAC;IACJ,CAAC;IACD,SAAS,CAAC,IAAY;QACnB,IAAI,CAAC;YACF,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;QAAC,MAAM,CAAC;YACN,oEAAoE;QACvE,CAAC;IACJ,CAAC;CACH,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"log-file-sink.js","sourceRoot":"","sources":["../../src/node/log-file-sink.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAoB,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAEpF;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAgB;IACzC,MAAM,CAAC,IAAY,EAAE,IAAY;QAC9B,IAAI,CAAC;YACF,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACN,yCAAyC;QAC5C,CAAC;IACJ,CAAC;IACD,SAAS,CAAC,IAAY;QACnB,IAAI,CAAC;YACF,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;QAAC,MAAM,CAAC;YACN,oEAAoE;QACvE,CAAC;IACJ,CAAC;CACH,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB;IACnC,cAAc,CAAC,eAAe,CAAC,CAAC;IAChC,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC"}
|
|
@@ -18,6 +18,9 @@
|
|
|
18
18
|
* into errors that look like they need investigation. A `true` for a real
|
|
19
19
|
* `applyEdit` failure (the client refused, the request malformed) hides it.
|
|
20
20
|
*
|
|
21
|
+
* Never throws, nor does {@link isClientGoneError}: a caller classifying a
|
|
22
|
+
* failure would otherwise lose it to a getter or proxy trap of the error.
|
|
23
|
+
*
|
|
21
24
|
* Callers that only need to *survive* a dead connection rather than classify it
|
|
22
25
|
* should just catch — see `LspLogger.emit`, whose fallback path does exactly
|
|
23
26
|
* that, since a logger has nowhere to report a logging failure anyway.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection-liveness.d.ts","sourceRoot":"","sources":["../../src/util/connection-liveness.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;
|
|
1
|
+
{"version":3,"file":"connection-liveness.d.ts","sourceRoot":"","sources":["../../src/util/connection-liveness.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAkDlF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAO3D;AAOD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAUvD"}
|
|
@@ -39,7 +39,16 @@ const CONNECTION_GONE_PATTERNS = [
|
|
|
39
39
|
];
|
|
40
40
|
const DESTROYED_STREAM_CODE = 'ERR_STREAM_DESTROYED';
|
|
41
41
|
function isDestroyedStreamError(err) {
|
|
42
|
-
return err
|
|
42
|
+
return errorCode(err) === DESTROYED_STREAM_CODE;
|
|
43
|
+
}
|
|
44
|
+
/** `err.code` where `err` is an `Error`, or `undefined` where reading it throws, as a getter or a revoked proxy can. */
|
|
45
|
+
function errorCode(err) {
|
|
46
|
+
try {
|
|
47
|
+
return err instanceof Error && 'code' in err ? err.code : undefined;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
43
52
|
}
|
|
44
53
|
/**
|
|
45
54
|
* Whether `err` means "the peer is gone", as opposed to a genuine failure of
|
|
@@ -53,6 +62,9 @@ function isDestroyedStreamError(err) {
|
|
|
53
62
|
* into errors that look like they need investigation. A `true` for a real
|
|
54
63
|
* `applyEdit` failure (the client refused, the request malformed) hides it.
|
|
55
64
|
*
|
|
65
|
+
* Never throws, nor does {@link isClientGoneError}: a caller classifying a
|
|
66
|
+
* failure would otherwise lose it to a getter or proxy trap of the error.
|
|
67
|
+
*
|
|
56
68
|
* Callers that only need to *survive* a dead connection rather than classify it
|
|
57
69
|
* should just catch — see `LspLogger.emit`, whose fallback path does exactly
|
|
58
70
|
* that, since a logger has nowhere to report a logging failure anyway.
|
|
@@ -83,7 +95,8 @@ export function isClientGoneError(err) {
|
|
|
83
95
|
if (isConnectionGoneError(err)) {
|
|
84
96
|
return true;
|
|
85
97
|
}
|
|
86
|
-
|
|
98
|
+
const code = errorCode(err);
|
|
99
|
+
if (typeof code === 'string' && SOCKET_GONE_CODES.has(code)) {
|
|
87
100
|
return true;
|
|
88
101
|
}
|
|
89
102
|
const message = describeError(err);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection-liveness.js","sourceRoot":"","sources":["../../src/util/connection-liveness.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,wBAAwB,GAAG;IAC9B,wBAAwB;IACxB,sBAAsB;IACtB,yBAAyB;IACzB,uBAAuB;IACvB,8BAA8B;CAChC,CAAC;AAEF,MAAM,qBAAqB,GAAG,sBAAsB,CAAC;AAErD,SAAS,sBAAsB,CAAC,GAAY;IACzC,OAAO,GAAG,YAAY,KAAK,IAAI,MAAM,IAAI,GAAG,
|
|
1
|
+
{"version":3,"file":"connection-liveness.js","sourceRoot":"","sources":["../../src/util/connection-liveness.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,wBAAwB,GAAG;IAC9B,wBAAwB;IACxB,sBAAsB;IACtB,yBAAyB;IACzB,uBAAuB;IACvB,8BAA8B;CAChC,CAAC;AAEF,MAAM,qBAAqB,GAAG,sBAAsB,CAAC;AAErD,SAAS,sBAAsB,CAAC,GAAY;IACzC,OAAO,SAAS,CAAC,GAAG,CAAC,KAAK,qBAAqB,CAAC;AACnD,CAAC;AAED,wHAAwH;AACxH,SAAS,SAAS,CAAC,GAAY;IAC5B,IAAI,CAAC;QACF,OAAO,GAAG,YAAY,KAAK,IAAI,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACN,OAAO,SAAS,CAAC;IACpB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAY;IAC/C,IAAI,sBAAsB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC;IACf,CAAC;IACD,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IACzC,OAAO,wBAAwB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;AAE3D,4FAA4F;AAC5F,MAAM,mBAAmB,GAAG,wBAAwB,CAAC;AAErD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAY;IAC3C,IAAI,qBAAqB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC;IACf,CAAC;IACD,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3D,OAAO,IAAI,CAAC;IACf,CAAC;IACD,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IACnC,OAAO,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;AAC1D,CAAC"}
|
|
@@ -29,6 +29,13 @@ export type ProcessEvent = 'exit' | 'beforeExit' | 'uncaughtException' | 'unhand
|
|
|
29
29
|
* `process`, no such events).
|
|
30
30
|
*/
|
|
31
31
|
export declare function onProcessEvent(event: ProcessEvent, handler: (...args: unknown[]) => void): void;
|
|
32
|
+
/**
|
|
33
|
+
* Whether a Node-like `process` exposes a writable standard error. A stricter
|
|
34
|
+
* test for Node than {@link processEnv}, since a bundler can shim
|
|
35
|
+
* `process.env` into a browser build without a writable stderr. `false`
|
|
36
|
+
* where reading the stderr throws, as a shim's getter can.
|
|
37
|
+
*/
|
|
38
|
+
export declare function hasProcessStderr(): boolean;
|
|
32
39
|
/**
|
|
33
40
|
* Write already-formatted text to the process's standard error, when a
|
|
34
41
|
* Node-like `process` exposes one. Returns `true` when the text was written and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../src/util/environment.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAYlF,wEAAwE;AACxE,wBAAgB,UAAU,IAAI,MAAM,CAAC,UAAU,GAAG,SAAS,CAE1D;AAED,gEAAgE;AAChE,wBAAgB,UAAU,IAAI,MAAM,GAAG,SAAS,CAE/C;AAED;;;;;;;;;GASG;AACH,wBAAgB,2BAA2B,IAAI,OAAO,CAGrD;AAED,2FAA2F;AAC3F,MAAM,MAAM,YAAY,GACnB,MAAM,GACN,YAAY,GACZ,mBAAmB,GACnB,oBAAoB,GACpB,kBAAkB,GAClB,SAAS,GACT,QAAQ,GACR,SAAS,GACT,QAAQ,CAAC;AAEd;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAI/F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMjD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAKrG"}
|
|
1
|
+
{"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../src/util/environment.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAYlF,wEAAwE;AACxE,wBAAgB,UAAU,IAAI,MAAM,CAAC,UAAU,GAAG,SAAS,CAE1D;AAED,gEAAgE;AAChE,wBAAgB,UAAU,IAAI,MAAM,GAAG,SAAS,CAE/C;AAED;;;;;;;;;GASG;AACH,wBAAgB,2BAA2B,IAAI,OAAO,CAGrD;AAED,2FAA2F;AAC3F,MAAM,MAAM,YAAY,GACnB,MAAM,GACN,YAAY,GACZ,mBAAmB,GACnB,oBAAoB,GACpB,kBAAkB,GAClB,SAAS,GACT,QAAQ,GACR,SAAS,GACT,QAAQ,CAAC;AAEd;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAI/F;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAM1C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMjD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAKrG"}
|
package/lib/util/environment.js
CHANGED
|
@@ -47,6 +47,20 @@ export function onProcessEvent(event, handler) {
|
|
|
47
47
|
process.on(event, handler);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Whether a Node-like `process` exposes a writable standard error. A stricter
|
|
52
|
+
* test for Node than {@link processEnv}, since a bundler can shim
|
|
53
|
+
* `process.env` into a browser build without a writable stderr. `false`
|
|
54
|
+
* where reading the stderr throws, as a shim's getter can.
|
|
55
|
+
*/
|
|
56
|
+
export function hasProcessStderr() {
|
|
57
|
+
try {
|
|
58
|
+
return typeof process !== 'undefined' && !!process.stderr && typeof process.stderr.write === 'function';
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
50
64
|
/**
|
|
51
65
|
* Write already-formatted text to the process's standard error, when a
|
|
52
66
|
* Node-like `process` exposes one. Returns `true` when the text was written and
|
|
@@ -59,7 +73,7 @@ export function onProcessEvent(event, handler) {
|
|
|
59
73
|
* process inherits when it spawns a head as a child.
|
|
60
74
|
*/
|
|
61
75
|
export function writeStderr(text) {
|
|
62
|
-
if (
|
|
76
|
+
if (!hasProcessStderr()) {
|
|
63
77
|
return false;
|
|
64
78
|
}
|
|
65
79
|
process.stderr.write(text);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"environment.js","sourceRoot":"","sources":["../../src/util/environment.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,8EAA8E;AAC9E,2EAA2E;AAC3E,gFAAgF;AAChF,4EAA4E;AAC5E,EAAE;AACF,2EAA2E;AAC3E,gFAAgF;AAChF,gFAAgF;AAChF,6EAA6E;AAE7E,wEAAwE;AACxE,MAAM,UAAU,UAAU;IACvB,OAAO,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AAClF,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,UAAU;IACvB,OAAO,OAAO,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACnE,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,2BAA2B;IACxC,MAAM,QAAQ,GAAG,OAAO,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/E,OAAO,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,QAAQ,CAAC;AACxD,CAAC;AAcD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,KAAmB,EAAE,OAAqC;IACtF,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,OAAO,CAAC,EAAE,KAAK,UAAU,EAAE,CAAC;QACtE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC9B,CAAC;AACJ,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"environment.js","sourceRoot":"","sources":["../../src/util/environment.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,8EAA8E;AAC9E,2EAA2E;AAC3E,gFAAgF;AAChF,4EAA4E;AAC5E,EAAE;AACF,2EAA2E;AAC3E,gFAAgF;AAChF,gFAAgF;AAChF,6EAA6E;AAE7E,wEAAwE;AACxE,MAAM,UAAU,UAAU;IACvB,OAAO,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AAClF,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,UAAU;IACvB,OAAO,OAAO,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACnE,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,2BAA2B;IACxC,MAAM,QAAQ,GAAG,OAAO,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/E,OAAO,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,QAAQ,CAAC;AACxD,CAAC;AAcD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,KAAmB,EAAE,OAAqC;IACtF,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,OAAO,CAAC,EAAE,KAAK,UAAU,EAAE,CAAC;QACtE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC9B,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC7B,IAAI,CAAC;QACF,OAAO,OAAO,OAAO,KAAK,WAAW,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,IAAI,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,UAAU,CAAC;IAC3G,CAAC;IAAC,MAAM,CAAC;QACN,OAAO,KAAK,CAAC;IAChB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACrC,IAAI,CAAC,gBAAgB,EAAE,EAAE,CAAC;QACvB,OAAO,KAAK,CAAC;IAChB,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,IAAI,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB;IAC/B,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,OAAO,CAAC,WAAW,KAAK,UAAU,EAAE,CAAC;QAC/E,OAAO,SAAS,CAAC;IACpB,CAAC;IACD,OAAO,OAAO,CAAC,WAAW,EAAE,CAAC;AAChC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hydranium/core",
|
|
3
|
-
"version": "1.0.0-next.
|
|
3
|
+
"version": "1.0.0-next.321",
|
|
4
4
|
"description": "Foundational runtime of the hydranium framework: AST coordination layer (services, integrity, AST extension, serialization, multi-client documents) plus the LSP textual head at the /lsp subpath. Consumed by protocol-head packages (@hydranium/data-server, @hydranium/glsp-server).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ast",
|
|
@@ -87,8 +87,8 @@
|
|
|
87
87
|
"diff": "^5.2.0"
|
|
88
88
|
},
|
|
89
89
|
"devDependencies": {
|
|
90
|
-
"@hydranium/langium": "1.0.0-next.
|
|
91
|
-
"@hydranium/protocol": "1.0.0-next.
|
|
90
|
+
"@hydranium/langium": "1.0.0-next.321",
|
|
91
|
+
"@hydranium/protocol": "1.0.0-next.321",
|
|
92
92
|
"@playwright/test": "^1.55.0",
|
|
93
93
|
"@types/diff": "^5.2.0",
|
|
94
94
|
"fake-indexeddb": "^6.2.5",
|
|
@@ -101,8 +101,8 @@
|
|
|
101
101
|
"vscode-languageserver-types": "^3.17.5"
|
|
102
102
|
},
|
|
103
103
|
"peerDependencies": {
|
|
104
|
-
"@hydranium/langium": "1.0.0-next.
|
|
105
|
-
"@hydranium/protocol": "1.0.0-next.
|
|
104
|
+
"@hydranium/langium": "1.0.0-next.321",
|
|
105
|
+
"@hydranium/protocol": "1.0.0-next.321",
|
|
106
106
|
"@playwright/test": "^1.40.0",
|
|
107
107
|
"vscode-jsonrpc": "^9.0.0",
|
|
108
108
|
"vscode-languageserver": "~10.0.1",
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 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 { Format, LEVEL_LABELS, type LogLevel, SystemClock } from '@hydranium/protocol';
|
|
11
|
+
import { formatLogArg } from './format-log-arg.js';
|
|
12
|
+
import { teeLogLine } from './logger.js';
|
|
13
|
+
import { hasProcessStderr } from '../../util/environment.js';
|
|
14
|
+
import { isConnectionGoneError } from '../../util/connection-liveness.js';
|
|
15
|
+
|
|
16
|
+
/** The `console` methods {@link installConsoleTee} wraps, with the level and label each line is written at. */
|
|
17
|
+
const CONSOLE_TEE: ReadonlyArray<readonly ['error' | 'warn' | 'info' | 'log' | 'debug', LogLevel, string]> = [
|
|
18
|
+
['error', 'error', LEVEL_LABELS.error],
|
|
19
|
+
['warn', 'warn', LEVEL_LABELS.warn],
|
|
20
|
+
['info', 'info', LEVEL_LABELS.info],
|
|
21
|
+
['log', 'info', 'Log'],
|
|
22
|
+
['debug', 'debug', LEVEL_LABELS.debug]
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
/** The wrappers this copy installed, so installing again wraps only a method that is none of them. */
|
|
26
|
+
const tees = new WeakSet<object>();
|
|
27
|
+
/**
|
|
28
|
+
* Whether a line is being written or handed on: a console call made meanwhile,
|
|
29
|
+
* by a sink or a library wrapper between two installs, is only handed on.
|
|
30
|
+
*/
|
|
31
|
+
let handling = false;
|
|
32
|
+
/**
|
|
33
|
+
* What a line is stamped with: not a logger's injected clock, whose service a
|
|
34
|
+
* console call made while another service is built could resolve into a cycle.
|
|
35
|
+
*/
|
|
36
|
+
const systemClock = new SystemClock();
|
|
37
|
+
/**
|
|
38
|
+
* How a line's arguments are rendered, Node's `util.format` where the `./node`
|
|
39
|
+
* entry set it: unlike JSON, it runs no ordinary getter and no proxy trap of an
|
|
40
|
+
* argument it inspects, the traps a services container builds a service in.
|
|
41
|
+
*/
|
|
42
|
+
let format: ((...args: unknown[]) => string) | undefined;
|
|
43
|
+
|
|
44
|
+
/** Render console lines with `render`, as `installNodeLogFileSink` does with Node's `util.format`. */
|
|
45
|
+
export function setConsoleFormat(render: (...args: unknown[]) => string): void {
|
|
46
|
+
format = render;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Write each line sent through a {@link CONSOLE_TEE} method, as Langium
|
|
51
|
+
* reports a linker or validator failure, to this copy's log file under a
|
|
52
|
+
* `console` component, then hand it to the method it replaced, even when
|
|
53
|
+
* writing the file fails. The file goes first because the method a stdio
|
|
54
|
+
* connection installs throws once the connection is closed, unless its remote
|
|
55
|
+
* console catches that, as `withHydraniumLspFeatures`'s does.
|
|
56
|
+
*
|
|
57
|
+
* Node only: in a browser no file is written, and a wrapper would only stand
|
|
58
|
+
* between devtools and the call site. Called by the first `LspLogger`, and by
|
|
59
|
+
* `startLanguageServer`, which wraps again where a logger existed before a
|
|
60
|
+
* stdio connection replaced `error`, `warn`, `log` and `debug`. A console
|
|
61
|
+
* replaced after that is not wrapped again, and each copy of this package
|
|
62
|
+
* wraps the console itself, so a file two copies share gets a line twice.
|
|
63
|
+
*/
|
|
64
|
+
export function installConsoleTee(): void {
|
|
65
|
+
if (!hasProcessStderr()) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
for (const [method, level, label] of CONSOLE_TEE) {
|
|
69
|
+
try {
|
|
70
|
+
wrap(method, level, label);
|
|
71
|
+
} catch {
|
|
72
|
+
// A host whose console is read-only, or throws when read, keeps its methods, unwritten.
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Replace `console[method]` by a tee writing its lines at `level` under `label`, unless it is already one of this copy's tees. */
|
|
78
|
+
function wrap(method: (typeof CONSOLE_TEE)[number][0], level: LogLevel, label: string): void {
|
|
79
|
+
const passOn = console[method];
|
|
80
|
+
if (tees.has(passOn)) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const tee = (...args: unknown[]): void => {
|
|
84
|
+
if (handling) {
|
|
85
|
+
Reflect.apply(passOn, console, args);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
handling = true;
|
|
89
|
+
try {
|
|
90
|
+
try {
|
|
91
|
+
const [lineLevel, lineLabel] = isDebugLine(args)
|
|
92
|
+
? (['debug', LEVEL_LABELS.debug] as const)
|
|
93
|
+
: level === 'error' && isNodeWarning(args)
|
|
94
|
+
? (['warn', LEVEL_LABELS.warn] as const)
|
|
95
|
+
: [level, label];
|
|
96
|
+
// As `AbstractLogger.formatLinePrefix` starts a line.
|
|
97
|
+
teeLogLine(
|
|
98
|
+
lineLevel,
|
|
99
|
+
() => `[${lineLabel.padEnd(5)} - ${Format.timestamp(new Date(systemClock.now()))}] [console] ${formatArgs(args)}`
|
|
100
|
+
);
|
|
101
|
+
} catch {
|
|
102
|
+
// A failing log sink must not propagate, nor keep the line from `passOn`.
|
|
103
|
+
}
|
|
104
|
+
Reflect.apply(passOn, console, args);
|
|
105
|
+
} finally {
|
|
106
|
+
handling = false;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
console[method] = tee;
|
|
110
|
+
tees.add(tee);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** `args` as the console prints them, or by {@link formatLogArg} where that throws, as for an error whose stack throws when read. */
|
|
114
|
+
function formatArgs(args: unknown[]): string {
|
|
115
|
+
if (format) {
|
|
116
|
+
try {
|
|
117
|
+
return format(...args);
|
|
118
|
+
} catch {
|
|
119
|
+
// Described below instead.
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return args.map(formatLogArg).join(' ');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* How Node's default warning handler starts what it prints through
|
|
127
|
+
* `console.error`: the runtime's release name and pid, the warning's code if
|
|
128
|
+
* it has one, and its type, as `(node:42) [DEP0005] DeprecationWarning: `.
|
|
129
|
+
*/
|
|
130
|
+
const NODE_WARNING = /^\([\w-]+:\d+\) (\[\w+\] )?\w*Warning: /;
|
|
131
|
+
|
|
132
|
+
/** Whether `args` is a Node process warning, a deprecation say, which is written at `warn`. */
|
|
133
|
+
function isNodeWarning(args: readonly unknown[]): boolean {
|
|
134
|
+
return args.length === 1 && typeof args[0] === 'string' && NODE_WARNING.test(args[0]);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** What Langium's `initialized` handler logs when a service's start rejects. */
|
|
138
|
+
const LANGIUM_START_FAILURES = new Set<unknown>([
|
|
139
|
+
'Error in ConfigurationProvider initialization:',
|
|
140
|
+
'Error in WorkspaceManager initialization:'
|
|
141
|
+
]);
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Whether `args` reports routine teardown, which is written at `debug`:
|
|
145
|
+
* Langium's failed configuration or workspace start whose connection has gone.
|
|
146
|
+
* `vscode-languageserver`'s `Sending log message failed` is not, since it
|
|
147
|
+
* carries no reason; `withHydraniumLspFeatures` keeps teardown from printing it.
|
|
148
|
+
*/
|
|
149
|
+
function isDebugLine(args: readonly unknown[]): boolean {
|
|
150
|
+
return args.length === 2 && LANGIUM_START_FAILURES.has(args[0]) && isConnectionGoneError(args[1]);
|
|
151
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 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 { describeErrorWithStack } from '@hydranium/protocol';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Render a log argument for the single string a line carries, to the LSP
|
|
14
|
+
* `window/logMessage` channel and the log file: a string as it is, an error by
|
|
15
|
+
* its stack and causes, another object as JSON, and anything else, what JSON
|
|
16
|
+
* renders as nothing or cannot render included, by its description. Never
|
|
17
|
+
* throws, since a log call that throws loses the line and interrupts its
|
|
18
|
+
* caller. A `%s`-style specifier is not substituted.
|
|
19
|
+
*/
|
|
20
|
+
export function formatLogArg(value: unknown): string {
|
|
21
|
+
if (typeof value === 'string') {
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
if (typeof value === 'object' && value !== null) {
|
|
25
|
+
try {
|
|
26
|
+
// `undefined` where `toJSON` returns nothing.
|
|
27
|
+
const json: string | undefined = value instanceof Error ? undefined : JSON.stringify(value);
|
|
28
|
+
if (json !== undefined) {
|
|
29
|
+
return json;
|
|
30
|
+
}
|
|
31
|
+
} catch {
|
|
32
|
+
// Cyclic, not serialisable, or a revoked proxy; described below instead.
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return describeErrorWithStack(value);
|
|
36
|
+
}
|
|
@@ -39,7 +39,7 @@ export function setLogFileSink(sink: LogFileSink | undefined): void {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
|
-
* File-tee state for `LspLogger`'s emit. When
|
|
42
|
+
* File-tee state for `LspLogger`'s emit and the console tee. When
|
|
43
43
|
* non-empty, every formatted log line is appended to the path in addition to
|
|
44
44
|
* its LSP `window/logMessage` / console destination (see {@link teeLogLine}).
|
|
45
45
|
* Diagnostic surface only — once the LSP connection binds, server logs route
|
|
@@ -201,19 +201,26 @@ export function getLogFilePath(): string | undefined {
|
|
|
201
201
|
* Fan a single already-formatted log line out to the file-tee target, if one is
|
|
202
202
|
* configured and `level` is within the file's threshold
|
|
203
203
|
* (`Logger.getFileLevel() ?? Logger.getLevel()`). Called by `LspLogger`'s emit in
|
|
204
|
-
* addition to the LSP/console sink
|
|
204
|
+
* addition to the LSP/console sink, and by the console tee before the console
|
|
205
|
+
* method it wraps — log sinks fan out, they don't replace.
|
|
205
206
|
* While the target is still pending (unresolved `{placeholder}`), the line is
|
|
206
|
-
* buffered instead of dropped.
|
|
207
|
+
* buffered instead of dropped. Where no sink is installed, as in a process
|
|
208
|
+
* that never loaded `@hydranium/core/node`, nothing is built or buffered,
|
|
209
|
+
* since nothing will write it. A caller whose line is costly to build passes
|
|
210
|
+
* a function, called only when the file takes the line.
|
|
207
211
|
*/
|
|
208
|
-
export function teeLogLine(level: LogLevel, formatted: string): void {
|
|
212
|
+
export function teeLogLine(level: LogLevel, formatted: string | (() => string)): void {
|
|
209
213
|
if (!logFileTemplate || LEVEL_ORDER[level] > LEVEL_ORDER[Logger.getFileLevel() ?? Logger.getLevel()]) {
|
|
210
214
|
return;
|
|
211
215
|
}
|
|
212
|
-
|
|
216
|
+
if (!fileSink) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const line = `${typeof formatted === 'string' ? formatted : formatted()}\n`;
|
|
213
220
|
if (resolvedLogFilePath) {
|
|
214
221
|
// The sink swallows its own errors; recursing into the logger here would
|
|
215
|
-
// re-enter emit and loop. The
|
|
216
|
-
fileSink
|
|
222
|
+
// re-enter emit and loop. The caller delivers the line to its other sink.
|
|
223
|
+
fileSink.append(resolvedLogFilePath, line);
|
|
217
224
|
} else {
|
|
218
225
|
pendingLogLines.push(line);
|
|
219
226
|
}
|
|
@@ -19,12 +19,13 @@ import {
|
|
|
19
19
|
type LogThreshold,
|
|
20
20
|
ObservableValue,
|
|
21
21
|
type MaybeObservableValue,
|
|
22
|
-
describeErrorWithStack,
|
|
23
22
|
parseLogLevel
|
|
24
23
|
} from '@hydranium/protocol';
|
|
25
24
|
import { type URI } from '@hydranium/langium';
|
|
26
25
|
import type { Connection } from 'vscode-languageserver';
|
|
27
26
|
import { type ServerSharedServices } from '../module.js';
|
|
27
|
+
import { installConsoleTee } from './console-tee.js';
|
|
28
|
+
import { formatLogArg } from './format-log-arg.js';
|
|
28
29
|
import { setLogFilePath, teeLogLine } from './logger.js';
|
|
29
30
|
import { processEnv, writeStderr } from '../../util/environment.js';
|
|
30
31
|
|
|
@@ -35,6 +36,8 @@ import { processEnv, writeStderr } from '../../util/environment.js';
|
|
|
35
36
|
* live setting.
|
|
36
37
|
*/
|
|
37
38
|
let envBaselineApplied = false;
|
|
39
|
+
/** Whether the first logger has run the console tee's install, which does nothing in a browser. */
|
|
40
|
+
let consoleTeeInstalled = false;
|
|
38
41
|
|
|
39
42
|
/**
|
|
40
43
|
* Options accepted by {@link LspLogger}. Carries the logger's own
|
|
@@ -113,6 +116,13 @@ export class LspLogger extends AbstractLogger implements Logger {
|
|
|
113
116
|
}
|
|
114
117
|
}
|
|
115
118
|
}
|
|
119
|
+
// Once, so a server without an LSP head writes console lines to the file
|
|
120
|
+
// too; `startLanguageServer` wraps again where a stdio connection created
|
|
121
|
+
// after this logger replaced them.
|
|
122
|
+
if (!consoleTeeInstalled) {
|
|
123
|
+
consoleTeeInstalled = true;
|
|
124
|
+
installConsoleTee();
|
|
125
|
+
}
|
|
116
126
|
// Apply the configured threshold from the root logger only — `derive`
|
|
117
127
|
// passes just `component`, so children never carry `logThreshold`. The
|
|
118
128
|
// setting overrides the env baseline above and re-applies on every change;
|
|
@@ -291,32 +301,3 @@ const LSP_CONSOLE: Record<LogLevel, (connection: Connection) => (message: string
|
|
|
291
301
|
debug: c => c.console.debug.bind(c.console),
|
|
292
302
|
trace: c => c.console.log.bind(c.console)
|
|
293
303
|
};
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Render a logger-argument value into the single combined string the LSP
|
|
297
|
-
* `window/logMessage` channel takes. Mirrors how raw `console.*` would
|
|
298
|
-
* format multiple args, but with explicit handling for Errors (stack) and
|
|
299
|
-
* objects (JSON).
|
|
300
|
-
*/
|
|
301
|
-
function formatLogArg(value: unknown): string {
|
|
302
|
-
if (value === undefined) {
|
|
303
|
-
return 'undefined';
|
|
304
|
-
}
|
|
305
|
-
if (value === null) {
|
|
306
|
-
return 'null';
|
|
307
|
-
}
|
|
308
|
-
if (typeof value === 'string') {
|
|
309
|
-
return value;
|
|
310
|
-
}
|
|
311
|
-
if (value instanceof Error) {
|
|
312
|
-
return describeErrorWithStack(value);
|
|
313
|
-
}
|
|
314
|
-
if (typeof value === 'object') {
|
|
315
|
-
try {
|
|
316
|
-
return JSON.stringify(value);
|
|
317
|
-
} catch {
|
|
318
|
-
return String(value);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
return String(value);
|
|
322
|
-
}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from '@hydranium/langium/lsp';
|
|
15
15
|
import { HydraniumDocumentUpdateHandler } from './hydranium-document-update-handler.js';
|
|
16
16
|
import { guardDiagnosticsConnection } from './diagnostics-connection.js';
|
|
17
|
+
import { installConsoleTee } from '../langium/diagnostics/console-tee.js';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Fail the LSP head's start when its shared module was never composed.
|
|
@@ -58,11 +59,14 @@ export function assertLspHeadComposed(services: LangiumSharedServices): void {
|
|
|
58
59
|
|
|
59
60
|
/**
|
|
60
61
|
* The framework's LSP server entry point: {@link assertLspHeadComposed}, then
|
|
61
|
-
* {@link guardDiagnosticsConnection} on the tree's connection, then
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
62
|
+
* {@link guardDiagnosticsConnection} on the tree's connection, then the
|
|
63
|
+
* console's wrapping for the log file, then Langium's `startLanguageServer`.
|
|
64
|
+
* Signature-compatible with Langium's, so it is a drop-in for adopters
|
|
65
|
+
* importing from `@hydranium/core/lsp`. Calling Langium's directly skips the
|
|
66
|
+
* composition check, the guard without which a publish racing teardown fails
|
|
67
|
+
* the build that validated the document, and the wrapping, which a stdio
|
|
68
|
+
* connection discards where a logger was built before it (see
|
|
69
|
+
* `installConsoleTee`).
|
|
66
70
|
*/
|
|
67
71
|
export function startLanguageServer(services: LangiumSharedServices, serviceRequirements: Partial<ServiceRequirements> = {}): void {
|
|
68
72
|
assertLspHeadComposed(services);
|
|
@@ -70,5 +74,6 @@ export function startLanguageServer(services: LangiumSharedServices, serviceRequ
|
|
|
70
74
|
if (connection) {
|
|
71
75
|
guardDiagnosticsConnection(connection);
|
|
72
76
|
}
|
|
77
|
+
installConsoleTee();
|
|
73
78
|
startLangiumLanguageServer(services, serviceRequirements);
|
|
74
79
|
}
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
import { appendFileSync, mkdirSync } from 'node:fs';
|
|
11
11
|
import { dirname } from 'node:path';
|
|
12
|
+
import { format } from 'node:util';
|
|
13
|
+
import { setConsoleFormat } from '../langium/diagnostics/console-tee.js';
|
|
12
14
|
import { type LogFileSink, setLogFileSink } from '../langium/diagnostics/logger.js';
|
|
13
15
|
|
|
14
16
|
/**
|
|
@@ -35,9 +37,11 @@ export const nodeLogFileSink: LogFileSink = {
|
|
|
35
37
|
|
|
36
38
|
/**
|
|
37
39
|
* Install the Node file-tee sink so a configured `HYDRANIUM_LOG_FILE` target
|
|
38
|
-
* is honoured
|
|
39
|
-
*
|
|
40
|
+
* is honoured, and render console lines in it with `util.format`, as the
|
|
41
|
+
* console prints them. Idempotent. Called once at `@hydranium/core/node` load
|
|
42
|
+
* so any Node host that imports the entry gets the file-tee for free.
|
|
40
43
|
*/
|
|
41
44
|
export function installNodeLogFileSink(): void {
|
|
42
45
|
setLogFileSink(nodeLogFileSink);
|
|
46
|
+
setConsoleFormat(format);
|
|
43
47
|
}
|
|
@@ -43,7 +43,16 @@ const CONNECTION_GONE_PATTERNS = [
|
|
|
43
43
|
const DESTROYED_STREAM_CODE = 'ERR_STREAM_DESTROYED';
|
|
44
44
|
|
|
45
45
|
function isDestroyedStreamError(err: unknown): boolean {
|
|
46
|
-
return err
|
|
46
|
+
return errorCode(err) === DESTROYED_STREAM_CODE;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** `err.code` where `err` is an `Error`, or `undefined` where reading it throws, as a getter or a revoked proxy can. */
|
|
50
|
+
function errorCode(err: unknown): unknown {
|
|
51
|
+
try {
|
|
52
|
+
return err instanceof Error && 'code' in err ? err.code : undefined;
|
|
53
|
+
} catch {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
47
56
|
}
|
|
48
57
|
|
|
49
58
|
/**
|
|
@@ -58,6 +67,9 @@ function isDestroyedStreamError(err: unknown): boolean {
|
|
|
58
67
|
* into errors that look like they need investigation. A `true` for a real
|
|
59
68
|
* `applyEdit` failure (the client refused, the request malformed) hides it.
|
|
60
69
|
*
|
|
70
|
+
* Never throws, nor does {@link isClientGoneError}: a caller classifying a
|
|
71
|
+
* failure would otherwise lose it to a getter or proxy trap of the error.
|
|
72
|
+
*
|
|
61
73
|
* Callers that only need to *survive* a dead connection rather than classify it
|
|
62
74
|
* should just catch — see `LspLogger.emit`, whose fallback path does exactly
|
|
63
75
|
* that, since a logger has nowhere to report a logging failure anyway.
|
|
@@ -91,7 +103,8 @@ export function isClientGoneError(err: unknown): boolean {
|
|
|
91
103
|
if (isConnectionGoneError(err)) {
|
|
92
104
|
return true;
|
|
93
105
|
}
|
|
94
|
-
|
|
106
|
+
const code = errorCode(err);
|
|
107
|
+
if (typeof code === 'string' && SOCKET_GONE_CODES.has(code)) {
|
|
95
108
|
return true;
|
|
96
109
|
}
|
|
97
110
|
const message = describeError(err);
|
package/src/util/environment.ts
CHANGED
|
@@ -65,6 +65,20 @@ export function onProcessEvent(event: ProcessEvent, handler: (...args: unknown[]
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Whether a Node-like `process` exposes a writable standard error. A stricter
|
|
70
|
+
* test for Node than {@link processEnv}, since a bundler can shim
|
|
71
|
+
* `process.env` into a browser build without a writable stderr. `false`
|
|
72
|
+
* where reading the stderr throws, as a shim's getter can.
|
|
73
|
+
*/
|
|
74
|
+
export function hasProcessStderr(): boolean {
|
|
75
|
+
try {
|
|
76
|
+
return typeof process !== 'undefined' && !!process.stderr && typeof process.stderr.write === 'function';
|
|
77
|
+
} catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
68
82
|
/**
|
|
69
83
|
* Write already-formatted text to the process's standard error, when a
|
|
70
84
|
* Node-like `process` exposes one. Returns `true` when the text was written and
|
|
@@ -77,7 +91,7 @@ export function onProcessEvent(event: ProcessEvent, handler: (...args: unknown[]
|
|
|
77
91
|
* process inherits when it spawns a head as a child.
|
|
78
92
|
*/
|
|
79
93
|
export function writeStderr(text: string): boolean {
|
|
80
|
-
if (
|
|
94
|
+
if (!hasProcessStderr()) {
|
|
81
95
|
return false;
|
|
82
96
|
}
|
|
83
97
|
process.stderr.write(text);
|