@hydranium/core 1.0.0-next.320 → 1.0.0-next.322

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/lib/langium/diagnostics/console-tee.d.ts +27 -0
  2. package/lib/langium/diagnostics/console-tee.d.ts.map +1 -0
  3. package/lib/langium/diagnostics/console-tee.js +142 -0
  4. package/lib/langium/diagnostics/console-tee.js.map +1 -0
  5. package/lib/langium/diagnostics/format-log-arg.d.ts +18 -0
  6. package/lib/langium/diagnostics/format-log-arg.d.ts.map +1 -0
  7. package/lib/langium/diagnostics/format-log-arg.js +36 -0
  8. package/lib/langium/diagnostics/format-log-arg.js.map +1 -0
  9. package/lib/langium/diagnostics/logger.d.ts +7 -3
  10. package/lib/langium/diagnostics/logger.d.ts.map +1 -1
  11. package/lib/langium/diagnostics/logger.js +13 -6
  12. package/lib/langium/diagnostics/logger.js.map +1 -1
  13. package/lib/langium/diagnostics/lsp-logger.d.ts.map +1 -1
  14. package/lib/langium/diagnostics/lsp-logger.js +12 -30
  15. package/lib/langium/diagnostics/lsp-logger.js.map +1 -1
  16. package/lib/lsp/start-language-server.d.ts +8 -5
  17. package/lib/lsp/start-language-server.d.ts.map +1 -1
  18. package/lib/lsp/start-language-server.js +10 -5
  19. package/lib/lsp/start-language-server.js.map +1 -1
  20. package/lib/node/log-file-sink.d.ts +3 -2
  21. package/lib/node/log-file-sink.d.ts.map +1 -1
  22. package/lib/node/log-file-sink.js +6 -2
  23. package/lib/node/log-file-sink.js.map +1 -1
  24. package/lib/util/connection-liveness.d.ts +3 -0
  25. package/lib/util/connection-liveness.d.ts.map +1 -1
  26. package/lib/util/connection-liveness.js +15 -2
  27. package/lib/util/connection-liveness.js.map +1 -1
  28. package/lib/util/environment.d.ts +7 -0
  29. package/lib/util/environment.d.ts.map +1 -1
  30. package/lib/util/environment.js +15 -1
  31. package/lib/util/environment.js.map +1 -1
  32. package/package.json +5 -5
  33. package/src/langium/diagnostics/console-tee.ts +151 -0
  34. package/src/langium/diagnostics/format-log-arg.ts +36 -0
  35. package/src/langium/diagnostics/logger.ts +14 -7
  36. package/src/langium/diagnostics/lsp-logger.ts +11 -30
  37. package/src/lsp/start-language-server.ts +10 -5
  38. package/src/node/log-file-sink.ts +6 -2
  39. package/src/util/connection-liveness.ts +15 -2
  40. 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 — log sinks fan out, they don't replace.
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;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAYnE;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"}
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 — log sinks fan out, they don't replace.
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
- const line = `${formatted}\n`;
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 LSP/console sink already delivered the line.
181
- fileSink?.append(resolvedLogFilePath, line);
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":"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;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,KAAe,EAAE,SAAiB;IAC1D,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,MAAM,IAAI,GAAG,GAAG,SAAS,IAAI,CAAC;IAC9B,IAAI,mBAAmB,EAAE,CAAC;QACvB,yEAAyE;QACzE,2EAA2E;QAC3E,QAAQ,EAAE,MAAM,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAC;IAC/C,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
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../../src/langium/diagnostics/logger.ts"],"names":[],"mappings":"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"}
@@ -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,EAG3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAYzD;;;;;;;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,EAiDhC;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"}
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, describeErrorWithStack, parseLogLevel } from '@hydranium/protocol';
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":"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"}
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 Langium's
38
- * `startLanguageServer`. Signature-compatible with Langium's, so it is a
39
- * drop-in for adopters importing from `@hydranium/core/lsp`. Calling Langium's
40
- * directly skips both: the composition check, and the guard without which a
41
- * publish racing teardown fails the build that validated the document.
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;AAIhC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAe3E;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,qBAAqB,EAAE,mBAAmB,GAAE,OAAO,CAAC,mBAAmB,CAAM,GAAG,IAAI,CAOjI"}
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 Langium's
53
- * `startLanguageServer`. Signature-compatible with Langium's, so it is a
54
- * drop-in for adopters importing from `@hydranium/core/lsp`. Calling Langium's
55
- * directly skips both: the composition check, and the guard without which a
56
- * publish racing teardown fails the build that validated the document.
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;AAEzE;;;;;;;;;;;;;;;;;;;;;;;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;;;;;;;GAOG;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,0BAA0B,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;AAC7D,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. Idempotent. Called once at `@hydranium/core/node` load so any
19
- * Node host that imports the entry gets the file-tee for free.
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;AAIlF,OAAO,EAAE,KAAK,WAAW,EAAkB,MAAM,kCAAkC,CAAC;AAEpF;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,WAe7B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
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. Idempotent. Called once at `@hydranium/core/node` load so any
38
- * Node host that imports the entry gets the file-tee for free.
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;;;;GAIG;AACH,MAAM,UAAU,sBAAsB;IACnC,cAAc,CAAC,eAAe,CAAC,CAAC;AACnC,CAAC"}
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;AAyClF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAO3D;AAOD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CASvD"}
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 instanceof Error && 'code' in err && err.code === DESTROYED_STREAM_CODE;
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
- if (err instanceof Error && 'code' in err && typeof err.code === 'string' && SOCKET_GONE_CODES.has(err.code)) {
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,IAAI,GAAG,CAAC,IAAI,KAAK,qBAAqB,CAAC;AACtF,CAAC;AAED;;;;;;;;;;;;;;;GAeG;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,IAAI,GAAG,YAAY,KAAK,IAAI,MAAM,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5G,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"}
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"}
@@ -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 (typeof process === 'undefined' || !process.stderr || typeof process.stderr.write !== 'function') {
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;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACrC,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;QACnG,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"}
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.320",
3
+ "version": "1.0.0-next.322",
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.320",
91
- "@hydranium/protocol": "1.0.0-next.320",
90
+ "@hydranium/langium": "1.0.0-next.322",
91
+ "@hydranium/protocol": "1.0.0-next.322",
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.320",
105
- "@hydranium/protocol": "1.0.0-next.320",
104
+ "@hydranium/langium": "1.0.0-next.322",
105
+ "@hydranium/protocol": "1.0.0-next.322",
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 — log sinks fan out, they don't replace.
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
- const line = `${formatted}\n`;
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 LSP/console sink already delivered the line.
216
- fileSink?.append(resolvedLogFilePath, line);
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 Langium's
62
- * `startLanguageServer`. Signature-compatible with Langium's, so it is a
63
- * drop-in for adopters importing from `@hydranium/core/lsp`. Calling Langium's
64
- * directly skips both: the composition check, and the guard without which a
65
- * publish racing teardown fails the build that validated the document.
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. Idempotent. Called once at `@hydranium/core/node` load so any
39
- * Node host that imports the entry gets the file-tee for free.
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 instanceof Error && 'code' in err && err.code === DESTROYED_STREAM_CODE;
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
- if (err instanceof Error && 'code' in err && typeof err.code === 'string' && SOCKET_GONE_CODES.has(err.code)) {
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);
@@ -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 (typeof process === 'undefined' || !process.stderr || typeof process.stderr.write !== 'function') {
94
+ if (!hasProcessStderr()) {
81
95
  return false;
82
96
  }
83
97
  process.stderr.write(text);