@hydranium/core 1.0.0-next.289 → 1.0.0-next.298

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 (38) hide show
  1. package/README.md +1 -0
  2. package/lib/browser/index.d.ts +10 -0
  3. package/lib/browser/index.d.ts.map +1 -0
  4. package/lib/browser/index.js +14 -0
  5. package/lib/browser/index.js.map +1 -0
  6. package/lib/browser/indexeddb-file-system-store.d.ts +82 -0
  7. package/lib/browser/indexeddb-file-system-store.d.ts.map +1 -0
  8. package/lib/browser/indexeddb-file-system-store.js +181 -0
  9. package/lib/browser/indexeddb-file-system-store.js.map +1 -0
  10. package/lib/langium/ast-extension/ast-extension-service.d.ts +39 -31
  11. package/lib/langium/ast-extension/ast-extension-service.d.ts.map +1 -1
  12. package/lib/langium/ast-extension/ast-extension-service.js +45 -19
  13. package/lib/langium/ast-extension/ast-extension-service.js.map +1 -1
  14. package/lib/langium/build-phase-pass/build-phase-pass-service.d.ts +8 -8
  15. package/lib/langium/build-phase-pass/build-phase-pass-service.d.ts.map +1 -1
  16. package/lib/langium/build-phase-pass/build-phase-pass-service.js +25 -16
  17. package/lib/langium/build-phase-pass/build-phase-pass-service.js.map +1 -1
  18. package/lib/langium/build-phase-pass/build-phase-pass.d.ts +19 -7
  19. package/lib/langium/build-phase-pass/build-phase-pass.d.ts.map +1 -1
  20. package/lib/langium/build-phase-pass/build-phase-pass.js +20 -0
  21. package/lib/langium/build-phase-pass/build-phase-pass.js.map +1 -1
  22. package/lib/langium/document-builder/build-pipeline-integration.d.ts.map +1 -1
  23. package/lib/langium/document-builder/build-pipeline-integration.js +11 -40
  24. package/lib/langium/document-builder/build-pipeline-integration.js.map +1 -1
  25. package/lib/langium/residency/cst-residency-service.js +1 -1
  26. package/lib/langium/workspace/persistent-file-system-provider.d.ts +8 -0
  27. package/lib/langium/workspace/persistent-file-system-provider.d.ts.map +1 -1
  28. package/lib/langium/workspace/persistent-file-system-provider.js +11 -0
  29. package/lib/langium/workspace/persistent-file-system-provider.js.map +1 -1
  30. package/package.json +14 -9
  31. package/src/browser/index.ts +14 -0
  32. package/src/browser/indexeddb-file-system-store.ts +192 -0
  33. package/src/langium/ast-extension/ast-extension-service.ts +74 -41
  34. package/src/langium/build-phase-pass/build-phase-pass-service.ts +28 -18
  35. package/src/langium/build-phase-pass/build-phase-pass.ts +36 -7
  36. package/src/langium/document-builder/build-pipeline-integration.ts +11 -42
  37. package/src/langium/residency/cst-residency-service.ts +1 -1
  38. package/src/langium/workspace/persistent-file-system-provider.ts +11 -0
package/README.md CHANGED
@@ -59,6 +59,7 @@ See *Compose a server by hand* and *Add a validation check* in
59
59
  | ---------------------- | ---------------------------------------------------------------------------- | --------------- |
60
60
  | `.` | Composing services and extending the language's semantics | browser-neutral |
61
61
  | `./lsp` | The LSP head and its modules | browser-neutral |
62
+ | `./browser` | The IndexedDB store behind a persistent filesystem in a web worker | browser-only |
62
63
  | `./node` | The Node filesystem, the stdio and socket launchers, the headless tools | Node-only |
63
64
  | `./messages` | Validation messages with stable codes, and the codes this package raises | browser-neutral |
64
65
  | `./testing` | Parsing and service doubles for unit tests | browser-neutral |
@@ -0,0 +1,10 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2026 CrossBreeze, EclipseSource and others.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the MIT License which is available in the project root.
6
+ *
7
+ * SPDX-License-Identifier: MIT
8
+ ********************************************************************************/
9
+ export * from './indexeddb-file-system-store.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAMlF,cAAc,kCAAkC,CAAC"}
@@ -0,0 +1,14 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2026 CrossBreeze, EclipseSource and others.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the MIT License which is available in the project root.
6
+ *
7
+ * SPDX-License-Identifier: MIT
8
+ ********************************************************************************/
9
+ // Worker-only entry (`@hydranium/core/browser`). Compiled as its own project
10
+ // against `lib.webworker`, so a worker global such as `indexedDB` resolves here
11
+ // and nowhere else in the package: the portable `.` barrel must stay free of
12
+ // DOM and worker globals, since it also loads in a plain Node consumer.
13
+ export * from './indexeddb-file-system-store.js';
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,6EAA6E;AAC7E,gFAAgF;AAChF,6EAA6E;AAC7E,wEAAwE;AACxE,cAAc,kCAAkC,CAAC"}
@@ -0,0 +1,82 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2026 CrossBreeze, EclipseSource and others.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the MIT License which is available in the project root.
6
+ *
7
+ * SPDX-License-Identifier: MIT
8
+ ********************************************************************************/
9
+ import type { FileSystemSeed } from '../langium/workspace/in-memory-file-system-provider.js';
10
+ import type { FileSystemStore } from '../langium/workspace/persistent-file-system-provider.js';
11
+ /**
12
+ * A {@link FileSystemStore} in `IndexedDB`, so an edit survives a reload.
13
+ *
14
+ * # Why IndexedDB and not OPFS
15
+ *
16
+ * OPFS is the tempting answer, because `createSyncAccessHandle` gives
17
+ * SYNCHRONOUS file reads in a worker, which is what Langium's
18
+ * `FileSystemProvider` needs. It buys nothing here: directory enumeration
19
+ * (`FileSystemDirectoryHandle.entries()`) is async-only, so a directory index
20
+ * still has to live in memory, and once it does the file reads are being served
21
+ * from memory too. IndexedDB is the smaller dependency for the same shape.
22
+ *
23
+ * # It holds the DELTA, not the workspace
24
+ *
25
+ * Only what has been written lands here, and `PersistentFileSystemProvider` lays
26
+ * it over the host's seed. So a fixture edited in the host's bundle still
27
+ * reaches a returning visitor, and this database stays a few kilobytes rather
28
+ * than a copy of the workspace.
29
+ *
30
+ * # A failure is loud, deliberately
31
+ *
32
+ * Nothing here degrades to non-persistent operation. A refused open, a blocked
33
+ * upgrade or an aborted transaction, which is what a quota overrun looks like,
34
+ * rejects the call, which fails the worker's bootstrap or the save that asked.
35
+ * The alternative, carrying on without a store, would report a saved workspace
36
+ * and lose it on the next load. An origin EVICTED between visits is not a
37
+ * failure this class can see: its database opens empty, which is a first visit,
38
+ * and the provider is correct in that state. Evicted mid-session, the next
39
+ * transaction rejects like any other failure.
40
+ *
41
+ * # The database is origin-scoped
42
+ *
43
+ * Every page on the origin shares a database of the same name, and the user
44
+ * agent may evict it. The name is the one per-app value and is REQUIRED for
45
+ * that reason: a default would let two apps on one origin overwrite each
46
+ * other's edits with nothing reporting it.
47
+ */
48
+ export declare class IndexedDbFileSystemStore implements FileSystemStore {
49
+ protected readonly databaseName: string;
50
+ /**
51
+ * Opened once and shared, and NOT kept when it fails: a refused or blocked
52
+ * open rejects the call that needed it and the next call opens again, so a
53
+ * cause that has cleared, such as the other tab closing, does not fail every
54
+ * later save with the first error.
55
+ */
56
+ protected connection?: Promise<IDBDatabase>;
57
+ constructor(databaseName: string);
58
+ load(): Promise<FileSystemSeed>;
59
+ write(path: string, content: string): Promise<void>;
60
+ remove(path: string): Promise<void>;
61
+ /**
62
+ * Drop everything, returning the next load to the seed.
63
+ *
64
+ * The escape hatch a persistent host needs and a seeded one does not: a saved
65
+ * document that no longer parses is otherwise restored on every load, and
66
+ * clearing site data is the only other way out. Not on {@link FileSystemStore},
67
+ * which the provider never clears.
68
+ */
69
+ clear(): Promise<void>;
70
+ /**
71
+ * Run one write transaction and settle on its COMPLETION.
72
+ *
73
+ * Not on the request's `success`: a request can succeed inside a transaction
74
+ * that then aborts, which is exactly what a quota overrun looks like, so
75
+ * resolving early would report a save the database never kept.
76
+ */
77
+ protected mutate(operation: string, act: (files: IDBObjectStore) => void): Promise<void>;
78
+ protected open(): Promise<IDBDatabase>;
79
+ protected openDatabase(): Promise<IDBDatabase>;
80
+ protected failure(transaction: IDBTransaction, operation: string): Error;
81
+ }
82
+ //# sourceMappingURL=indexeddb-file-system-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexeddb-file-system-store.d.ts","sourceRoot":"","sources":["../../src/browser/indexeddb-file-system-store.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wDAAwD,CAAC;AAC7F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yDAAyD,CAAC;AAW/F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,qBAAa,wBAAyB,YAAW,eAAe;IASjD,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM;IARnD;;;;;OAKG;IACH,SAAS,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAE5C,YAA+B,YAAY,EAAE,MAAM,EAAI;IAEjD,IAAI,IAAI,OAAO,CAAC,cAAc,CAAC,CA2BpC;IAEK,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAExD;IAEK,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAExC;IAED;;;;;;;OAOG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3B;IAED;;;;;;OAMG;IACH,UAAgB,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ7F;IAED,SAAS,CAAC,IAAI,IAAI,OAAO,CAAC,WAAW,CAAC,CAMrC;IAED,SAAS,CAAC,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC,CAwC7C;IAED,SAAS,CAAC,OAAO,CAAC,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK,CAKvE;CACH"}
@@ -0,0 +1,181 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2026 CrossBreeze, EclipseSource and others.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the MIT License which is available in the project root.
6
+ *
7
+ * SPDX-License-Identifier: MIT
8
+ ********************************************************************************/
9
+ /** Out-of-line keys, so a key is the store path and the value is the content. */
10
+ const OBJECT_STORE_NAME = 'files';
11
+ /**
12
+ * The schema version. A bump needs a migration in `onupgradeneeded`, or every
13
+ * returning visitor's rows are left in a store the new code never reads.
14
+ */
15
+ const DATABASE_VERSION = 1;
16
+ /**
17
+ * A {@link FileSystemStore} in `IndexedDB`, so an edit survives a reload.
18
+ *
19
+ * # Why IndexedDB and not OPFS
20
+ *
21
+ * OPFS is the tempting answer, because `createSyncAccessHandle` gives
22
+ * SYNCHRONOUS file reads in a worker, which is what Langium's
23
+ * `FileSystemProvider` needs. It buys nothing here: directory enumeration
24
+ * (`FileSystemDirectoryHandle.entries()`) is async-only, so a directory index
25
+ * still has to live in memory, and once it does the file reads are being served
26
+ * from memory too. IndexedDB is the smaller dependency for the same shape.
27
+ *
28
+ * # It holds the DELTA, not the workspace
29
+ *
30
+ * Only what has been written lands here, and `PersistentFileSystemProvider` lays
31
+ * it over the host's seed. So a fixture edited in the host's bundle still
32
+ * reaches a returning visitor, and this database stays a few kilobytes rather
33
+ * than a copy of the workspace.
34
+ *
35
+ * # A failure is loud, deliberately
36
+ *
37
+ * Nothing here degrades to non-persistent operation. A refused open, a blocked
38
+ * upgrade or an aborted transaction, which is what a quota overrun looks like,
39
+ * rejects the call, which fails the worker's bootstrap or the save that asked.
40
+ * The alternative, carrying on without a store, would report a saved workspace
41
+ * and lose it on the next load. An origin EVICTED between visits is not a
42
+ * failure this class can see: its database opens empty, which is a first visit,
43
+ * and the provider is correct in that state. Evicted mid-session, the next
44
+ * transaction rejects like any other failure.
45
+ *
46
+ * # The database is origin-scoped
47
+ *
48
+ * Every page on the origin shares a database of the same name, and the user
49
+ * agent may evict it. The name is the one per-app value and is REQUIRED for
50
+ * that reason: a default would let two apps on one origin overwrite each
51
+ * other's edits with nothing reporting it.
52
+ */
53
+ export class IndexedDbFileSystemStore {
54
+ databaseName;
55
+ /**
56
+ * Opened once and shared, and NOT kept when it fails: a refused or blocked
57
+ * open rejects the call that needed it and the next call opens again, so a
58
+ * cause that has cleared, such as the other tab closing, does not fail every
59
+ * later save with the first error.
60
+ */
61
+ connection;
62
+ constructor(databaseName) {
63
+ this.databaseName = databaseName;
64
+ }
65
+ async load() {
66
+ const database = await this.open();
67
+ return new Promise((resolve, reject) => {
68
+ const transaction = database.transaction(OBJECT_STORE_NAME, 'readonly');
69
+ const files = transaction.objectStore(OBJECT_STORE_NAME);
70
+ const keyRequest = files.getAllKeys();
71
+ const valueRequest = files.getAll();
72
+ // Resolved on the TRANSACTION rather than on either request, which is
73
+ // what guarantees both have landed, and `getAllKeys` and `getAll`
74
+ // enumerate in the same key order, so they zip.
75
+ transaction.oncomplete = () => {
76
+ const keys = keyRequest.result;
77
+ const values = valueRequest.result;
78
+ const seed = {};
79
+ keys.forEach((key, index) => {
80
+ const value = values[index];
81
+ // Guarded rather than cast: this database outlives the code that
82
+ // wrote it, so a row from an older shape must be skipped instead
83
+ // of reaching the filesystem as non-string content.
84
+ if (typeof key === 'string' && typeof value === 'string') {
85
+ seed[key] = value;
86
+ }
87
+ });
88
+ resolve(seed);
89
+ };
90
+ transaction.onabort = () => reject(this.failure(transaction, 'read the stored workspace'));
91
+ });
92
+ }
93
+ async write(path, content) {
94
+ await this.mutate('store', files => files.put(content, path));
95
+ }
96
+ async remove(path) {
97
+ await this.mutate('drop', files => files.delete(path));
98
+ }
99
+ /**
100
+ * Drop everything, returning the next load to the seed.
101
+ *
102
+ * The escape hatch a persistent host needs and a seeded one does not: a saved
103
+ * document that no longer parses is otherwise restored on every load, and
104
+ * clearing site data is the only other way out. Not on {@link FileSystemStore},
105
+ * which the provider never clears.
106
+ */
107
+ async clear() {
108
+ await this.mutate('clear', files => files.clear());
109
+ }
110
+ /**
111
+ * Run one write transaction and settle on its COMPLETION.
112
+ *
113
+ * Not on the request's `success`: a request can succeed inside a transaction
114
+ * that then aborts, which is exactly what a quota overrun looks like, so
115
+ * resolving early would report a save the database never kept.
116
+ */
117
+ async mutate(operation, act) {
118
+ const database = await this.open();
119
+ return new Promise((resolve, reject) => {
120
+ const transaction = database.transaction(OBJECT_STORE_NAME, 'readwrite');
121
+ transaction.oncomplete = () => resolve();
122
+ transaction.onabort = () => reject(this.failure(transaction, operation));
123
+ act(transaction.objectStore(OBJECT_STORE_NAME));
124
+ });
125
+ }
126
+ open() {
127
+ this.connection ??= this.openDatabase().catch((error) => {
128
+ this.connection = undefined;
129
+ throw error;
130
+ });
131
+ return this.connection;
132
+ }
133
+ openDatabase() {
134
+ return new Promise((resolve, reject) => {
135
+ const request = indexedDB.open(this.databaseName, DATABASE_VERSION);
136
+ let blocked = false;
137
+ request.onupgradeneeded = () => {
138
+ // Runs on a first visit and on a version bump. `createObjectStore`
139
+ // throws if it already exists, so the guard is not defensive: a page
140
+ // opened twice with the store created once gets here only when the
141
+ // version moved.
142
+ if (!request.result.objectStoreNames.contains(OBJECT_STORE_NAME)) {
143
+ request.result.createObjectStore(OBJECT_STORE_NAME);
144
+ }
145
+ };
146
+ request.onsuccess = () => {
147
+ const database = request.result;
148
+ if (blocked) {
149
+ // Went through once the other tab closed, after this call had
150
+ // rejected. Nobody holds it, so it is closed rather than left
151
+ // open to block the NEXT upgrade.
152
+ database.close();
153
+ return;
154
+ }
155
+ // A newer version opening in another tab asks this one to let go,
156
+ // and holding on would block that upgrade for as long as this tab
157
+ // lives. Dropped, so every later call here rejects, which is loud.
158
+ database.onversionchange = () => {
159
+ database.close();
160
+ this.connection = undefined;
161
+ };
162
+ resolve(database);
163
+ };
164
+ request.onerror = () => reject(request.error ?? new Error(`Cannot open ${this.databaseName}`));
165
+ // A tab at an older version that lacks the close above holds the
166
+ // upgrade indefinitely. Reported rather than waited on: the host can say
167
+ // so, where a hung open looks like a worker that never started.
168
+ request.onblocked = () => {
169
+ blocked = true;
170
+ reject(new Error(`${this.databaseName} is open in another tab at an older version`));
171
+ };
172
+ });
173
+ }
174
+ failure(transaction, operation) {
175
+ // `transaction.error` names the real cause (`QuotaExceededError` above
176
+ // all); an abort with no error is a caller-side abort, which nothing here
177
+ // does.
178
+ return transaction.error ?? new Error(`Failed to ${operation} in ${this.databaseName}`);
179
+ }
180
+ }
181
+ //# sourceMappingURL=indexeddb-file-system-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexeddb-file-system-store.js","sourceRoot":"","sources":["../../src/browser/indexeddb-file-system-store.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,20 @@ import { type LogNameOptions } from '../diagnostics/logger.js';
13
13
  import { type ServerLanguageServices } from '../language-module.js';
14
14
  import { Registry, type RegistryItem } from '../../util/registry.js';
15
15
  import { type AstExtensionRegistry } from './ast-extension-contribution.js';
16
+ /**
17
+ * The document-build phases an {@link AstExtension} may fire at. `Changed` has
18
+ * no AST yet, and `IndexedContent` carries the same per-document information
19
+ * as `Parsed`, so neither is one.
20
+ */
21
+ export type AstExtensionPhase = DocumentState.Parsed | DocumentState.ComputedScopes | DocumentState.Linked | DocumentState.IndexedReferences | DocumentState.Validated;
22
+ export declare namespace AstExtensionPhase {
23
+ /**
24
+ * Every {@link AstExtensionPhase}, in build order. `BuildPipelineIntegration`
25
+ * wires one `onDocumentPhase` listener per entry and `register` refuses a
26
+ * phase outside it, so the two cannot drift apart. A fresh array each call.
27
+ */
28
+ function all(): readonly AstExtensionPhase[];
29
+ }
16
30
  /**
17
31
  * A registration that derives data from an AST node and attaches it to the
18
32
  * node at a declared document-build phase.
@@ -27,13 +41,13 @@ import { type AstExtensionRegistry } from './ast-extension-contribution.js';
27
41
  * are stored as non-enumerable own properties. Otherwise symbol providers,
28
42
  * folding, semantic tokens, and validators will treat them as real children.
29
43
  *
30
- * **Phase semantics:** {@link state} declares the single phase at which
44
+ * **Phase semantics:** {@link phase} declares the single phase at which
31
45
  * {@link compute} fires. The framework wires one `onDocumentPhase` listener per
32
- * useful state unconditionally, rather than deriving the set from the states
33
- * registrations declare — so a registration's state must be one of those, and
34
- * adding one is a framework change, not a registration-time choice.
46
+ * {@link AstExtensionPhase} unconditionally, rather than deriving the set from
47
+ * the phases registrations declare — so `register` refuses any other phase,
48
+ * and adding one is a framework change, not a registration-time choice.
35
49
  *
36
- * **`.ref`-dependent derivations: declare `state: ComputedScopes`, not `Linked`.**
50
+ * **`.ref`-dependent derivations: declare `phase: ComputedScopes`, not `Linked`.**
37
51
  * Lazy reference resolution is available from `ComputedScopes` onward — the
38
52
  * global scope is built by `IndexedContent` (which runs before
39
53
  * `ComputedScopes`), so reading a reference's `.ref` inside a `ComputedScopes`
@@ -42,8 +56,8 @@ import { type AstExtensionRegistry } from './ast-extension-contribution.js';
42
56
  * {@link DocumentBuilderOptions.refreshCrossDocumentComputedScopes} is set, so
43
57
  * one that reads another document's node goes stale when that node changes.
44
58
  *
45
- * **DO NOT** stage a `state: ComputedScopes` placeholder hoping to refine it
46
- * via a separate `state: Linked` registration when a downstream consumer
59
+ * **DO NOT** stage a `phase: ComputedScopes` placeholder hoping to refine it
60
+ * via a separate `phase: Linked` registration when a downstream consumer
47
61
  * (typically a `ScopeExtension.addDescriptions` firing during link)
48
62
  * iterates the derived value to build a scope: `onDocumentPhase(Linked)` fires
49
63
  * AFTER Langium's `Linker.link()` has already walked the document, by which
@@ -61,32 +75,26 @@ export interface AstExtension<T extends AstNode = AstNode> extends RegistryItem
61
75
  /**
62
76
  * Per-document early-out. When set, the framework only invokes the
63
77
  * registration's callback for documents the filter accepts. If every
64
- * registration at a given state rejects the document, the framework skips
78
+ * registration at a given phase rejects the document, the framework skips
65
79
  * the `streamAllContents` walk entirely — important for large workspaces
66
80
  * where most documents carry none of the AST shapes a given extension
67
81
  * targets.
68
82
  *
69
- * Default: no filter (registration applies to every document at its state).
83
+ * Default: no filter (registration applies to every document at its phase).
70
84
  */
71
85
  documentFilter?: (document: LangiumDocument) => boolean;
72
86
  /**
73
- * The single document-build state at which {@link compute} fires.
87
+ * The single document-build phase at which {@link compute} fires.
74
88
  *
75
- * Useful values:
76
89
  * - `Parsed` — derivations from AST shape only, no `.ref` reads.
77
- * - `ComputedScopes` — lazy `.ref` resolution available; the most common state.
90
+ * - `ComputedScopes` — lazy `.ref` resolution available; the most common.
78
91
  * - `Linked` — derivations needing fully resolved cross-references.
79
92
  * - `IndexedReferences` — cross-document reverse-lookup derivations.
80
93
  * - `Validated` — derivations depending on diagnostics (rare).
81
- *
82
- * Any other state type-checks but never fires: the framework wires a
83
- * listener only for the states above. `Changed` has no AST yet, and
84
- * `IndexedContent` carries the same per-document information as `Parsed`,
85
- * so neither is wired.
86
94
  */
87
- state: DocumentState;
95
+ phase: AstExtensionPhase;
88
96
  /**
89
- * Mutate `node` for this registration at {@link state}. The framework hands
97
+ * Mutate `node` for this registration at {@link phase}. The framework hands
90
98
  * `node` typed as {@link Mutable} so computed / synthetic property
91
99
  * assignments (`node._foo = ...`) compile without per-callback
92
100
  * `asMutable(...)` casts.
@@ -109,18 +117,18 @@ export interface AstExtension<T extends AstNode = AstNode> extends RegistryItem
109
117
  *
110
118
  * Adopter overrides go through {@link DefaultAstExtensionService}; the
111
119
  * interface keeps the public API stable while internals (the bucket-
112
- * by-state cache) stay `protected` on the default class.
120
+ * by-phase cache) stay `protected` on the default class.
113
121
  */
114
122
  export interface AstExtensionService extends AstExtensionRegistry {
115
123
  /**
116
124
  * Run a single AST node through every registration declaring
117
- * `phase` as its state. Honours `documentFilter` and `nodeFilter`.
125
+ * `phase`. Honours `documentFilter` and `nodeFilter`.
118
126
  * Primarily for tests that need to invoke an extension callback without
119
127
  * driving Langium's full document-build pipeline.
120
128
  */
121
129
  extendNode(node: AstNode, document: LangiumDocument, phase: DocumentState): void;
122
130
  /**
123
- * Re-run all `compute` callbacks declaring `phase` as their state against
131
+ * Re-run all `compute` callbacks declaring `phase` against
124
132
  * every node in the document. Single-pass. Called by
125
133
  * `BuildPipelineIntegration` from an `onDocumentPhase` listener;
126
134
  * not for general adopter use.
@@ -143,19 +151,19 @@ export type AstExtensionServiceOptions = LogNameOptions;
143
151
  * owns the registry + per-node logic, not the listener lifecycle.
144
152
  *
145
153
  * **Performance:** `extendDocument` shares a single `streamAllContents` walk
146
- * per document per wired phase; cost is O(registrations-for-state × nodes),
147
- * and it early-outs when no registration targets the state or the document.
154
+ * per document per wired phase; cost is O(registrations-for-phase × nodes),
155
+ * and it early-outs when no registration targets the phase or the document.
148
156
  */
149
157
  export declare class DefaultAstExtensionService implements AstExtensionService {
150
158
  protected readonly services: ServerLanguageServices;
151
159
  protected readonly extensions: Registry<AstExtension<AstNode>>;
152
160
  /**
153
- * Per-state bucket of registrations targeting each state. Rebuilt lazily
161
+ * Per-phase bucket of registrations targeting each phase. Rebuilt lazily
154
162
  * on the first refresh after a registry mutation — staleness is detected
155
163
  * via reference identity of the registry's cached array (see {@link Registry.all}).
156
164
  */
157
- protected stateBuckets: Map<DocumentState, readonly AstExtension<AstNode>[]>;
158
- protected stateBucketsFor: readonly AstExtension[] | undefined;
165
+ protected phaseBuckets: Map<DocumentState, readonly AstExtension<AstNode>[]>;
166
+ protected phaseBucketsFor: readonly AstExtension[] | undefined;
159
167
  protected readonly tracer: Tracer;
160
168
  constructor(services: ServerLanguageServices, options?: AstExtensionServiceOptions);
161
169
  /**
@@ -170,7 +178,7 @@ export declare class DefaultAstExtensionService implements AstExtensionService {
170
178
  register<T extends AstNode>(extension: AstExtension<T>): Disposable;
171
179
  /**
172
180
  * Production code goes through {@link extendDocument} via the
173
- * `onDocumentPhase(state)` listener wired by `BuildPipelineIntegration`.
181
+ * `onDocumentPhase(phase)` listener wired by `BuildPipelineIntegration`.
174
182
  */
175
183
  extendNode(node: AstNode, document: LangiumDocument, phase: DocumentState): void;
176
184
  /**
@@ -188,13 +196,13 @@ export declare class DefaultAstExtensionService implements AstExtensionService {
188
196
  */
189
197
  extendDocument(document: LangiumDocument, phase: DocumentState, cancelToken?: CancellationToken): void;
190
198
  /**
191
- * Bucket-by-state view of {@link extensions}, rebuilt lazily when the
199
+ * Bucket-by-phase view of {@link extensions}, rebuilt lazily when the
192
200
  * registry's cached `all()` reference changes. Replaces per-call allocation
193
201
  * on the per-node hot path — for a workspace with many documents at the
194
- * same state, the bucket is computed once and reused until the next
202
+ * same phase, the bucket is computed once and reused until the next
195
203
  * register / unregister.
196
204
  */
197
- protected getStateBucket(phase: DocumentState): readonly AstExtension[];
205
+ protected getPhaseBucket(phase: DocumentState): readonly AstExtension[];
198
206
  }
199
207
  /**
200
208
  * Assign a non-enumerable own property — for synthetic children that must NOT
@@ -1 +1 @@
1
- {"version":3,"file":"ast-extension-service.d.ts","sourceRoot":"","sources":["../../../src/langium/ast-extension/ast-extension-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ast-extension-service.d.ts","sourceRoot":"","sources":["../../../src/langium/ast-extension/ast-extension-service.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,24 @@
9
9
  import { Logger } from '@hydranium/protocol';
10
10
  import { AstUtils, DocumentState } from '@hydranium/langium';
11
11
  import { Registry } from '../../util/registry.js';
12
+ export var AstExtensionPhase;
13
+ (function (AstExtensionPhase) {
14
+ /**
15
+ * Every {@link AstExtensionPhase}, in build order. `BuildPipelineIntegration`
16
+ * wires one `onDocumentPhase` listener per entry and `register` refuses a
17
+ * phase outside it, so the two cannot drift apart. A fresh array each call.
18
+ */
19
+ function all() {
20
+ return [
21
+ DocumentState.Parsed,
22
+ DocumentState.ComputedScopes,
23
+ DocumentState.Linked,
24
+ DocumentState.IndexedReferences,
25
+ DocumentState.Validated
26
+ ];
27
+ }
28
+ AstExtensionPhase.all = all;
29
+ })(AstExtensionPhase || (AstExtensionPhase = {}));
12
30
  /**
13
31
  * Default {@link AstExtensionService} implementation. Generic registry for
14
32
  * build-phase AST-node enrichment: computed properties and synthetic
@@ -23,19 +41,19 @@ import { Registry } from '../../util/registry.js';
23
41
  * owns the registry + per-node logic, not the listener lifecycle.
24
42
  *
25
43
  * **Performance:** `extendDocument` shares a single `streamAllContents` walk
26
- * per document per wired phase; cost is O(registrations-for-state × nodes),
27
- * and it early-outs when no registration targets the state or the document.
44
+ * per document per wired phase; cost is O(registrations-for-phase × nodes),
45
+ * and it early-outs when no registration targets the phase or the document.
28
46
  */
29
47
  export class DefaultAstExtensionService {
30
48
  services;
31
49
  extensions = new Registry();
32
50
  /**
33
- * Per-state bucket of registrations targeting each state. Rebuilt lazily
51
+ * Per-phase bucket of registrations targeting each phase. Rebuilt lazily
34
52
  * on the first refresh after a registry mutation — staleness is detected
35
53
  * via reference identity of the registry's cached array (see {@link Registry.all}).
36
54
  */
37
- stateBuckets = new Map();
38
- stateBucketsFor;
55
+ phaseBuckets = new Map();
56
+ phaseBucketsFor;
39
57
  tracer;
40
58
  constructor(services, options = {}) {
41
59
  this.services = services;
@@ -59,16 +77,24 @@ export class DefaultAstExtensionService {
59
77
  * which calls {@link extendDocument} for the wired phases.
60
78
  */
61
79
  register(extension) {
80
+ // A phase outside the wired set type-checks only past a cast, and a
81
+ // registration there is accepted and never fires.
82
+ if (!AstExtensionPhase.all().includes(extension.phase)) {
83
+ throw new Error(`AST extension '${extension.id}' registered at phase ${DocumentState[extension.phase]} would never fire. ` +
84
+ `Allowed phases are ${AstExtensionPhase.all()
85
+ .map(phase => DocumentState[phase])
86
+ .join(', ')}.`);
87
+ }
62
88
  // Cast: TypeScript can't see that `T extends AstNode` makes the extension
63
89
  // assignable to the default-parameter form. The runtime contract is the same.
64
90
  return this.extensions.register(extension);
65
91
  }
66
92
  /**
67
93
  * Production code goes through {@link extendDocument} via the
68
- * `onDocumentPhase(state)` listener wired by `BuildPipelineIntegration`.
94
+ * `onDocumentPhase(phase)` listener wired by `BuildPipelineIntegration`.
69
95
  */
70
96
  extendNode(node, document, phase) {
71
- const bucket = this.getStateBucket(phase);
97
+ const bucket = this.getPhaseBucket(phase);
72
98
  for (const extension of bucket) {
73
99
  if (extension.documentFilter && !extension.documentFilter(document)) {
74
100
  continue;
@@ -96,7 +122,7 @@ export class DefaultAstExtensionService {
96
122
  if (cancelToken?.isCancellationRequested) {
97
123
  return;
98
124
  }
99
- const bucket = this.getStateBucket(phase);
125
+ const bucket = this.getPhaseBucket(phase);
100
126
  if (bucket.length === 0) {
101
127
  return;
102
128
  }
@@ -129,32 +155,32 @@ export class DefaultAstExtensionService {
129
155
  session?.report('debug');
130
156
  }
131
157
  /**
132
- * Bucket-by-state view of {@link extensions}, rebuilt lazily when the
158
+ * Bucket-by-phase view of {@link extensions}, rebuilt lazily when the
133
159
  * registry's cached `all()` reference changes. Replaces per-call allocation
134
160
  * on the per-node hot path — for a workspace with many documents at the
135
- * same state, the bucket is computed once and reused until the next
161
+ * same phase, the bucket is computed once and reused until the next
136
162
  * register / unregister.
137
163
  */
138
- getStateBucket(phase) {
164
+ getPhaseBucket(phase) {
139
165
  const current = this.extensions.all();
140
- if (this.stateBucketsFor !== current) {
166
+ if (this.phaseBucketsFor !== current) {
141
167
  const next = new Map();
142
168
  for (const extension of current) {
143
- let bucket = next.get(extension.state);
169
+ let bucket = next.get(extension.phase);
144
170
  if (!bucket) {
145
171
  bucket = [];
146
- next.set(extension.state, bucket);
172
+ next.set(extension.phase, bucket);
147
173
  }
148
174
  bucket.push(extension);
149
175
  }
150
- this.stateBuckets = next;
151
- this.stateBucketsFor = current;
176
+ this.phaseBuckets = next;
177
+ this.phaseBucketsFor = current;
152
178
  }
153
- return this.stateBuckets.get(phase) ?? EMPTY_STATE_BUCKET;
179
+ return this.phaseBuckets.get(phase) ?? EMPTY_PHASE_BUCKET;
154
180
  }
155
181
  }
156
- /** Shared empty result so `getStateBucket` doesn't allocate per call on the cold path. */
157
- const EMPTY_STATE_BUCKET = Object.freeze([]);
182
+ /** Shared empty result so `getPhaseBucket` doesn't allocate per call on the cold path. */
183
+ const EMPTY_PHASE_BUCKET = Object.freeze([]);
158
184
  /**
159
185
  * Assign a non-enumerable own property — for synthetic children that must NOT
160
186
  * be walked by Langium's `streamContents`.
@@ -1 +1 @@
1
- {"version":3,"file":"ast-extension-service.js","sourceRoot":"","sources":["../../../src/langium/ast-extension/ast-extension-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ast-extension-service.js","sourceRoot":"","sources":["../../../src/langium/ast-extension/ast-extension-service.ts"],"names":[],"mappings":"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"}