@domainlang/language 0.1.20
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/README.md +163 -0
- package/out/ast-augmentation.d.ts +6 -0
- package/out/ast-augmentation.js +2 -0
- package/out/ast-augmentation.js.map +1 -0
- package/out/domain-lang-module.d.ts +57 -0
- package/out/domain-lang-module.js +67 -0
- package/out/domain-lang-module.js.map +1 -0
- package/out/generated/ast.d.ts +759 -0
- package/out/generated/ast.js +556 -0
- package/out/generated/ast.js.map +1 -0
- package/out/generated/grammar.d.ts +6 -0
- package/out/generated/grammar.js +2407 -0
- package/out/generated/grammar.js.map +1 -0
- package/out/generated/module.d.ts +13 -0
- package/out/generated/module.js +21 -0
- package/out/generated/module.js.map +1 -0
- package/out/index.d.ts +16 -0
- package/out/index.js +22 -0
- package/out/index.js.map +1 -0
- package/out/lsp/domain-lang-code-actions.d.ts +55 -0
- package/out/lsp/domain-lang-code-actions.js +143 -0
- package/out/lsp/domain-lang-code-actions.js.map +1 -0
- package/out/lsp/domain-lang-completion.d.ts +37 -0
- package/out/lsp/domain-lang-completion.js +452 -0
- package/out/lsp/domain-lang-completion.js.map +1 -0
- package/out/lsp/domain-lang-formatter.d.ts +15 -0
- package/out/lsp/domain-lang-formatter.js +43 -0
- package/out/lsp/domain-lang-formatter.js.map +1 -0
- package/out/lsp/domain-lang-naming.d.ts +34 -0
- package/out/lsp/domain-lang-naming.js +49 -0
- package/out/lsp/domain-lang-naming.js.map +1 -0
- package/out/lsp/domain-lang-scope.d.ts +59 -0
- package/out/lsp/domain-lang-scope.js +102 -0
- package/out/lsp/domain-lang-scope.js.map +1 -0
- package/out/lsp/domain-lang-workspace-manager.d.ts +21 -0
- package/out/lsp/domain-lang-workspace-manager.js +93 -0
- package/out/lsp/domain-lang-workspace-manager.js.map +1 -0
- package/out/lsp/hover/ddd-pattern-explanations.d.ts +50 -0
- package/out/lsp/hover/ddd-pattern-explanations.js +196 -0
- package/out/lsp/hover/ddd-pattern-explanations.js.map +1 -0
- package/out/lsp/hover/domain-lang-hover.d.ts +19 -0
- package/out/lsp/hover/domain-lang-hover.js +302 -0
- package/out/lsp/hover/domain-lang-hover.js.map +1 -0
- package/out/lsp/hover/domain-lang-keywords.d.ts +13 -0
- package/out/lsp/hover/domain-lang-keywords.js +47 -0
- package/out/lsp/hover/domain-lang-keywords.js.map +1 -0
- package/out/lsp/manifest-diagnostics.d.ts +82 -0
- package/out/lsp/manifest-diagnostics.js +230 -0
- package/out/lsp/manifest-diagnostics.js.map +1 -0
- package/out/main-browser.d.ts +1 -0
- package/out/main-browser.js +11 -0
- package/out/main-browser.js.map +1 -0
- package/out/main.d.ts +1 -0
- package/out/main.js +74 -0
- package/out/main.js.map +1 -0
- package/out/sdk/ast-augmentation.d.ts +136 -0
- package/out/sdk/ast-augmentation.js +62 -0
- package/out/sdk/ast-augmentation.js.map +1 -0
- package/out/sdk/index.d.ts +94 -0
- package/out/sdk/index.js +97 -0
- package/out/sdk/index.js.map +1 -0
- package/out/sdk/indexes.d.ts +16 -0
- package/out/sdk/indexes.js +97 -0
- package/out/sdk/indexes.js.map +1 -0
- package/out/sdk/loader-node.d.ts +51 -0
- package/out/sdk/loader-node.js +119 -0
- package/out/sdk/loader-node.js.map +1 -0
- package/out/sdk/loader.d.ts +49 -0
- package/out/sdk/loader.js +85 -0
- package/out/sdk/loader.js.map +1 -0
- package/out/sdk/patterns.d.ts +93 -0
- package/out/sdk/patterns.js +123 -0
- package/out/sdk/patterns.js.map +1 -0
- package/out/sdk/query.d.ts +90 -0
- package/out/sdk/query.js +679 -0
- package/out/sdk/query.js.map +1 -0
- package/out/sdk/resolution.d.ts +52 -0
- package/out/sdk/resolution.js +68 -0
- package/out/sdk/resolution.js.map +1 -0
- package/out/sdk/types.d.ts +280 -0
- package/out/sdk/types.js +8 -0
- package/out/sdk/types.js.map +1 -0
- package/out/services/dependency-analyzer.d.ts +58 -0
- package/out/services/dependency-analyzer.js +254 -0
- package/out/services/dependency-analyzer.js.map +1 -0
- package/out/services/dependency-resolver.d.ts +146 -0
- package/out/services/dependency-resolver.js +452 -0
- package/out/services/dependency-resolver.js.map +1 -0
- package/out/services/git-url-resolver.browser.d.ts +10 -0
- package/out/services/git-url-resolver.browser.js +19 -0
- package/out/services/git-url-resolver.browser.js.map +1 -0
- package/out/services/git-url-resolver.d.ts +158 -0
- package/out/services/git-url-resolver.js +416 -0
- package/out/services/git-url-resolver.js.map +1 -0
- package/out/services/governance-validator.d.ts +44 -0
- package/out/services/governance-validator.js +153 -0
- package/out/services/governance-validator.js.map +1 -0
- package/out/services/import-resolver.d.ts +77 -0
- package/out/services/import-resolver.js +240 -0
- package/out/services/import-resolver.js.map +1 -0
- package/out/services/performance-optimizer.d.ts +60 -0
- package/out/services/performance-optimizer.js +140 -0
- package/out/services/performance-optimizer.js.map +1 -0
- package/out/services/relationship-inference.d.ts +11 -0
- package/out/services/relationship-inference.js +98 -0
- package/out/services/relationship-inference.js.map +1 -0
- package/out/services/semver.d.ts +98 -0
- package/out/services/semver.js +195 -0
- package/out/services/semver.js.map +1 -0
- package/out/services/types.d.ts +340 -0
- package/out/services/types.js +46 -0
- package/out/services/types.js.map +1 -0
- package/out/services/workspace-manager.d.ts +123 -0
- package/out/services/workspace-manager.js +489 -0
- package/out/services/workspace-manager.js.map +1 -0
- package/out/syntaxes/domain-lang.monarch.d.ts +76 -0
- package/out/syntaxes/domain-lang.monarch.js +29 -0
- package/out/syntaxes/domain-lang.monarch.js.map +1 -0
- package/out/utils/import-utils.d.ts +49 -0
- package/out/utils/import-utils.js +128 -0
- package/out/utils/import-utils.js.map +1 -0
- package/out/validation/bounded-context.d.ts +11 -0
- package/out/validation/bounded-context.js +79 -0
- package/out/validation/bounded-context.js.map +1 -0
- package/out/validation/classification.d.ts +3 -0
- package/out/validation/classification.js +3 -0
- package/out/validation/classification.js.map +1 -0
- package/out/validation/constants.d.ts +180 -0
- package/out/validation/constants.js +235 -0
- package/out/validation/constants.js.map +1 -0
- package/out/validation/domain-lang-validator.d.ts +2 -0
- package/out/validation/domain-lang-validator.js +27 -0
- package/out/validation/domain-lang-validator.js.map +1 -0
- package/out/validation/domain.d.ts +11 -0
- package/out/validation/domain.js +63 -0
- package/out/validation/domain.js.map +1 -0
- package/out/validation/import.d.ts +68 -0
- package/out/validation/import.js +237 -0
- package/out/validation/import.js.map +1 -0
- package/out/validation/manifest.d.ts +144 -0
- package/out/validation/manifest.js +327 -0
- package/out/validation/manifest.js.map +1 -0
- package/out/validation/maps.d.ts +21 -0
- package/out/validation/maps.js +60 -0
- package/out/validation/maps.js.map +1 -0
- package/out/validation/metadata.d.ts +7 -0
- package/out/validation/metadata.js +16 -0
- package/out/validation/metadata.js.map +1 -0
- package/out/validation/model.d.ts +12 -0
- package/out/validation/model.js +29 -0
- package/out/validation/model.js.map +1 -0
- package/out/validation/relationships.d.ts +12 -0
- package/out/validation/relationships.js +94 -0
- package/out/validation/relationships.js.map +1 -0
- package/out/validation/shared.d.ts +6 -0
- package/out/validation/shared.js +12 -0
- package/out/validation/shared.js.map +1 -0
- package/package.json +110 -0
- package/src/ast-augmentation.ts +5 -0
- package/src/domain-lang-module.ts +112 -0
- package/src/domain-lang.langium +351 -0
- package/src/generated/ast.ts +986 -0
- package/src/generated/grammar.ts +2409 -0
- package/src/generated/module.ts +25 -0
- package/src/index.ts +24 -0
- package/src/lsp/domain-lang-code-actions.ts +189 -0
- package/src/lsp/domain-lang-completion.ts +514 -0
- package/src/lsp/domain-lang-formatter.ts +51 -0
- package/src/lsp/domain-lang-naming.ts +56 -0
- package/src/lsp/domain-lang-scope.ts +137 -0
- package/src/lsp/domain-lang-workspace-manager.ts +104 -0
- package/src/lsp/hover/ddd-pattern-explanations.ts +237 -0
- package/src/lsp/hover/domain-lang-hover.ts +338 -0
- package/src/lsp/hover/domain-lang-keywords.ts +50 -0
- package/src/lsp/manifest-diagnostics.ts +290 -0
- package/src/main-browser.ts +15 -0
- package/src/main.ts +85 -0
- package/src/sdk/README.md +297 -0
- package/src/sdk/ast-augmentation.ts +157 -0
- package/src/sdk/index.ts +126 -0
- package/src/sdk/indexes.ts +155 -0
- package/src/sdk/loader-node.ts +146 -0
- package/src/sdk/loader.ts +99 -0
- package/src/sdk/patterns.ts +147 -0
- package/src/sdk/query.ts +802 -0
- package/src/sdk/resolution.ts +78 -0
- package/src/sdk/types.ts +323 -0
- package/src/services/dependency-analyzer.ts +321 -0
- package/src/services/dependency-resolver.ts +551 -0
- package/src/services/git-url-resolver.browser.ts +26 -0
- package/src/services/git-url-resolver.ts +517 -0
- package/src/services/governance-validator.ts +177 -0
- package/src/services/import-resolver.ts +292 -0
- package/src/services/performance-optimizer.ts +170 -0
- package/src/services/relationship-inference.ts +121 -0
- package/src/services/semver.ts +213 -0
- package/src/services/types.ts +415 -0
- package/src/services/workspace-manager.ts +607 -0
- package/src/syntaxes/domain-lang.monarch.ts +29 -0
- package/src/utils/import-utils.ts +152 -0
- package/src/validation/bounded-context.ts +99 -0
- package/src/validation/classification.ts +5 -0
- package/src/validation/constants.ts +304 -0
- package/src/validation/domain-lang-validator.ts +33 -0
- package/src/validation/domain.ts +77 -0
- package/src/validation/import.ts +295 -0
- package/src/validation/manifest.ts +439 -0
- package/src/validation/maps.ts +76 -0
- package/src/validation/metadata.ts +18 -0
- package/src/validation/model.ts +37 -0
- package/src/validation/relationships.ts +154 -0
- package/src/validation/shared.ts +14 -0
package/src/main.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { startLanguageServer } from 'langium/lsp';
|
|
2
|
+
import { NodeFileSystem } from 'langium/node';
|
|
3
|
+
import { createConnection, ProposedFeatures } from 'vscode-languageserver/node.js';
|
|
4
|
+
import { createDomainLangServices } from './domain-lang-module.js';
|
|
5
|
+
import { ensureImportGraphFromEntryFile } from './utils/import-utils.js';
|
|
6
|
+
import { URI } from 'langium';
|
|
7
|
+
|
|
8
|
+
// Create a connection to the client
|
|
9
|
+
const connection = createConnection(ProposedFeatures.all);
|
|
10
|
+
|
|
11
|
+
// Inject the shared services and language-specific services
|
|
12
|
+
const { shared, DomainLang } = createDomainLangServices({ connection, ...NodeFileSystem });
|
|
13
|
+
|
|
14
|
+
// Initialize workspace on connection
|
|
15
|
+
connection.onInitialize(async (params) => {
|
|
16
|
+
const workspaceRoot = params.rootUri ? URI.parse(params.rootUri).fsPath : undefined;
|
|
17
|
+
|
|
18
|
+
if (workspaceRoot) {
|
|
19
|
+
try {
|
|
20
|
+
// Initialize workspace manager
|
|
21
|
+
const workspaceManager = DomainLang.imports.WorkspaceManager;
|
|
22
|
+
await workspaceManager.initialize(workspaceRoot);
|
|
23
|
+
console.warn(`DomainLang workspace initialized: ${workspaceRoot}`);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
26
|
+
console.warn(`Failed to initialize workspace: ${message}`);
|
|
27
|
+
// Continue without workspace - local imports will still work
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
capabilities: {
|
|
33
|
+
// Language server capabilities are configured by Langium
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Optionally start from a single entry file and follow imports.
|
|
39
|
+
// Configure via env DOMAINLANG_ENTRY (absolute or workspace-relative path)
|
|
40
|
+
const entryFile = process.env.DOMAINLANG_ENTRY;
|
|
41
|
+
if (entryFile) {
|
|
42
|
+
let currentGraph = new Set<string>();
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Reloads the import graph from the entry file.
|
|
46
|
+
* Handles errors gracefully and notifies the LSP client.
|
|
47
|
+
*/
|
|
48
|
+
const reloadFromEntry = async (): Promise<void> => {
|
|
49
|
+
try {
|
|
50
|
+
currentGraph = await ensureImportGraphFromEntryFile(
|
|
51
|
+
entryFile,
|
|
52
|
+
shared.workspace.LangiumDocuments
|
|
53
|
+
);
|
|
54
|
+
console.warn(`Successfully loaded import graph from ${entryFile}`);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
57
|
+
console.error(`Failed to preload import graph from ${entryFile}: ${message}`);
|
|
58
|
+
// Notify LSP client of the error
|
|
59
|
+
connection.console.error(
|
|
60
|
+
`DomainLang: Could not load entry file ${entryFile}. Error: ${message}`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Initial load from entry file, then start the server
|
|
66
|
+
reloadFromEntry().finally(() => startLanguageServer(shared));
|
|
67
|
+
|
|
68
|
+
// Any change within the loaded graph should trigger a reload from the entry
|
|
69
|
+
shared.workspace.TextDocuments.onDidChangeContent(async (event) => {
|
|
70
|
+
const changed = event.document.uri;
|
|
71
|
+
if (currentGraph.has(changed)) {
|
|
72
|
+
await reloadFromEntry();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// If the entry file itself is opened/changed, also reload
|
|
77
|
+
shared.workspace.TextDocuments.onDidOpen(async (event) => {
|
|
78
|
+
if (URI.parse(event.document.uri).fsPath === URI.file(entryFile).fsPath) {
|
|
79
|
+
await reloadFromEntry();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
} else {
|
|
83
|
+
// No entry file configured: start normally
|
|
84
|
+
startLanguageServer(shared);
|
|
85
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
# Model Query SDK
|
|
2
|
+
|
|
3
|
+
Fluent, type-safe query API for DomainLang models.
|
|
4
|
+
|
|
5
|
+
## SDK Architecture
|
|
6
|
+
|
|
7
|
+
The SDK is **read-only and query-focused**. It provides:
|
|
8
|
+
|
|
9
|
+
- Augmented AST properties (`effectiveClassification`, `effectiveTeam`, etc.)
|
|
10
|
+
- Fluent query chains with lazy iteration
|
|
11
|
+
- O(1) indexed lookups by FQN/name
|
|
12
|
+
|
|
13
|
+
The SDK does **NOT** manage:
|
|
14
|
+
|
|
15
|
+
- Workspace lifecycle (LSP/WorkspaceManager handles this)
|
|
16
|
+
- File watching or change detection (LSP handles this)
|
|
17
|
+
- Cross-file import resolution (DocumentBuilder handles this)
|
|
18
|
+
|
|
19
|
+
## Entry Points by Deployment Target
|
|
20
|
+
|
|
21
|
+
| Target | Entry Point | Browser-Safe | Notes |
|
|
22
|
+
| --- | --- | --- | --- |
|
|
23
|
+
| VS Code Extension | `fromDocument()` | ✅ | Zero-copy LSP integration |
|
|
24
|
+
| Web Editor | `fromDocument()`, `loadModelFromText()` | ✅ | Browser-compatible |
|
|
25
|
+
| CLI (Node.js) | `loadModel()` | ❌ | Requires `sdk/loader-node` |
|
|
26
|
+
| Hosted LSP | `fromDocument()`, `fromServices()` | ✅ | Server-side |
|
|
27
|
+
| Testing | `loadModelFromText()` | ✅ | In-memory parsing |
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
The SDK is bundled with the `domain-lang-language` package:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
// Browser-safe imports (VS Code, web editor, testing)
|
|
35
|
+
import { loadModelFromText, fromDocument, fromModel } from 'domain-lang-language/sdk';
|
|
36
|
+
|
|
37
|
+
// Node.js CLI only (NOT browser-safe)
|
|
38
|
+
import { loadModel } from 'domain-lang-language/sdk/loader-node';
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Entry Points
|
|
42
|
+
|
|
43
|
+
### For Node.js CLI, Scripts
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// Note: This import requires Node.js filesystem
|
|
47
|
+
import { loadModel } from 'domain-lang-language/sdk/loader-node';
|
|
48
|
+
|
|
49
|
+
// Load from file (async)
|
|
50
|
+
const { query, model } = await loadModel('./domains.dlang', {
|
|
51
|
+
workspaceDir: process.cwd()
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### For Testing, REPL, Web Editor
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { loadModelFromText } from 'domain-lang-language/sdk';
|
|
59
|
+
|
|
60
|
+
// Load from string (async, browser-safe)
|
|
61
|
+
const { query } = await loadModelFromText(`
|
|
62
|
+
Domain Sales { vision: "Handle sales operations" }
|
|
63
|
+
bc OrderContext for Sales as Core
|
|
64
|
+
`);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### For LSP, Validation (Zero-Copy)
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { fromModel, fromDocument, fromServices } from 'domain-lang-language/sdk';
|
|
71
|
+
|
|
72
|
+
// From Model node (sync)
|
|
73
|
+
const query = fromModel(model);
|
|
74
|
+
|
|
75
|
+
// From LangiumDocument (sync)
|
|
76
|
+
const query = fromDocument(document);
|
|
77
|
+
|
|
78
|
+
// From services + URI (sync)
|
|
79
|
+
const query = fromServices(services, documentUri);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Query API
|
|
83
|
+
|
|
84
|
+
### Collections
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// All bounded contexts
|
|
88
|
+
query.boundedContexts()
|
|
89
|
+
|
|
90
|
+
// All domains
|
|
91
|
+
query.domains()
|
|
92
|
+
|
|
93
|
+
// All teams
|
|
94
|
+
query.teams()
|
|
95
|
+
|
|
96
|
+
// All classifications
|
|
97
|
+
query.classifications()
|
|
98
|
+
|
|
99
|
+
// All relationships
|
|
100
|
+
query.relationships()
|
|
101
|
+
|
|
102
|
+
// All context maps
|
|
103
|
+
query.contextMaps()
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Fluent Chaining
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// Filter by strategic classification
|
|
110
|
+
query.boundedContexts()
|
|
111
|
+
.withClassification('Core')
|
|
112
|
+
.withTeam('PaymentTeam')
|
|
113
|
+
.toArray()
|
|
114
|
+
|
|
115
|
+
// Filter by domain
|
|
116
|
+
query.boundedContexts()
|
|
117
|
+
.inDomain('Sales')
|
|
118
|
+
.withMetadata('Language', 'TypeScript')
|
|
119
|
+
|
|
120
|
+
// Custom predicates
|
|
121
|
+
query.domains()
|
|
122
|
+
.where(d => d.parent !== undefined)
|
|
123
|
+
.where(d => d.type?.ref?.name === 'Core') // Direct reference access
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Direct Lookups (O(1))
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
// By FQN
|
|
130
|
+
const bc = query.byFqn<BoundedContext>('Sales.OrderContext');
|
|
131
|
+
|
|
132
|
+
// By simple name
|
|
133
|
+
const domain = query.domain('Sales');
|
|
134
|
+
const context = query.boundedContext('OrderContext');
|
|
135
|
+
|
|
136
|
+
// Get FQN
|
|
137
|
+
const fqn = query.fqn(bc);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## SDK-Augmented Properties
|
|
141
|
+
|
|
142
|
+
The SDK augments AST nodes **only for properties that add value beyond direct access**:
|
|
143
|
+
|
|
144
|
+
### BoundedContext
|
|
145
|
+
|
|
146
|
+
**Augmented (precedence resolution, transformation, computed):**
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
bc.effectiveClassification // Inline header (`as`) → body (`classification:`) precedence
|
|
150
|
+
bc.effectiveTeam // Inline header (`by`) → body (`team:`) precedence
|
|
151
|
+
bc.metadataMap // Metadata entries as ReadonlyMap<string, string>
|
|
152
|
+
bc.fqn // Computed fully qualified name
|
|
153
|
+
bc.hasClassification('Core') // Check if classification matches
|
|
154
|
+
bc.hasTeam('SalesTeam') // Check if team matches
|
|
155
|
+
bc.hasMetadata('Lang') // Check if metadata key exists
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Direct AST access (no augmentation needed):**
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
bc.description // Direct string property
|
|
162
|
+
bc.businessModel?.ref // Direct reference to Classification
|
|
163
|
+
bc.evolution?.ref // Direct reference to Classification
|
|
164
|
+
bc.archetype?.ref // Direct reference to Classification
|
|
165
|
+
bc.relationships // Direct array of Relationship
|
|
166
|
+
bc.terminology // Direct array of DomainTerm
|
|
167
|
+
bc.decisions // Direct array of AbstractDecision
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Domain
|
|
171
|
+
|
|
172
|
+
**Augmented (computed):**
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
domain.fqn // Computed fully qualified name
|
|
176
|
+
domain.hasType('Core') // Check type matches
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Direct AST access (no augmentation needed):**
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
domain.description // Direct string property
|
|
183
|
+
domain.vision // Direct string property
|
|
184
|
+
domain.type?.ref // Direct reference to Classification
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Examples
|
|
188
|
+
|
|
189
|
+
### Find All Core Contexts with C# Stack
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
const { query } = await loadModel('./banking.dlang');
|
|
193
|
+
|
|
194
|
+
const results = query.boundedContexts()
|
|
195
|
+
.withClassification('Core')
|
|
196
|
+
.withMetadata('Language', 'CSharp')
|
|
197
|
+
.toArray();
|
|
198
|
+
|
|
199
|
+
for (const bc of results) {
|
|
200
|
+
console.log(`${bc.fqn}: ${bc.description ?? 'n/a'}`);
|
|
201
|
+
console.log(` Team: ${bc.effectiveTeam?.name ?? 'unassigned'}`);
|
|
202
|
+
console.log(` Evolution: ${bc.evolution?.ref?.name ?? 'n/a'}`);
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### List All Relationships for a Domain
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
const sales = query.domain('Sales');
|
|
210
|
+
|
|
211
|
+
const relationships = query.relationships()
|
|
212
|
+
.where(rel =>
|
|
213
|
+
rel.left.domain?.ref?.name === 'Sales' ||
|
|
214
|
+
rel.right.domain?.ref?.name === 'Sales'
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
for (const rel of relationships) {
|
|
218
|
+
const type = rel.type ?? rel.inferredType ?? 'unknown';
|
|
219
|
+
console.log(`${rel.left.name} ${rel.arrow} ${rel.right.name} [${type}]`);
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
### Extract Documentation
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
const docs = query.boundedContexts()
|
|
228
|
+
.where(bc => bc.description !== undefined)
|
|
229
|
+
.toArray()
|
|
230
|
+
.map(bc => ({
|
|
231
|
+
fqn: bc.fqn,
|
|
232
|
+
description: bc.description,
|
|
233
|
+
team: bc.effectiveTeam?.name,
|
|
234
|
+
metadata: Object.fromEntries(bc.metadataMap),
|
|
235
|
+
}));
|
|
236
|
+
|
|
237
|
+
console.log(JSON.stringify(docs, null, 2));
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Resolution Rules
|
|
241
|
+
|
|
242
|
+
The SDK provides precedence resolution **only for properties with multiple assignment locations**:
|
|
243
|
+
|
|
244
|
+
| Augmented Property | Precedence | Why Augmented |
|
|
245
|
+
| --- | --- | --- |
|
|
246
|
+
| `bc.effectiveClassification` | Header inline (`as`) → body (`classification:`) | Array-based precedence |
|
|
247
|
+
| `bc.effectiveTeam` | Header inline (`by`) → body (`team:`) | Array-based precedence |
|
|
248
|
+
| `bc.metadataMap` | Later entries override earlier | Array to Map conversion |
|
|
249
|
+
|
|
250
|
+
**Direct access (no precedence needed):**
|
|
251
|
+
|
|
252
|
+
| Property | Access Pattern | Why Direct |
|
|
253
|
+
| --- | --- | --- |
|
|
254
|
+
| `bc.description` | Direct | Single location |
|
|
255
|
+
| `bc.businessModel?.ref` | Direct reference | Single location |
|
|
256
|
+
| `bc.evolution?.ref` | Direct reference | Single location |
|
|
257
|
+
| `bc.archetype?.ref` | Direct reference | Single location |
|
|
258
|
+
| `domain.description` | Direct | Single location |
|
|
259
|
+
| `domain.vision` | Direct | Single location |
|
|
260
|
+
| `domain.type?.ref` | Direct reference | Single location |
|
|
261
|
+
|
|
262
|
+
## Performance
|
|
263
|
+
|
|
264
|
+
- **O(1) lookups**: `byFqn()`, indexed team/classification/metadata filters
|
|
265
|
+
- **Lazy iteration**: QueryBuilder chains don't materialize until consumed
|
|
266
|
+
- **Zero-copy**: `fromModel()` / `fromDocument()` reuse existing AST
|
|
267
|
+
|
|
268
|
+
## Architecture
|
|
269
|
+
|
|
270
|
+
The SDK is an **internal module** within the language package:
|
|
271
|
+
|
|
272
|
+
```text
|
|
273
|
+
packages/language/src/sdk/
|
|
274
|
+
├── index.ts # Public API
|
|
275
|
+
├── types.ts # Type definitions
|
|
276
|
+
├── query.ts # Query & QueryBuilder
|
|
277
|
+
├── resolution.ts # Property resolution (internal)
|
|
278
|
+
├── indexes.ts # Index building (internal)
|
|
279
|
+
└── loader.ts # loadModel functions (internal)
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Internal vs Public
|
|
283
|
+
|
|
284
|
+
**Public** (exported from `sdk/index.ts`):
|
|
285
|
+
|
|
286
|
+
- `loadModel`, `loadModelFromText`
|
|
287
|
+
- `fromModel`, `fromDocument`, `fromServices`
|
|
288
|
+
- `Query`, `QueryBuilder`, `BcQueryBuilder`
|
|
289
|
+
- Type definitions
|
|
290
|
+
|
|
291
|
+
**Internal** (not exported):
|
|
292
|
+
|
|
293
|
+
- `effectiveClassification`, `effectiveTeam`, `metadataAsMap`, etc.
|
|
294
|
+
- `buildIndexes`, `buildFqnIndex`, etc.
|
|
295
|
+
- Implementation classes
|
|
296
|
+
|
|
297
|
+
Use SDK-augmented properties (`bc.effectiveClassification`) instead of calling resolution functions directly.
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK AST Augmentation - Module augmentation for native SDK properties.
|
|
3
|
+
*
|
|
4
|
+
* This file uses TypeScript's declaration merging to add computed helpers
|
|
5
|
+
* directly to the AST types. After importing this module, BoundedContext,
|
|
6
|
+
* Domain, and Relationship instances expose convenience getters.
|
|
7
|
+
*
|
|
8
|
+
* **Only properties that add value beyond direct AST access are augmented:**
|
|
9
|
+
* - Precedence resolution: `effectiveClassification`, `effectiveTeam` (for array-based properties)
|
|
10
|
+
* - Data transformation: `metadataMap` (array to Map)
|
|
11
|
+
* - Computed values: `fqn` (fully qualified name)
|
|
12
|
+
* - Helper methods: `hasClassification()`, `hasTeam()`, `hasMetadata()`, `hasType()`
|
|
13
|
+
*
|
|
14
|
+
* **Direct AST access (no augmentation needed):**
|
|
15
|
+
* - `bc.description` - Direct string property
|
|
16
|
+
* - `bc.businessModel?.ref` - Direct reference
|
|
17
|
+
* - `bc.evolution?.ref` - Direct reference
|
|
18
|
+
* - `bc.archetype?.ref` - Direct reference
|
|
19
|
+
* - `domain.description` - Direct string property
|
|
20
|
+
* - `domain.vision` - Direct string property
|
|
21
|
+
* - `domain.type?.ref` - Direct reference
|
|
22
|
+
*
|
|
23
|
+
* **Usage:**
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import '../sdk/ast-augmentation.js'; // Enable augmented properties
|
|
26
|
+
* import { fromDocument } from '../sdk/index.js';
|
|
27
|
+
*
|
|
28
|
+
* fromDocument(document); // Ensures augmentation
|
|
29
|
+
* const bc: BoundedContext = ...;
|
|
30
|
+
* console.log(bc.effectiveClassification?.name);
|
|
31
|
+
* console.log(bc.metadataMap.get('Language'));
|
|
32
|
+
* // Direct access for simple properties:
|
|
33
|
+
* console.log(bc.businessModel?.ref?.name);
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* **Properties added to BoundedContext:**
|
|
37
|
+
* - `effectiveClassification` - Inline `as` header → body `classification:`
|
|
38
|
+
* - `effectiveTeam` - Inline `team` header → body `team:`
|
|
39
|
+
* - `metadataMap` - Metadata entries exposed as a ReadonlyMap
|
|
40
|
+
* - `fqn` - Computed fully qualified name
|
|
41
|
+
* - `hasClassification(name)` - Check if classification matches
|
|
42
|
+
* - `hasTeam(name)` - Check if team matches
|
|
43
|
+
* - `hasMetadata(key, value?)` - Check metadata
|
|
44
|
+
*
|
|
45
|
+
* **Properties added to Domain:**
|
|
46
|
+
* - `fqn` - Computed fully qualified name
|
|
47
|
+
* - `hasType(name)` - Check type matches
|
|
48
|
+
*
|
|
49
|
+
* **Properties added to Relationship:**
|
|
50
|
+
* - `hasPattern(pattern)` - Check if pattern exists on either side
|
|
51
|
+
* - `hasLeftPattern(pattern)` - Check left patterns
|
|
52
|
+
* - `hasRightPattern(pattern)` - Check right patterns
|
|
53
|
+
* - `isUpstream(side)` - Check if side is upstream
|
|
54
|
+
* - `isDownstream(side)` - Check if side is downstream
|
|
55
|
+
* - `isBidirectional` - Check if relationship is bidirectional
|
|
56
|
+
* - `leftContextName` - Resolved name of left context
|
|
57
|
+
* - `rightContextName` - Resolved name of right context
|
|
58
|
+
*
|
|
59
|
+
* @module sdk/ast-augmentation
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
// Note: Types are referenced directly via import('...').TypeName in the declare module
|
|
63
|
+
// to avoid importing and re-exporting type-only modules that cause compilation issues
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Module augmentation for native SDK properties on AST types.
|
|
67
|
+
*/
|
|
68
|
+
declare module '../generated/ast.js' {
|
|
69
|
+
/**
|
|
70
|
+
* Augmented BoundedContext with SDK-computed properties.
|
|
71
|
+
*
|
|
72
|
+
* Note: description, businessModel, evolution, archetype, metadata, relationships,
|
|
73
|
+
* terminology, and decisions are direct AST properties - use them directly.
|
|
74
|
+
*/
|
|
75
|
+
interface BoundedContext {
|
|
76
|
+
/** Effective classification with inline precedence (header `as` > body `classification:`) */
|
|
77
|
+
readonly effectiveClassification: import('../generated/ast.js').Classification | undefined;
|
|
78
|
+
|
|
79
|
+
/** Effective team with inline precedence (header `by` > body `team:`) */
|
|
80
|
+
readonly effectiveTeam: import('../generated/ast.js').Team | undefined;
|
|
81
|
+
|
|
82
|
+
/** Metadata entries exposed as a map for O(1) lookups */
|
|
83
|
+
readonly metadataMap: ReadonlyMap<string, string>;
|
|
84
|
+
|
|
85
|
+
/** SDK-computed fully qualified name */
|
|
86
|
+
readonly fqn: string;
|
|
87
|
+
|
|
88
|
+
/** Checks if this bounded context has the specified classification. */
|
|
89
|
+
hasClassification(name: string | import('../generated/ast.js').Classification): boolean;
|
|
90
|
+
|
|
91
|
+
/** Checks if this bounded context is owned by the specified team. */
|
|
92
|
+
hasTeam(name: string | import('../generated/ast.js').Team): boolean;
|
|
93
|
+
|
|
94
|
+
/** Checks if this bounded context has metadata with the given key (and optionally value). */
|
|
95
|
+
hasMetadata(key: string, value?: string): boolean;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Augmented Domain with SDK-computed properties.
|
|
100
|
+
*
|
|
101
|
+
* Note: description, vision, and type are direct AST properties - use them directly.
|
|
102
|
+
*/
|
|
103
|
+
interface Domain {
|
|
104
|
+
/** SDK-computed fully qualified name */
|
|
105
|
+
readonly fqn: string;
|
|
106
|
+
|
|
107
|
+
/** Checks if this domain has the specified type. */
|
|
108
|
+
hasType(name: string | import('../generated/ast.js').Classification): boolean;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Augmented Relationship with SDK helper methods.
|
|
113
|
+
*/
|
|
114
|
+
interface Relationship {
|
|
115
|
+
/** Resolved name of left context (handles 'this') */
|
|
116
|
+
readonly leftContextName: string;
|
|
117
|
+
/** Resolved name of right context (handles 'this') */
|
|
118
|
+
readonly rightContextName: string;
|
|
119
|
+
/** Whether this is a bidirectional relationship (<->) */
|
|
120
|
+
readonly isBidirectional: boolean;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Checks if the relationship has a specific integration pattern on either side.
|
|
124
|
+
* Accepts both abbreviations (SK, ACL) and full names (SharedKernel, AntiCorruptionLayer).
|
|
125
|
+
* @param pattern - Pattern abbreviation or full name
|
|
126
|
+
*/
|
|
127
|
+
hasPattern(pattern: import('./patterns.js').IntegrationPattern | string): boolean;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Checks if the left side has a specific integration pattern.
|
|
131
|
+
* @param pattern - Pattern abbreviation or full name
|
|
132
|
+
*/
|
|
133
|
+
hasLeftPattern(pattern: import('./patterns.js').IntegrationPattern | string): boolean;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Checks if the right side has a specific integration pattern.
|
|
137
|
+
* @param pattern - Pattern abbreviation or full name
|
|
138
|
+
*/
|
|
139
|
+
hasRightPattern(pattern: import('./patterns.js').IntegrationPattern | string): boolean;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Checks if the specified side is upstream (provider) in this relationship.
|
|
143
|
+
* @param side - 'left' or 'right'
|
|
144
|
+
*/
|
|
145
|
+
isUpstream(side: 'left' | 'right'): boolean;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Checks if the specified side is downstream (consumer) in this relationship.
|
|
149
|
+
* @param side - 'left' or 'right'
|
|
150
|
+
*/
|
|
151
|
+
isDownstream(side: 'left' | 'right'): boolean;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Export nothing - this file is purely for type augmentation
|
|
156
|
+
// The actual implementation is in query.ts via Object.defineProperties
|
|
157
|
+
export {};
|
package/src/sdk/index.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model Query SDK - Public API
|
|
3
|
+
*
|
|
4
|
+
* Provides fluent, type-safe query operations on DomainLang models.
|
|
5
|
+
*
|
|
6
|
+
* ## Architecture: SDK vs LSP Responsibilities
|
|
7
|
+
*
|
|
8
|
+
* The SDK is **read-only and query-focused**. It does NOT manage:
|
|
9
|
+
* - Workspace lifecycle (LSP/WorkspaceManager handles this)
|
|
10
|
+
* - File watching or change detection (LSP/TextDocuments handles this)
|
|
11
|
+
* - Cross-file import resolution (LSP/DocumentBuilder handles this)
|
|
12
|
+
* - Document validation scheduling (LSP handles this)
|
|
13
|
+
*
|
|
14
|
+
* **SDK responsibilities:**
|
|
15
|
+
* - Augmented AST properties (transparent property access with precedence rules)
|
|
16
|
+
* - Fluent query chains with lazy iteration
|
|
17
|
+
* - O(1) indexed lookups by FQN/name
|
|
18
|
+
* - Resolution rules (which block wins for 0..1 properties)
|
|
19
|
+
*
|
|
20
|
+
* **Entry points for different deployment targets:**
|
|
21
|
+
*
|
|
22
|
+
* | Target | Entry Point | Browser-Safe | Notes |
|
|
23
|
+
* |--------|-------------|--------------|-------|
|
|
24
|
+
* | VS Code Extension | `fromDocument()` | ✅ | Zero-copy LSP integration |
|
|
25
|
+
* | Web Editor | `fromDocument()`, `loadModelFromText()` | ✅ | Browser-compatible |
|
|
26
|
+
* | CLI (Node.js) | `loadModel()` from `sdk/loader-node` | ❌ | File system access |
|
|
27
|
+
* | Hosted LSP | `fromDocument()`, `fromServices()` | ✅ | Server-side only |
|
|
28
|
+
* | Testing | `loadModelFromText()` | ✅ | In-memory parsing |
|
|
29
|
+
*
|
|
30
|
+
* ## Browser vs Node.js
|
|
31
|
+
*
|
|
32
|
+
* This module (`sdk/index`) is **browser-safe** and exports only:
|
|
33
|
+
* - `loadModelFromText()` - uses EmptyFileSystem
|
|
34
|
+
* - `fromModel()`, `fromDocument()`, `fromServices()` - zero-copy wrappers
|
|
35
|
+
*
|
|
36
|
+
* For file-based loading in Node.js CLI tools:
|
|
37
|
+
* ```typescript
|
|
38
|
+
* import { loadModel } from 'domain-lang-language/sdk/loader-node';
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @packageDocumentation
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* // Node.js CLI: Load from file (requires sdk/loader-node)
|
|
46
|
+
* import { loadModel } from 'domain-lang-language/sdk/loader-node';
|
|
47
|
+
*
|
|
48
|
+
* const { query } = await loadModel('./domains.dlang', {
|
|
49
|
+
* workspaceDir: process.cwd()
|
|
50
|
+
* });
|
|
51
|
+
*
|
|
52
|
+
* const coreContexts = query.boundedContexts()
|
|
53
|
+
* .withClassification('Core')
|
|
54
|
+
* .withTeam('PaymentTeam');
|
|
55
|
+
*
|
|
56
|
+
* for (const bc of coreContexts) {
|
|
57
|
+
* console.log(`${bc.name}: ${bc.description ?? 'n/a'}`);
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* // Browser/Testing: Load from text (browser-safe)
|
|
64
|
+
* import { loadModelFromText } from '@domainlang/language/sdk';
|
|
65
|
+
*
|
|
66
|
+
* const { query } = await loadModelFromText(`
|
|
67
|
+
* Domain Sales { vision: "Handle sales" }
|
|
68
|
+
* bc OrderContext for Sales
|
|
69
|
+
* `);
|
|
70
|
+
*
|
|
71
|
+
* const sales = query.domain('Sales');
|
|
72
|
+
* console.log(sales?.vision);
|
|
73
|
+
* ```
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* // LSP Integration: Zero-copy access to existing AST (browser-safe)
|
|
78
|
+
* import { fromDocument } from '@domainlang/language/sdk';
|
|
79
|
+
*
|
|
80
|
+
* export class HoverProvider {
|
|
81
|
+
* getHover(document: LangiumDocument<Model>) {
|
|
82
|
+
* // SDK wraps existing AST - no reloading, no file I/O
|
|
83
|
+
* const query = fromDocument(document);
|
|
84
|
+
* const bc = query.boundedContext('OrderContext');
|
|
85
|
+
* return bc?.description;
|
|
86
|
+
* }
|
|
87
|
+
* }
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
// Browser-safe entry points
|
|
92
|
+
export { loadModelFromText } from './loader.js';
|
|
93
|
+
export { fromModel, fromDocument, fromServices, augmentModel } from './query.js';
|
|
94
|
+
|
|
95
|
+
// Note: loadModel() is NOT exported here - it requires Node.js filesystem
|
|
96
|
+
// For CLI/Node.js usage: import { loadModel } from '@domainlang/language/sdk/loader-node';
|
|
97
|
+
|
|
98
|
+
// Integration patterns for type-safe pattern matching (no magic strings)
|
|
99
|
+
export {
|
|
100
|
+
Pattern,
|
|
101
|
+
PatternFullName,
|
|
102
|
+
PatternAliases,
|
|
103
|
+
matchesPattern,
|
|
104
|
+
isUpstreamPattern,
|
|
105
|
+
isDownstreamPattern,
|
|
106
|
+
isMutualPattern,
|
|
107
|
+
UpstreamPatterns,
|
|
108
|
+
DownstreamPatterns,
|
|
109
|
+
MutualPatterns,
|
|
110
|
+
} from './patterns.js';
|
|
111
|
+
|
|
112
|
+
export type { IntegrationPattern } from './patterns.js';
|
|
113
|
+
|
|
114
|
+
// AST augmentation - import for type declarations
|
|
115
|
+
// Usage: import '@domainlang/language/sdk/ast-augmentation';
|
|
116
|
+
// This enables native SDK properties on AST types via declaration merging
|
|
117
|
+
|
|
118
|
+
// Public types
|
|
119
|
+
export type {
|
|
120
|
+
Query,
|
|
121
|
+
QueryBuilder,
|
|
122
|
+
QueryContext,
|
|
123
|
+
LoadOptions,
|
|
124
|
+
BcQueryBuilder,
|
|
125
|
+
RelationshipView,
|
|
126
|
+
} from './types.js';
|