@hydranium/core 1.0.0-next.267 → 1.0.0-next.269
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/documents/ast-document-manager.d.ts.map +1 -1
- package/lib/documents/ast-document-manager.js +4 -3
- package/lib/documents/ast-document-manager.js.map +1 -1
- package/lib/documents/document-release-handler.d.ts +2 -2
- package/lib/documents/document-release-handler.d.ts.map +1 -1
- package/lib/documents/document-release-handler.js +14 -19
- package/lib/documents/document-release-handler.js.map +1 -1
- package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
- package/lib/documents/hydranium-text-documents.js +7 -10
- package/lib/documents/hydranium-text-documents.js.map +1 -1
- package/lib/documents/language-client-shadow.d.ts.map +1 -1
- package/lib/documents/language-client-shadow.js +2 -1
- package/lib/documents/language-client-shadow.js.map +1 -1
- package/lib/documents/version-sync-service.d.ts +18 -14
- package/lib/documents/version-sync-service.d.ts.map +1 -1
- package/lib/documents/version-sync-service.js +15 -13
- package/lib/documents/version-sync-service.js.map +1 -1
- package/lib/langium/diagnostics/lsp-logger.d.ts.map +1 -1
- package/lib/langium/diagnostics/lsp-logger.js +2 -2
- package/lib/langium/diagnostics/lsp-logger.js.map +1 -1
- package/lib/langium/document-builder/document-builder.d.ts +2 -2
- package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
- package/lib/langium/document-builder/document-builder.js +23 -10
- package/lib/langium/document-builder/document-builder.js.map +1 -1
- package/lib/langium/integrity/integrity-service.d.ts.map +1 -1
- package/lib/langium/integrity/integrity-service.js +5 -1
- package/lib/langium/integrity/integrity-service.js.map +1 -1
- package/lib/langium/model-service/client-session.d.ts.map +1 -1
- package/lib/langium/model-service/client-session.js +20 -3
- package/lib/langium/model-service/client-session.js.map +1 -1
- package/lib/langium/model-service/model-service.d.ts +4 -3
- package/lib/langium/model-service/model-service.d.ts.map +1 -1
- package/lib/langium/model-service/model-service.js +49 -19
- package/lib/langium/model-service/model-service.js.map +1 -1
- package/lib/langium/project/abstract-project-manager.d.ts.map +1 -1
- package/lib/langium/project/abstract-project-manager.js +4 -4
- package/lib/langium/project/abstract-project-manager.js.map +1 -1
- package/lib/langium/scope/hydranium-scope-computation.d.ts +6 -0
- package/lib/langium/scope/hydranium-scope-computation.d.ts.map +1 -1
- package/lib/langium/scope/hydranium-scope-computation.js +6 -10
- package/lib/langium/scope/hydranium-scope-computation.js.map +1 -1
- package/lib/langium/validation/document-validator.d.ts.map +1 -1
- package/lib/langium/validation/document-validator.js +5 -3
- package/lib/langium/validation/document-validator.js.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-lock.d.ts +1 -1
- package/lib/langium/workspace/hydranium-workspace-lock.js +1 -1
- package/lib/langium/workspace/hydranium-workspace-lock.js.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-manager.js +4 -5
- package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
- package/lib/langium/workspace/persistent-file-system-provider.d.ts.map +1 -1
- package/lib/langium/workspace/persistent-file-system-provider.js +2 -1
- package/lib/langium/workspace/persistent-file-system-provider.js.map +1 -1
- package/lib/launcher/integrated-server.d.ts +3 -1
- package/lib/launcher/integrated-server.d.ts.map +1 -1
- package/lib/lsp/hydranium-document-update-handler.d.ts.map +1 -1
- package/lib/lsp/hydranium-document-update-handler.js +5 -5
- package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
- package/lib/node/event-loop-monitor.d.ts.map +1 -1
- package/lib/node/event-loop-monitor.js +2 -1
- package/lib/node/event-loop-monitor.js.map +1 -1
- package/lib/node/log-preamble-node.d.ts +5 -3
- package/lib/node/log-preamble-node.d.ts.map +1 -1
- package/lib/node/log-preamble-node.js +13 -10
- package/lib/node/log-preamble-node.js.map +1 -1
- package/lib/node/memory-monitor.d.ts +2 -2
- package/lib/node/memory-monitor.d.ts.map +1 -1
- package/lib/node/memory-monitor.js +3 -5
- package/lib/node/memory-monitor.js.map +1 -1
- package/lib/node/server-diagnostics.js +1 -1
- package/lib/node/server-diagnostics.js.map +1 -1
- package/lib/node/socket-launcher.d.ts.map +1 -1
- package/lib/node/socket-launcher.js +4 -4
- package/lib/node/socket-launcher.js.map +1 -1
- package/lib/node/stdio-launcher.d.ts.map +1 -1
- package/lib/node/stdio-launcher.js +12 -3
- package/lib/node/stdio-launcher.js.map +1 -1
- package/lib/testing/playwright/server-log-capture.d.ts +7 -0
- package/lib/testing/playwright/server-log-capture.d.ts.map +1 -1
- package/lib/testing/playwright/server-log-capture.js +12 -0
- package/lib/testing/playwright/server-log-capture.js.map +1 -1
- package/lib/testing/playwright/server-log-fixture.d.ts +16 -0
- package/lib/testing/playwright/server-log-fixture.d.ts.map +1 -1
- package/lib/testing/playwright/server-log-fixture.js +25 -6
- package/lib/testing/playwright/server-log-fixture.js.map +1 -1
- package/lib/testing/playwright/server-log-region.d.ts +15 -0
- package/lib/testing/playwright/server-log-region.d.ts.map +1 -0
- package/lib/testing/playwright/server-log-region.js +30 -0
- package/lib/testing/playwright/server-log-region.js.map +1 -0
- package/lib/util/connection-liveness.d.ts.map +1 -1
- package/lib/util/connection-liveness.js +3 -2
- package/lib/util/connection-liveness.js.map +1 -1
- package/package.json +5 -5
- package/src/documents/ast-document-manager.ts +7 -3
- package/src/documents/document-release-handler.ts +15 -21
- package/src/documents/hydranium-text-documents.ts +13 -12
- package/src/documents/language-client-shadow.ts +2 -1
- package/src/documents/version-sync-service.ts +32 -24
- package/src/langium/diagnostics/lsp-logger.ts +2 -1
- package/src/langium/document-builder/document-builder.ts +30 -11
- package/src/langium/integrity/integrity-service.ts +4 -1
- package/src/langium/model-service/client-session.ts +20 -3
- package/src/langium/model-service/model-service.ts +55 -24
- package/src/langium/project/abstract-project-manager.ts +4 -4
- package/src/langium/scope/hydranium-scope-computation.ts +12 -12
- package/src/langium/validation/document-validator.ts +5 -3
- package/src/langium/workspace/hydranium-workspace-lock.ts +2 -2
- package/src/langium/workspace/hydranium-workspace-manager.ts +4 -5
- package/src/langium/workspace/persistent-file-system-provider.ts +2 -1
- package/src/launcher/integrated-server.ts +3 -1
- package/src/lsp/hydranium-document-update-handler.ts +15 -7
- package/src/node/event-loop-monitor.ts +2 -1
- package/src/node/log-preamble-node.ts +20 -10
- package/src/node/memory-monitor.ts +4 -5
- package/src/node/server-diagnostics.ts +1 -1
- package/src/node/socket-launcher.ts +4 -4
- package/src/node/stdio-launcher.ts +11 -3
- package/src/testing/playwright/server-log-capture.ts +14 -0
- package/src/testing/playwright/server-log-fixture.ts +46 -6
- package/src/testing/playwright/server-log-region.ts +32 -0
- package/src/util/connection-liveness.ts +4 -2
|
@@ -16,6 +16,21 @@ export interface ServerLogFixtures {
|
|
|
16
16
|
* `attachServerLog` in `beforeEach` / `afterEach` instead.
|
|
17
17
|
*/
|
|
18
18
|
serverLogWorkspace: string;
|
|
19
|
+
/**
|
|
20
|
+
* Fail a test whose part of the server log holds a `Warn` or `Error` line
|
|
21
|
+
* that {@link serverLogAllowlist} does not match. Off by default; reads
|
|
22
|
+
* nothing when capture is off.
|
|
23
|
+
*/
|
|
24
|
+
serverLogClean: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* The `Warn` and `Error` lines {@link serverLogClean} lets through: each key
|
|
27
|
+
* is why the line is expected, and its pattern is matched against the whole
|
|
28
|
+
* line, label and timestamp included. A pattern takes neither the `g` nor
|
|
29
|
+
* the `y` flag, which make `test` resume from its last match. An object
|
|
30
|
+
* rather than a list, because Playwright reads an array whose second entry
|
|
31
|
+
* is an object as a fixture's `[value, options]`.
|
|
32
|
+
*/
|
|
33
|
+
serverLogAllowlist: Readonly<Record<string, RegExp>>;
|
|
19
34
|
}
|
|
20
35
|
/** Full fixture set including the internal side-effect-only auto fixture. */
|
|
21
36
|
interface ServerLogTestFixtures extends ServerLogFixtures {
|
|
@@ -28,6 +43,7 @@ interface ServerLogTestFixtures extends ServerLogFixtures {
|
|
|
28
43
|
* (teardown) — i.e. the `beforeEach` + `afterEach` pair fused into one auto
|
|
29
44
|
* fixture. Compose onto any test base, or use the pre-extended {@link test}.
|
|
30
45
|
* Override `serverLogWorkspace` to wire the workspace; otherwise it's inert.
|
|
46
|
+
* Set `serverLogClean` to also fail a test on an unexpected `Warn` or `Error`.
|
|
31
47
|
*/
|
|
32
48
|
export declare const serverLogFixtures: Fixtures<ServerLogTestFixtures, object, PlaywrightTestArgs & PlaywrightTestOptions>;
|
|
33
49
|
/** Pre-extended test with {@link serverLogFixtures} applied, for adopters on the plain base. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-log-fixture.d.ts","sourceRoot":"","sources":["../../../src/testing/playwright/server-log-fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAKlF,OAAO,EAAgB,KAAK,QAAQ,EAAE,KAAK,kBAAkB,EAAE,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"server-log-fixture.d.ts","sourceRoot":"","sources":["../../../src/testing/playwright/server-log-fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAKlF,OAAO,EAAgB,KAAK,QAAQ,EAAE,KAAK,kBAAkB,EAAE,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAUpH,MAAM,WAAW,iBAAiB;IAC/B;;;;;;OAMG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,cAAc,EAAE,OAAO,CAAC;IACxB;;;;;;;OAOG;IACH,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACvD;AAED,6EAA6E;AAC7E,UAAU,qBAAsB,SAAQ,iBAAiB;IACtD,sFAAsF;IACtF,gBAAgB,EAAE,IAAI,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,qBAAqB,EAAE,MAAM,EAAE,kBAAkB,GAAG,qBAAqB,CA+BjH,CAAC;AAEF,gGAAgG;AAChG,eAAO,MAAM,IAAI,+MAAwD,CAAC"}
|
|
@@ -10,24 +10,43 @@
|
|
|
10
10
|
// The plain functions in `server-log-capture.ts` stay playwright-free; only
|
|
11
11
|
// adopters that use this fixture need `@playwright/test` installed.
|
|
12
12
|
import { test as base } from '@playwright/test';
|
|
13
|
-
import { attachServerLog, markServerLog } from './server-log-capture.js';
|
|
13
|
+
import { attachServerLog, DEFAULT_SERVER_LOG_ATTACH_ENV, findUnexpectedServerLogLines, markServerLog, resolveServerLogPath } from './server-log-capture.js';
|
|
14
|
+
import { readServerLogRegion } from './server-log-region.js';
|
|
14
15
|
/**
|
|
15
16
|
* Composable fixtures for per-test server-log capture: writes the start marker
|
|
16
17
|
* before each test (setup) and attaches the log on failure after each test
|
|
17
18
|
* (teardown) — i.e. the `beforeEach` + `afterEach` pair fused into one auto
|
|
18
19
|
* fixture. Compose onto any test base, or use the pre-extended {@link test}.
|
|
19
20
|
* Override `serverLogWorkspace` to wire the workspace; otherwise it's inert.
|
|
21
|
+
* Set `serverLogClean` to also fail a test on an unexpected `Warn` or `Error`.
|
|
20
22
|
*/
|
|
21
23
|
export const serverLogFixtures = {
|
|
22
24
|
serverLogWorkspace: ['', { option: true }],
|
|
25
|
+
serverLogClean: [false, { option: true }],
|
|
26
|
+
serverLogAllowlist: [{}, { option: true }],
|
|
23
27
|
serverLogCapture: [
|
|
24
|
-
async ({ serverLogWorkspace }, use) => {
|
|
25
|
-
if (serverLogWorkspace) {
|
|
26
|
-
|
|
28
|
+
async ({ serverLogWorkspace, serverLogClean, serverLogAllowlist }, use) => {
|
|
29
|
+
if (!serverLogWorkspace) {
|
|
30
|
+
await use(undefined);
|
|
31
|
+
return;
|
|
27
32
|
}
|
|
33
|
+
markServerLog(base.info(), serverLogWorkspace);
|
|
28
34
|
await use(undefined);
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
const file = resolveServerLogPath(serverLogWorkspace);
|
|
36
|
+
const region = file ? readServerLogRegion(file) : '';
|
|
37
|
+
let problem;
|
|
38
|
+
if (serverLogClean) {
|
|
39
|
+
const unexpected = findUnexpectedServerLogLines(region, serverLogAllowlist);
|
|
40
|
+
if (unexpected.length > 0) {
|
|
41
|
+
problem = `The server logged ${unexpected.length} unexpected Warn or Error line(s):\n${unexpected.join('\n')}`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// The failure-only policy reads the test's status, which failing here
|
|
45
|
+
// does not change; a run set to attach nothing still attaches nothing.
|
|
46
|
+
const attachOn = process.env[DEFAULT_SERVER_LOG_ATTACH_ENV] === 'never' ? 'never' : 'always';
|
|
47
|
+
await attachServerLog(base.info(), serverLogWorkspace, problem ? { attachOn } : {});
|
|
48
|
+
if (problem) {
|
|
49
|
+
throw new Error(problem);
|
|
31
50
|
}
|
|
32
51
|
},
|
|
33
52
|
{ auto: true }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-log-fixture.js","sourceRoot":"","sources":["../../../src/testing/playwright/server-log-fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,kFAAkF;AAClF,4EAA4E;AAC5E,oEAAoE;AACpE,OAAO,EAAE,IAAI,IAAI,IAAI,EAAsE,MAAM,kBAAkB,CAAC;AACpH,OAAO,
|
|
1
|
+
{"version":3,"file":"server-log-fixture.js","sourceRoot":"","sources":["../../../src/testing/playwright/server-log-fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,kFAAkF;AAClF,4EAA4E;AAC5E,oEAAoE;AACpE,OAAO,EAAE,IAAI,IAAI,IAAI,EAAsE,MAAM,kBAAkB,CAAC;AACpH,OAAO,EACJ,eAAe,EACf,6BAA6B,EAC7B,4BAA4B,EAC5B,aAAa,EACb,oBAAoB,EACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAkC7D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAwF;IACnH,kBAAkB,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC1C,cAAc,EAAE,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACzC,kBAAkB,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC1C,gBAAgB,EAAE;QACf,KAAK,EAAE,EAAE,kBAAkB,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,GAAG,EAAiB,EAAE;YACtF,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBACvB,MAAM,GAAG,CAAC,SAAS,CAAC,CAAC;gBACrB,OAAO;YACV,CAAC;YACD,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,kBAAkB,CAAC,CAAC;YAC/C,MAAM,GAAG,CAAC,SAAS,CAAC,CAAC;YACrB,MAAM,IAAI,GAAG,oBAAoB,CAAC,kBAAkB,CAAC,CAAC;YACtD,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACrD,IAAI,OAA2B,CAAC;YAChC,IAAI,cAAc,EAAE,CAAC;gBAClB,MAAM,UAAU,GAAG,4BAA4B,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;gBAC5E,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACzB,OAAO,GAAG,qBAAqB,UAAU,CAAC,MAAM,uCAAuC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClH,CAAC;YACJ,CAAC;YACD,sEAAsE;YACtE,uEAAuE;YACvE,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;YAC7F,MAAM,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,kBAAkB,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACpF,IAAI,OAAO,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;YAC5B,CAAC;QACJ,CAAC;QACD,EAAE,IAAI,EAAE,IAAI,EAAE;KAChB;CACH,CAAC;AAEF,gGAAgG;AAChG,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAwB,iBAAiB,CAAC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
* The part of `file` written since the last read of it, up to its last complete
|
|
11
|
+
* line, since the server may be mid-write. Every test reads, checked or not, so
|
|
12
|
+
* a test that is not checked leaves no lines to the next one.
|
|
13
|
+
*/
|
|
14
|
+
export declare function readServerLogRegion(file: string): string;
|
|
15
|
+
//# sourceMappingURL=server-log-region.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-log-region.d.ts","sourceRoot":"","sources":["../../../src/testing/playwright/server-log-region.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAUlF;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASxD"}
|
|
@@ -0,0 +1,30 @@
|
|
|
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 { existsSync, readFileSync } from 'node:fs';
|
|
10
|
+
/**
|
|
11
|
+
* How far each log has been read. A suite starts its app before its first
|
|
12
|
+
* test's marker, so reading from the marker would leave startup unread.
|
|
13
|
+
*/
|
|
14
|
+
const readUpTo = new Map();
|
|
15
|
+
/**
|
|
16
|
+
* The part of `file` written since the last read of it, up to its last complete
|
|
17
|
+
* line, since the server may be mid-write. Every test reads, checked or not, so
|
|
18
|
+
* a test that is not checked leaves no lines to the next one.
|
|
19
|
+
*/
|
|
20
|
+
export function readServerLogRegion(file) {
|
|
21
|
+
if (!existsSync(file)) {
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
const log = readFileSync(file);
|
|
25
|
+
const end = log.lastIndexOf('\n') + 1;
|
|
26
|
+
const text = log.subarray(readUpTo.get(file) ?? 0, end).toString('utf-8');
|
|
27
|
+
readUpTo.set(file, end);
|
|
28
|
+
return text;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=server-log-region.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-log-region.js","sourceRoot":"","sources":["../../../src/testing/playwright/server-log-region.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEnD;;;GAGG;AACH,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;AAE3C;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC7C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,CAAC;IACb,CAAC;IACD,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,GAAG,GAAG,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC1E,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACxB,OAAO,IAAI,CAAC;AACf,CAAC"}
|
|
@@ -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;AAyClF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAO3D;AAOD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CASvD"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* SPDX-License-Identifier: MIT
|
|
8
8
|
********************************************************************************/
|
|
9
|
+
import { describeError } from '@hydranium/protocol';
|
|
9
10
|
/**
|
|
10
11
|
* Phrases that mean a call lost its connection. Matched on the message because
|
|
11
12
|
* the condition surfaces in shapes that share no discriminator:
|
|
@@ -60,7 +61,7 @@ export function isConnectionGoneError(err) {
|
|
|
60
61
|
if (isDestroyedStreamError(err)) {
|
|
61
62
|
return true;
|
|
62
63
|
}
|
|
63
|
-
const message =
|
|
64
|
+
const message = describeError(err);
|
|
64
65
|
const normalized = message.toLowerCase();
|
|
65
66
|
return CONNECTION_GONE_PATTERNS.some(pattern => pattern.test(normalized));
|
|
66
67
|
}
|
|
@@ -85,7 +86,7 @@ export function isClientGoneError(err) {
|
|
|
85
86
|
if (err instanceof Error && 'code' in err && typeof err.code === 'string' && SOCKET_GONE_CODES.has(err.code)) {
|
|
86
87
|
return true;
|
|
87
88
|
}
|
|
88
|
-
const message =
|
|
89
|
+
const message = describeError(err);
|
|
89
90
|
return SOCKET_GONE_PATTERN.test(message.toLowerCase());
|
|
90
91
|
}
|
|
91
92
|
//# sourceMappingURL=connection-liveness.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection-liveness.js","sourceRoot":"","sources":["../../src/util/connection-liveness.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF;;;;;;;;;;;;;;;;;;;;;;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,
|
|
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"}
|
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.269",
|
|
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",
|
|
@@ -83,8 +83,8 @@
|
|
|
83
83
|
"diff": "^5.2.0"
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
|
-
"@hydranium/langium": "1.0.0-next.
|
|
87
|
-
"@hydranium/protocol": "1.0.0-next.
|
|
86
|
+
"@hydranium/langium": "1.0.0-next.269",
|
|
87
|
+
"@hydranium/protocol": "1.0.0-next.269",
|
|
88
88
|
"@playwright/test": "^1.40.0",
|
|
89
89
|
"@types/diff": "^5.2.0",
|
|
90
90
|
"rimraf": "^5.0.0",
|
|
@@ -96,8 +96,8 @@
|
|
|
96
96
|
"vscode-languageserver-types": "^3.17.5"
|
|
97
97
|
},
|
|
98
98
|
"peerDependencies": {
|
|
99
|
-
"@hydranium/langium": "1.0.0-next.
|
|
100
|
-
"@hydranium/protocol": "1.0.0-next.
|
|
99
|
+
"@hydranium/langium": "1.0.0-next.269",
|
|
100
|
+
"@hydranium/protocol": "1.0.0-next.269",
|
|
101
101
|
"@playwright/test": "^1.40.0",
|
|
102
102
|
"vscode-jsonrpc": "^9.0.0",
|
|
103
103
|
"vscode-languageserver": "~10.0.1",
|
|
@@ -17,7 +17,8 @@ import {
|
|
|
17
17
|
asModelVersion,
|
|
18
18
|
type ModelVersion,
|
|
19
19
|
type TextVersion,
|
|
20
|
-
UNRECORDED_VERSION
|
|
20
|
+
UNRECORDED_VERSION,
|
|
21
|
+
describeErrorWithStack
|
|
21
22
|
} from '@hydranium/protocol';
|
|
22
23
|
import { type AstNode, DocumentState, type LangiumDocument, type LangiumDocuments, URI, UriUtils } from '@hydranium/langium';
|
|
23
24
|
import { type LogNameOptions } from '../langium/diagnostics/logger.js';
|
|
@@ -443,8 +444,11 @@ export class DefaultAstDocumentManager<
|
|
|
443
444
|
}
|
|
444
445
|
const built = this.getDocument(args.uri);
|
|
445
446
|
if (creates && built) {
|
|
446
|
-
// Without an LSP head nothing else builds the opened text.
|
|
447
|
-
|
|
447
|
+
// Without an LSP head nothing else builds the opened text. Nothing
|
|
448
|
+
// waits on the build either, so its failure ends here.
|
|
449
|
+
this.services.workspace.VersionSyncService.syncTo(built.uri, this.textDocuments.version(args.uri))?.catch((error: unknown) =>
|
|
450
|
+
this.tracer.with(args.uri).error(`The build of the opened text failed. ${describeErrorWithStack(error)}`)
|
|
451
|
+
);
|
|
448
452
|
}
|
|
449
453
|
}
|
|
450
454
|
return Disposable.create(() => this.close(args));
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
|
|
10
10
|
import { type URI, UriUtils } from '@hydranium/langium';
|
|
11
|
-
import { type CanonicalUri, type Tracer } from '@hydranium/protocol';
|
|
11
|
+
import { type CanonicalUri, type Tracer, describeError, describeErrorWithStack } from '@hydranium/protocol';
|
|
12
12
|
import { type CancellationToken, type Disposable } from 'vscode-languageserver';
|
|
13
13
|
import { type LogNameOptions } from '../langium/diagnostics/logger.js';
|
|
14
14
|
import { HYDRANIUM_BUILD_REASONS } from '../langium/document-builder/document-builder.js';
|
|
@@ -38,7 +38,7 @@ export interface ReleasedDocument {
|
|
|
38
38
|
*/
|
|
39
39
|
export class DocumentReleaseSkippedError extends Error {
|
|
40
40
|
constructor(cause: unknown) {
|
|
41
|
-
super(`Release skipped: the connection or the workspace went away. ${
|
|
41
|
+
super(`Release skipped: the connection or the workspace went away. ${describeError(cause)}`, {
|
|
42
42
|
cause
|
|
43
43
|
});
|
|
44
44
|
this.name = 'DocumentReleaseSkippedError';
|
|
@@ -149,16 +149,13 @@ export class DefaultDocumentReleaseHandler implements DocumentReleaseHandler {
|
|
|
149
149
|
}
|
|
150
150
|
// The build holds the file's text: only what followed its parse, such as the diagnostics publish, was lost.
|
|
151
151
|
const { error } = outcome;
|
|
152
|
-
this.tracer
|
|
153
|
-
.with(released.uri)
|
|
154
|
-
.debug(`Revert on release skipped after its parse: ${error instanceof Error ? error.message : String(error)}`);
|
|
152
|
+
this.tracer.with(released.uri).debug(`Revert on release skipped after its parse: ${describeError(error)}`);
|
|
155
153
|
return;
|
|
156
154
|
}
|
|
157
155
|
if (outcome.kind === 'failed') {
|
|
156
|
+
// Handled below, by a recovery build or the client holding the document again; one that fails rejects on its own.
|
|
158
157
|
const { error } = outcome;
|
|
159
|
-
this.tracer
|
|
160
|
-
.with(released.uri)
|
|
161
|
-
.error(`Revert on release dropped. ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`);
|
|
158
|
+
this.tracer.with(released.uri).warn(`Revert on release failed; building in its place. ${describeErrorWithStack(error)}`);
|
|
162
159
|
}
|
|
163
160
|
const parsed = 'parsed' in outcome && outcome.parsed;
|
|
164
161
|
// Without it, a revert stopped short, or an override that built nothing,
|
|
@@ -171,15 +168,12 @@ export class DefaultDocumentReleaseHandler implements DocumentReleaseHandler {
|
|
|
171
168
|
this.services.workspace.LangiumDocuments.getDocument(target) !== undefined) ||
|
|
172
169
|
outcome.kind === 'failed';
|
|
173
170
|
const recover = async (): Promise<void> => {
|
|
174
|
-
const failure = 'Build after a revert that stopped short failed; the build keeps the released text';
|
|
175
|
-
let built: boolean;
|
|
176
171
|
try {
|
|
177
|
-
|
|
172
|
+
await this.requestRecoveryBuild(released, target, 'decision' in outcome && outcome.decision === 'remove');
|
|
178
173
|
} catch (err: unknown) {
|
|
179
|
-
throw new Error(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
throw new Error(failure);
|
|
174
|
+
throw new Error(`Build after a revert that stopped short failed; the build keeps the released text. ${describeError(err)}`, {
|
|
175
|
+
cause: err
|
|
176
|
+
});
|
|
183
177
|
}
|
|
184
178
|
};
|
|
185
179
|
await this.waitForReleaseBuild(released, target, parsed, stoppedShort && !released.isReclaimed() ? recover : undefined);
|
|
@@ -290,8 +284,8 @@ export class DefaultDocumentReleaseHandler implements DocumentReleaseHandler {
|
|
|
290
284
|
}
|
|
291
285
|
}
|
|
292
286
|
|
|
293
|
-
/** The build in place of a release that stopped short;
|
|
294
|
-
protected requestRecoveryBuild(released: ReleasedDocument, target: URI, deleted: boolean): Promise<
|
|
287
|
+
/** The build in place of a release that stopped short; rejects with its error when it was given up. */
|
|
288
|
+
protected requestRecoveryBuild(released: ReleasedDocument, target: URI, deleted: boolean): Promise<void> {
|
|
295
289
|
return this.services.workspace.VersionSyncService.requestRecoveryBuild(target, {
|
|
296
290
|
deleted,
|
|
297
291
|
reason: HYDRANIUM_BUILD_REASONS.didRelease,
|
|
@@ -319,8 +313,8 @@ export class DefaultDocumentReleaseHandler implements DocumentReleaseHandler {
|
|
|
319
313
|
const workspace = this.services.workspace;
|
|
320
314
|
// A client holding the document again keeps what the build holds for it.
|
|
321
315
|
if (parsed || workspace.LangiumDocuments.getDocument(target) === undefined || released.isReclaimed()) {
|
|
322
|
-
//
|
|
323
|
-
startRecovery?.().catch((err: unknown) => this.tracer.with(released.uri).
|
|
316
|
+
// The release got what it waits for, so a failed recovery build is diagnosis; its retry warned.
|
|
317
|
+
startRecovery?.().catch((err: unknown) => this.tracer.with(released.uri).debug(describeErrorWithStack(err)));
|
|
324
318
|
return Promise.resolve();
|
|
325
319
|
}
|
|
326
320
|
return new Promise<void>((resolve, reject) => {
|
|
@@ -352,9 +346,9 @@ export class DefaultDocumentReleaseHandler implements DocumentReleaseHandler {
|
|
|
352
346
|
}
|
|
353
347
|
},
|
|
354
348
|
(err: unknown) => {
|
|
355
|
-
// Settled by a parse that came first, so
|
|
349
|
+
// Settled by a parse or removal that came first, so diagnosis; its retry warned.
|
|
356
350
|
if (ended) {
|
|
357
|
-
this.tracer.with(released.uri).
|
|
351
|
+
this.tracer.with(released.uri).debug(describeErrorWithStack(err));
|
|
358
352
|
} else {
|
|
359
353
|
finish(() => reject(err));
|
|
360
354
|
}
|
|
@@ -54,7 +54,9 @@ import {
|
|
|
54
54
|
type TextVersion,
|
|
55
55
|
STALE_VERSION,
|
|
56
56
|
type Tracer,
|
|
57
|
-
UNRECORDED_VERSION
|
|
57
|
+
UNRECORDED_VERSION,
|
|
58
|
+
describeError,
|
|
59
|
+
describeErrorWithStack
|
|
58
60
|
} from '@hydranium/protocol';
|
|
59
61
|
import { type LogNameOptions } from '../langium/diagnostics/logger.js';
|
|
60
62
|
import { LANGUAGE_CLIENT_ID } from './client-ids.js';
|
|
@@ -467,9 +469,11 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
467
469
|
return;
|
|
468
470
|
}
|
|
469
471
|
}
|
|
470
|
-
|
|
472
|
+
// Diagnosis: one comes with every keystroke.
|
|
473
|
+
this.logUri(
|
|
471
474
|
document.uri,
|
|
472
|
-
`Update to version ${document.version} by ${this.formatClientId(clientId)}${committed.changed ? '' : ' (content unchanged)'}
|
|
475
|
+
`Update to version ${document.version} by ${this.formatClientId(clientId)}${committed.changed ? '' : ' (content unchanged)'}`,
|
|
476
|
+
'debug'
|
|
473
477
|
);
|
|
474
478
|
this.__onDidChangeContent.fire(Object.freeze({ document, clientId }));
|
|
475
479
|
}
|
|
@@ -501,9 +505,10 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
501
505
|
// Unchanged text calls no update: a configuration may return a new document for one.
|
|
502
506
|
const { document, changed } =
|
|
503
507
|
synced.getText() === text ? { document: synced, changed: false } : this.commitChange(key, synced, [{ text }], clientId);
|
|
504
|
-
this.
|
|
508
|
+
this.logUri(
|
|
505
509
|
document.uri,
|
|
506
|
-
`Update to version ${document.version} by ${this.formatClientId(clientId)}${changed ? '' : ' (content unchanged)'}
|
|
510
|
+
`Update to version ${document.version} by ${this.formatClientId(clientId)}${changed ? '' : ' (content unchanged)'}`,
|
|
511
|
+
'debug'
|
|
507
512
|
);
|
|
508
513
|
this.__onDidChangeContent.fire(Object.freeze({ document, clientId }));
|
|
509
514
|
return document.version;
|
|
@@ -643,9 +648,7 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
643
648
|
if (isDocumentReleaseSkippedError(err)) {
|
|
644
649
|
this.tracer.with(released.uri).debug(err.message);
|
|
645
650
|
} else {
|
|
646
|
-
this.tracer
|
|
647
|
-
.with(released.uri)
|
|
648
|
-
.error(`Release handler failed. ${err instanceof Error ? (err.stack ?? err.message) : String(err)}`);
|
|
651
|
+
this.tracer.with(released.uri).error(`Release handler failed. ${describeErrorWithStack(err)}`);
|
|
649
652
|
}
|
|
650
653
|
// Left unsettled, a watcher keeps the document dirty though the store answers clean.
|
|
651
654
|
announceClean(false);
|
|
@@ -709,9 +712,7 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
709
712
|
this.services.workspace.FileSystemProvider.readFile(UriUtils.toUri(uri))
|
|
710
713
|
);
|
|
711
714
|
} catch (err: unknown) {
|
|
712
|
-
this.tracer
|
|
713
|
-
.with(uri)
|
|
714
|
-
.debug(`Editor save not announced: reading the file back failed. ${err instanceof Error ? err.message : String(err)}`);
|
|
715
|
+
this.tracer.with(uri).debug(`Editor save not announced: reading the file back failed. ${describeError(err)}`);
|
|
715
716
|
return;
|
|
716
717
|
}
|
|
717
718
|
// What the file holds, not what the editor meant to write: an editor
|
|
@@ -1204,7 +1205,7 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
1204
1205
|
this.services.workspace.FileSystemProvider.readFile(UriUtils.toUri(key))
|
|
1205
1206
|
);
|
|
1206
1207
|
} catch (err: unknown) {
|
|
1207
|
-
this.tracer.with(key).debug(`Disk baseline: the file cannot be read. ${
|
|
1208
|
+
this.tracer.with(key).debug(`Disk baseline: the file cannot be read. ${describeError(err)}`);
|
|
1208
1209
|
}
|
|
1209
1210
|
this.setDiskBaseline(key, onDisk);
|
|
1210
1211
|
}
|
|
@@ -333,10 +333,11 @@ export class DefaultLanguageClientShadow<T extends TextDocument = TextDocument>
|
|
|
333
333
|
notifyOutcome: outcome => {
|
|
334
334
|
if (!settled) {
|
|
335
335
|
settled = true;
|
|
336
|
+
// Routine: an edit in the editor moved the version on before the push arrived.
|
|
336
337
|
if (outcome === 'refused' && version !== null) {
|
|
337
338
|
this.tracer
|
|
338
339
|
.with(key)
|
|
339
|
-
.
|
|
340
|
+
.debug(
|
|
340
341
|
`Language client refused applyEdit addressed at version ${version} (it last declared version ${state.declaredVersion})`
|
|
341
342
|
);
|
|
342
343
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
|
|
10
10
|
import { DocumentState, type LangiumDocument, type URI } from '@hydranium/langium';
|
|
11
|
-
import { type Tracer, UNRECORDED_VERSION } from '@hydranium/protocol';
|
|
11
|
+
import { type Tracer, UNRECORDED_VERSION, describeError, describeErrorWithStack } from '@hydranium/protocol';
|
|
12
12
|
import { Disposable, Emitter, type Event } from 'vscode-languageserver-protocol';
|
|
13
13
|
import { type LogNameOptions } from '../langium/diagnostics/logger.js';
|
|
14
14
|
import { type ServerSharedServices } from '../langium/module.js';
|
|
@@ -46,20 +46,21 @@ export interface VersionSyncService {
|
|
|
46
46
|
/**
|
|
47
47
|
* Build `uri` from a `WorkspaceLock` read, so the build queues behind a
|
|
48
48
|
* running one rather than cancelling it. Requests made while one for `uri`
|
|
49
|
-
* is pending share its next build. Resolves
|
|
50
|
-
*
|
|
49
|
+
* is pending share its next build. Resolves once a build that carried this
|
|
50
|
+
* request completed, or once a check before a build found it no longer
|
|
51
51
|
* needed or deferred. A build a later write cancelled does not count: the
|
|
52
|
-
* request rides the next one.
|
|
53
|
-
* request have failed more often than it may retry, so a
|
|
54
|
-
* the build can stop
|
|
52
|
+
* request rides the next one. Rejects with the last build's error once builds
|
|
53
|
+
* carrying this request have failed more often than it may retry, so a
|
|
54
|
+
* caller waiting on the build can stop; the caller ends that failure, and
|
|
55
|
+
* logs it if nothing else reports it.
|
|
55
56
|
*/
|
|
56
|
-
requestRecoveryBuild(uri: URI, request: RecoveryBuildRequest): Promise<
|
|
57
|
+
requestRecoveryBuild(uri: URI, request: RecoveryBuildRequest): Promise<void>;
|
|
57
58
|
/**
|
|
58
59
|
* Build `uri` unless it is {@link isSyncedTo synced to} `textVersion`, asked
|
|
59
60
|
* again inside the lock. `undefined` when no build is needed, else the
|
|
60
|
-
* {@link requestRecoveryBuild}
|
|
61
|
+
* {@link requestRecoveryBuild} promise.
|
|
61
62
|
*/
|
|
62
|
-
syncTo(uri: URI, textVersion: number): Promise<
|
|
63
|
+
syncTo(uri: URI, textVersion: number): Promise<void> | undefined;
|
|
63
64
|
/**
|
|
64
65
|
* Whether `uri`'s root was parsed from text at `textVersion` or later; requests
|
|
65
66
|
* nothing. A root with no recorded version or a placeholder counts as synced,
|
|
@@ -85,7 +86,10 @@ export interface VersionSyncService {
|
|
|
85
86
|
export interface QueuedRecoveryBuild {
|
|
86
87
|
readonly request: RecoveryBuildRequest;
|
|
87
88
|
failures: number;
|
|
88
|
-
|
|
89
|
+
/** Settles the request once a build carried it or it was no longer needed. */
|
|
90
|
+
readonly resolve: () => void;
|
|
91
|
+
/** Settles the request once it has used its retries, with the last build's error. */
|
|
92
|
+
readonly reject: (error: unknown) => void;
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
/** The requests waiting on one URI's builds in {@link DefaultVersionSyncService}. */
|
|
@@ -102,7 +106,7 @@ export class DefaultVersionSyncService implements VersionSyncService {
|
|
|
102
106
|
protected readonly recordedEmitter = new Emitter<LangiumDocument>();
|
|
103
107
|
protected readonly deferredBuildChecks = new Set<(uri: URI) => boolean>();
|
|
104
108
|
protected readonly pendingBuilds = new Map<string, PendingRecoveryBuilds>();
|
|
105
|
-
/** Retries of each request after a failed build before it is
|
|
109
|
+
/** Retries of each request after a failed build before it is rejected. */
|
|
106
110
|
protected readonly maxBuildRetries: number = 1;
|
|
107
111
|
|
|
108
112
|
constructor(
|
|
@@ -142,19 +146,22 @@ export class DefaultVersionSyncService implements VersionSyncService {
|
|
|
142
146
|
}
|
|
143
147
|
ledger.record(root, version, origin.text);
|
|
144
148
|
}
|
|
145
|
-
|
|
149
|
+
// Nothing waits on this build, so its failure ends here.
|
|
150
|
+
this.syncTo(document.uri, store.version(uri))?.catch((error: unknown) =>
|
|
151
|
+
this.tracer.withUri(uri).error(`The build that would sync the root to its text failed. ${describeErrorWithStack(error)}`)
|
|
152
|
+
);
|
|
146
153
|
this.recordedEmitter.fire(document);
|
|
147
154
|
}
|
|
148
155
|
|
|
149
|
-
syncTo(uri: URI, textVersion: number): Promise<
|
|
156
|
+
syncTo(uri: URI, textVersion: number): Promise<void> | undefined {
|
|
150
157
|
const behind = (): boolean => !this.isSyncedTo(uri, textVersion);
|
|
151
158
|
return behind() ? this.requestRecoveryBuild(uri, { stillNeeded: behind }) : undefined;
|
|
152
159
|
}
|
|
153
160
|
|
|
154
|
-
requestRecoveryBuild(uri: URI, request: RecoveryBuildRequest): Promise<
|
|
155
|
-
return new Promise<
|
|
161
|
+
requestRecoveryBuild(uri: URI, request: RecoveryBuildRequest): Promise<void> {
|
|
162
|
+
return new Promise<void>((resolve, reject) => {
|
|
156
163
|
const key = uri.toString();
|
|
157
|
-
const queued: QueuedRecoveryBuild = { request, failures: 0,
|
|
164
|
+
const queued: QueuedRecoveryBuild = { request, failures: 0, resolve, reject };
|
|
158
165
|
const pending = this.pendingBuilds.get(key);
|
|
159
166
|
if (pending) {
|
|
160
167
|
// Not dropped: the pending build may have read the text before this request's change.
|
|
@@ -189,8 +196,8 @@ export class DefaultVersionSyncService implements VersionSyncService {
|
|
|
189
196
|
|
|
190
197
|
/**
|
|
191
198
|
* Run `entry`'s requests, one build per batch, until none is queued, and
|
|
192
|
-
*
|
|
193
|
-
* {@link maxBuildRetries} times, then
|
|
199
|
+
* settle each. A request a failed build carried is queued again
|
|
200
|
+
* {@link maxBuildRetries} times, then rejected; one a cancelled
|
|
194
201
|
* build carried is queued again without counting. The entry is dropped
|
|
195
202
|
* in the same tick as the last check of the queue, so no request lands in
|
|
196
203
|
* an entry nothing runs any more.
|
|
@@ -210,7 +217,7 @@ export class DefaultVersionSyncService implements VersionSyncService {
|
|
|
210
217
|
await lock.read(() => {
|
|
211
218
|
carried = entry.queued.splice(0);
|
|
212
219
|
const needed = carried.filter(queued => this.isStillNeeded(uri, queued.request));
|
|
213
|
-
carried.filter(queued => !needed.includes(queued)).forEach(queued => queued.
|
|
220
|
+
carried.filter(queued => !needed.includes(queued)).forEach(queued => queued.resolve());
|
|
214
221
|
carried = needed;
|
|
215
222
|
if (needed.length > 0) {
|
|
216
223
|
build = this.scheduleBatch(
|
|
@@ -227,18 +234,19 @@ export class DefaultVersionSyncService implements VersionSyncService {
|
|
|
227
234
|
entry.queued.unshift(...carried);
|
|
228
235
|
continue;
|
|
229
236
|
}
|
|
230
|
-
carried.forEach(queued => queued.
|
|
237
|
+
carried.forEach(queued => queued.resolve());
|
|
231
238
|
} catch (err: unknown) {
|
|
232
|
-
const detail = err instanceof Error ? (err.stack ?? err.message) : String(err);
|
|
233
239
|
const retried = carried.filter(queued => queued.failures++ < this.maxBuildRetries);
|
|
234
240
|
const givenUp = carried.filter(queued => !retried.includes(queued));
|
|
235
241
|
if (retried.length > 0) {
|
|
236
|
-
|
|
242
|
+
// The only line about this failure when the retry succeeds.
|
|
243
|
+
tracer.warn(`Requested build failed; retrying. ${describeErrorWithStack(err)}`);
|
|
237
244
|
entry.queued.unshift(...retried);
|
|
238
245
|
}
|
|
239
246
|
if (givenUp.length > 0) {
|
|
240
|
-
|
|
241
|
-
|
|
247
|
+
// Each requester ends the failure, so the log line is theirs.
|
|
248
|
+
tracer.debug(`Requested build failed again; giving up. ${describeError(err)}`);
|
|
249
|
+
givenUp.forEach(queued => queued.reject(err));
|
|
242
250
|
}
|
|
243
251
|
}
|
|
244
252
|
}
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
type LogThreshold,
|
|
20
20
|
ObservableValue,
|
|
21
21
|
type MaybeObservableValue,
|
|
22
|
+
describeErrorWithStack,
|
|
22
23
|
parseLogLevel
|
|
23
24
|
} from '@hydranium/protocol';
|
|
24
25
|
import { type URI } from '@hydranium/langium';
|
|
@@ -308,7 +309,7 @@ function formatLogArg(value: unknown): string {
|
|
|
308
309
|
return value;
|
|
309
310
|
}
|
|
310
311
|
if (value instanceof Error) {
|
|
311
|
-
return value
|
|
312
|
+
return describeErrorWithStack(value);
|
|
312
313
|
}
|
|
313
314
|
if (typeof value === 'object') {
|
|
314
315
|
try {
|