@domainlang/language 0.1.81
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 +32 -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 +55 -0
- package/out/domain-lang-module.js +59 -0
- package/out/domain-lang-module.js.map +1 -0
- package/out/generated/ast.d.ts +770 -0
- package/out/generated/ast.js +565 -0
- package/out/generated/ast.js.map +1 -0
- package/out/generated/grammar.d.ts +6 -0
- package/out/generated/grammar.js +2502 -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 +13 -0
- package/out/index.js +17 -0
- package/out/index.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/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 +306 -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/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 +47 -0
- package/out/sdk/loader-node.js +104 -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 +301 -0
- package/out/sdk/types.js +8 -0
- package/out/sdk/types.js.map +1 -0
- package/out/services/dependency-analyzer.d.ts +94 -0
- package/out/services/dependency-analyzer.js +279 -0
- package/out/services/dependency-analyzer.js.map +1 -0
- package/out/services/dependency-resolver.d.ts +123 -0
- package/out/services/dependency-resolver.js +252 -0
- package/out/services/dependency-resolver.js.map +1 -0
- package/out/services/git-url-resolver.browser.d.ts +18 -0
- package/out/services/git-url-resolver.browser.js +15 -0
- package/out/services/git-url-resolver.browser.js.map +1 -0
- package/out/services/git-url-resolver.d.ts +192 -0
- package/out/services/git-url-resolver.js +382 -0
- package/out/services/git-url-resolver.js.map +1 -0
- package/out/services/governance-validator.d.ts +80 -0
- package/out/services/governance-validator.js +159 -0
- package/out/services/governance-validator.js.map +1 -0
- package/out/services/import-resolver.d.ts +18 -0
- package/out/services/import-resolver.js +22 -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/workspace-manager.d.ts +76 -0
- package/out/services/workspace-manager.js +323 -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 +57 -0
- package/out/utils/import-utils.js +228 -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 +77 -0
- package/out/validation/constants.js +96 -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 +18 -0
- package/out/validation/domain.js.map +1 -0
- package/out/validation/import.d.ts +44 -0
- package/out/validation/import.js +135 -0
- package/out/validation/import.js.map +1 -0
- package/out/validation/maps.d.ts +21 -0
- package/out/validation/maps.js +56 -0
- package/out/validation/maps.js.map +1 -0
- package/out/validation/metadata.d.ts +7 -0
- package/out/validation/metadata.js +12 -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 +97 -0
- package/src/ast-augmentation.ts +5 -0
- package/src/domain-lang-module.ts +100 -0
- package/src/domain-lang.langium +356 -0
- package/src/generated/ast.ts +999 -0
- package/src/generated/grammar.ts +2504 -0
- package/src/generated/module.ts +25 -0
- package/src/index.ts +17 -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/hover/ddd-pattern-explanations.ts +237 -0
- package/src/lsp/hover/domain-lang-hover.ts +340 -0
- package/src/lsp/hover/domain-lang-keywords.ts +50 -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 +128 -0
- package/src/sdk/indexes.ts +155 -0
- package/src/sdk/loader-node.ts +126 -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 +346 -0
- package/src/services/dependency-analyzer.ts +381 -0
- package/src/services/dependency-resolver.ts +334 -0
- package/src/services/git-url-resolver.browser.ts +31 -0
- package/src/services/git-url-resolver.ts +524 -0
- package/src/services/governance-validator.ts +219 -0
- package/src/services/import-resolver.ts +30 -0
- package/src/services/performance-optimizer.ts +170 -0
- package/src/services/relationship-inference.ts +121 -0
- package/src/services/workspace-manager.ts +416 -0
- package/src/syntaxes/domain-lang.monarch.ts +29 -0
- package/src/utils/import-utils.ts +274 -0
- package/src/validation/bounded-context.ts +99 -0
- package/src/validation/classification.ts +5 -0
- package/src/validation/constants.ts +124 -0
- package/src/validation/domain-lang-validator.ts +33 -0
- package/src/validation/domain.ts +24 -0
- package/src/validation/import.ts +171 -0
- package/src/validation/maps.ts +72 -0
- package/src/validation/metadata.ts +14 -0
- package/src/validation/model.ts +37 -0
- package/src/validation/relationships.ts +154 -0
- package/src/validation/shared.ts +14 -0
package/out/sdk/query.js
ADDED
|
@@ -0,0 +1,679 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query and QueryBuilder implementation for the Model Query SDK.
|
|
3
|
+
* Provides fluent, lazy evaluation of model queries.
|
|
4
|
+
*/
|
|
5
|
+
import { AstUtils } from 'langium';
|
|
6
|
+
import { isBoundedContext, isClassification, isContextMap, isDomain, isDomainMap, isModel, isNamespaceDeclaration, isTeam, isThisRef, } from '../generated/ast.js';
|
|
7
|
+
import { QualifiedNameProvider } from '../lsp/domain-lang-naming.js';
|
|
8
|
+
import { buildIndexes } from './indexes.js';
|
|
9
|
+
import { metadataAsMap, effectiveClassification, effectiveTeam, } from './resolution.js';
|
|
10
|
+
import { isDownstreamPattern, isUpstreamPattern, matchesPattern } from './patterns.js';
|
|
11
|
+
/**
|
|
12
|
+
* Tracks which models have been augmented to avoid redundant augmentation.
|
|
13
|
+
* Uses WeakSet to allow garbage collection of unused models.
|
|
14
|
+
*/
|
|
15
|
+
const augmentedModels = new WeakSet();
|
|
16
|
+
/**
|
|
17
|
+
* Ensures a model is augmented with SDK properties.
|
|
18
|
+
* Idempotent - safe to call multiple times.
|
|
19
|
+
*
|
|
20
|
+
* @param model - Model to ensure is augmented
|
|
21
|
+
*/
|
|
22
|
+
function ensureAugmented(model) {
|
|
23
|
+
if (!augmentedModels.has(model)) {
|
|
24
|
+
augmentModelInternal(model);
|
|
25
|
+
augmentedModels.add(model);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Creates a Query instance from a Model node.
|
|
30
|
+
* Zero-copy operation for already-linked AST (used in LSP/validation).
|
|
31
|
+
*
|
|
32
|
+
* Automatically augments the AST with resolved properties on first access.
|
|
33
|
+
*
|
|
34
|
+
* @param model - Root Model node
|
|
35
|
+
* @returns Query interface for model traversal
|
|
36
|
+
*/
|
|
37
|
+
export function fromModel(model) {
|
|
38
|
+
ensureAugmented(model);
|
|
39
|
+
return new QueryImpl(model);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Creates a Query instance from a LangiumDocument.
|
|
43
|
+
* Zero-copy operation for already-linked AST (used in LSP providers).
|
|
44
|
+
*
|
|
45
|
+
* Automatically augments the AST with resolved properties on first access.
|
|
46
|
+
*
|
|
47
|
+
* @param document - LangiumDocument containing a Model
|
|
48
|
+
* @returns Query interface for model traversal
|
|
49
|
+
*/
|
|
50
|
+
export function fromDocument(document) {
|
|
51
|
+
const model = document.parseResult.value;
|
|
52
|
+
ensureAugmented(model);
|
|
53
|
+
return new QueryImpl(model);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Creates a Query instance from DomainLangServices and document URI.
|
|
57
|
+
* Zero-copy operation for already-linked AST (used when only services available).
|
|
58
|
+
*
|
|
59
|
+
* Automatically augments the AST with resolved properties on first access.
|
|
60
|
+
*
|
|
61
|
+
* @param services - DomainLangServices instance
|
|
62
|
+
* @param documentUri - URI of the document to query
|
|
63
|
+
* @returns Query interface for model traversal
|
|
64
|
+
* @throws Error if document not found or not a Model
|
|
65
|
+
*/
|
|
66
|
+
export function fromServices(services, documentUri) {
|
|
67
|
+
const document = services.shared.workspace.LangiumDocuments.getDocument(documentUri);
|
|
68
|
+
if (!document) {
|
|
69
|
+
throw new Error(`Document not found: ${documentUri.toString()}`);
|
|
70
|
+
}
|
|
71
|
+
const model = document.parseResult.value;
|
|
72
|
+
if (!isModel(model)) {
|
|
73
|
+
throw new Error(`Document root is not a Model: ${documentUri.toString()}`);
|
|
74
|
+
}
|
|
75
|
+
ensureAugmented(model);
|
|
76
|
+
return new QueryImpl(model);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Implementation of Query interface.
|
|
80
|
+
* Lazily builds indexes on first access.
|
|
81
|
+
*/
|
|
82
|
+
class QueryImpl {
|
|
83
|
+
constructor(model) {
|
|
84
|
+
this.model = model;
|
|
85
|
+
this.fqnProvider = new QualifiedNameProvider();
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Lazily builds and caches indexes on first access.
|
|
89
|
+
*/
|
|
90
|
+
getIndexes() {
|
|
91
|
+
if (!this.indexes) {
|
|
92
|
+
this.indexes = buildIndexes(this.model);
|
|
93
|
+
}
|
|
94
|
+
return this.indexes;
|
|
95
|
+
}
|
|
96
|
+
domains() {
|
|
97
|
+
// Use generator for lazy iteration per PRS requirement
|
|
98
|
+
const model = this.model;
|
|
99
|
+
function* domainIterator() {
|
|
100
|
+
for (const node of AstUtils.streamAllContents(model)) {
|
|
101
|
+
if (isDomain(node)) {
|
|
102
|
+
yield node;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return new QueryBuilderImpl(domainIterator(), this.fqnProvider);
|
|
107
|
+
}
|
|
108
|
+
boundedContexts() {
|
|
109
|
+
// Use generator for lazy iteration per PRS requirement
|
|
110
|
+
const model = this.model;
|
|
111
|
+
function* bcIterator() {
|
|
112
|
+
for (const node of AstUtils.streamAllContents(model)) {
|
|
113
|
+
if (isBoundedContext(node)) {
|
|
114
|
+
yield node;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return new BcQueryBuilderImpl(bcIterator(), this.fqnProvider, this.getIndexes());
|
|
119
|
+
}
|
|
120
|
+
teams() {
|
|
121
|
+
// Use generator for lazy iteration
|
|
122
|
+
const model = this.model;
|
|
123
|
+
function* teamIterator() {
|
|
124
|
+
for (const node of AstUtils.streamAllContents(model)) {
|
|
125
|
+
if (isTeam(node)) {
|
|
126
|
+
yield node;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return new QueryBuilderImpl(teamIterator(), this.fqnProvider);
|
|
131
|
+
}
|
|
132
|
+
classifications() {
|
|
133
|
+
// Use generator for lazy iteration
|
|
134
|
+
const model = this.model;
|
|
135
|
+
function* classIterator() {
|
|
136
|
+
for (const node of AstUtils.streamAllContents(model)) {
|
|
137
|
+
if (isClassification(node)) {
|
|
138
|
+
yield node;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return new QueryBuilderImpl(classIterator(), this.fqnProvider);
|
|
143
|
+
}
|
|
144
|
+
relationships() {
|
|
145
|
+
// Use generator for lazy iteration - combines BC and ContextMap relationships
|
|
146
|
+
return new QueryBuilderImpl(this.iterateRelationships(), this.fqnProvider);
|
|
147
|
+
}
|
|
148
|
+
/** @internal Generator for relationship iteration */
|
|
149
|
+
*iterateRelationships() {
|
|
150
|
+
// Collect relationships defined on bounded contexts
|
|
151
|
+
for (const node of AstUtils.streamAllContents(this.model)) {
|
|
152
|
+
if (isBoundedContext(node)) {
|
|
153
|
+
for (const rel of node.relationships) {
|
|
154
|
+
const view = this.createRelationshipView(rel, node, 'BoundedContext');
|
|
155
|
+
if (view) {
|
|
156
|
+
yield view;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Collect from ContextMap.relationships
|
|
162
|
+
for (const node of AstUtils.streamAllContents(this.model)) {
|
|
163
|
+
if (isContextMap(node)) {
|
|
164
|
+
for (const rel of node.relationships) {
|
|
165
|
+
const view = this.createRelationshipView(rel, undefined, 'ContextMap');
|
|
166
|
+
if (view) {
|
|
167
|
+
yield view;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
contextMaps() {
|
|
174
|
+
const model = this.model;
|
|
175
|
+
function* cmapIterator() {
|
|
176
|
+
for (const node of AstUtils.streamAllContents(model)) {
|
|
177
|
+
if (isContextMap(node)) {
|
|
178
|
+
yield node;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return new QueryBuilderImpl(cmapIterator(), this.fqnProvider);
|
|
183
|
+
}
|
|
184
|
+
domainMaps() {
|
|
185
|
+
const model = this.model;
|
|
186
|
+
function* dmapIterator() {
|
|
187
|
+
for (const node of AstUtils.streamAllContents(model)) {
|
|
188
|
+
if (isDomainMap(node)) {
|
|
189
|
+
yield node;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return new QueryBuilderImpl(dmapIterator(), this.fqnProvider);
|
|
194
|
+
}
|
|
195
|
+
namespaces() {
|
|
196
|
+
const model = this.model;
|
|
197
|
+
function* nsIterator() {
|
|
198
|
+
for (const node of AstUtils.streamAllContents(model)) {
|
|
199
|
+
if (isNamespaceDeclaration(node)) {
|
|
200
|
+
yield node;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return new QueryBuilderImpl(nsIterator(), this.fqnProvider);
|
|
205
|
+
}
|
|
206
|
+
byFqn(fqn) {
|
|
207
|
+
return this.getIndexes().byFqn.get(fqn);
|
|
208
|
+
}
|
|
209
|
+
domain(name) {
|
|
210
|
+
// Try FQN lookup first
|
|
211
|
+
const byFqn = this.byFqn(name);
|
|
212
|
+
if (byFqn) {
|
|
213
|
+
return byFqn;
|
|
214
|
+
}
|
|
215
|
+
// Fallback to simple name lookup
|
|
216
|
+
const nodes = this.getIndexes().byName.get(name) ?? [];
|
|
217
|
+
return nodes.find(isDomain);
|
|
218
|
+
}
|
|
219
|
+
boundedContext(name) {
|
|
220
|
+
// Try FQN lookup first
|
|
221
|
+
const byFqn = this.byFqn(name);
|
|
222
|
+
if (byFqn) {
|
|
223
|
+
return byFqn;
|
|
224
|
+
}
|
|
225
|
+
// Fallback to simple name lookup
|
|
226
|
+
const nodes = this.getIndexes().byName.get(name) ?? [];
|
|
227
|
+
return nodes.find(isBoundedContext);
|
|
228
|
+
}
|
|
229
|
+
bc(name) {
|
|
230
|
+
return this.boundedContext(name);
|
|
231
|
+
}
|
|
232
|
+
team(name) {
|
|
233
|
+
const nodes = this.getIndexes().byName.get(name) ?? [];
|
|
234
|
+
return nodes.find(isTeam);
|
|
235
|
+
}
|
|
236
|
+
fqn(node) {
|
|
237
|
+
if ('name' in node && typeof node.name === 'string' && node.$container) {
|
|
238
|
+
const container = node.$container;
|
|
239
|
+
if (isModel(container) || isNamespaceDeclaration(container)) {
|
|
240
|
+
return this.fqnProvider.getQualifiedName(container, node.name);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return '';
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Creates a RelationshipView from a Relationship AST node.
|
|
247
|
+
* Resolves 'this' references to the containing BoundedContext.
|
|
248
|
+
*/
|
|
249
|
+
createRelationshipView(rel, containingBc, source) {
|
|
250
|
+
const left = this.resolveContextRef(rel.left, containingBc);
|
|
251
|
+
const right = this.resolveContextRef(rel.right, containingBc);
|
|
252
|
+
if (!left || !right) {
|
|
253
|
+
return undefined;
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
left,
|
|
257
|
+
right,
|
|
258
|
+
arrow: rel.arrow,
|
|
259
|
+
leftPatterns: rel.leftPatterns,
|
|
260
|
+
rightPatterns: rel.rightPatterns,
|
|
261
|
+
type: rel.type,
|
|
262
|
+
inferredType: this.inferRelationshipType(rel),
|
|
263
|
+
source,
|
|
264
|
+
astNode: rel,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Resolves a BoundedContextRef to a BoundedContext.
|
|
269
|
+
* Handles 'this' references by using the containing BoundedContext.
|
|
270
|
+
*/
|
|
271
|
+
resolveContextRef(ref, containingBc) {
|
|
272
|
+
if (isThisRef(ref)) {
|
|
273
|
+
return containingBc;
|
|
274
|
+
}
|
|
275
|
+
return ref.link?.ref;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Infers relationship type from integration patterns.
|
|
279
|
+
* Simple heuristic based on common DDD pattern combinations.
|
|
280
|
+
*/
|
|
281
|
+
inferRelationshipType(rel) {
|
|
282
|
+
const leftPatterns = rel.leftPatterns;
|
|
283
|
+
const rightPatterns = rel.rightPatterns;
|
|
284
|
+
// Partnership: Bidirectional with P or SK
|
|
285
|
+
if (rel.arrow === '<->' && (leftPatterns.includes('P') || leftPatterns.includes('SK'))) {
|
|
286
|
+
return 'Partnership';
|
|
287
|
+
}
|
|
288
|
+
// Shared Kernel: SK pattern
|
|
289
|
+
if (leftPatterns.includes('SK') || rightPatterns.includes('SK')) {
|
|
290
|
+
return 'SharedKernel';
|
|
291
|
+
}
|
|
292
|
+
// Customer-Supplier: OHS + CF
|
|
293
|
+
if (leftPatterns.includes('OHS') && rightPatterns.includes('CF')) {
|
|
294
|
+
return 'CustomerSupplier';
|
|
295
|
+
}
|
|
296
|
+
// Upstream-Downstream: directional arrow
|
|
297
|
+
if (rel.arrow === '->') {
|
|
298
|
+
return 'UpstreamDownstream';
|
|
299
|
+
}
|
|
300
|
+
// Separate Ways
|
|
301
|
+
if (rel.arrow === '><') {
|
|
302
|
+
return 'SeparateWays';
|
|
303
|
+
}
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Base implementation of QueryBuilder with lazy iteration.
|
|
309
|
+
* Predicates are chained and only evaluated during iteration.
|
|
310
|
+
*/
|
|
311
|
+
class QueryBuilderImpl {
|
|
312
|
+
constructor(items, fqnProvider, predicates = []) {
|
|
313
|
+
this.fqnProvider = fqnProvider;
|
|
314
|
+
this.sourceItems = items;
|
|
315
|
+
this.predicateList = predicates;
|
|
316
|
+
}
|
|
317
|
+
where(predicate) {
|
|
318
|
+
return new QueryBuilderImpl(this.sourceItems, this.fqnProvider, [...this.predicateList, predicate]);
|
|
319
|
+
}
|
|
320
|
+
withName(pattern) {
|
|
321
|
+
const regex = typeof pattern === 'string' ? new RegExp(`^${escapeRegex(pattern)}$`) : pattern;
|
|
322
|
+
return this.where((item) => {
|
|
323
|
+
const node = item;
|
|
324
|
+
if ('name' in node && typeof node.name === 'string') {
|
|
325
|
+
return regex.test(node.name);
|
|
326
|
+
}
|
|
327
|
+
return false;
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
withFqn(pattern) {
|
|
331
|
+
const regex = typeof pattern === 'string' ? new RegExp(`^${escapeRegex(pattern)}$`) : pattern;
|
|
332
|
+
return this.where((item) => {
|
|
333
|
+
const node = item;
|
|
334
|
+
if ('name' in node && typeof node.name === 'string' && node.$container) {
|
|
335
|
+
const container = node.$container;
|
|
336
|
+
if (isModel(container) || isNamespaceDeclaration(container)) {
|
|
337
|
+
const fqn = this.fqnProvider.getQualifiedName(container, node.name);
|
|
338
|
+
return regex.test(fqn);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return false;
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
first() {
|
|
345
|
+
for (const item of this) {
|
|
346
|
+
return item;
|
|
347
|
+
}
|
|
348
|
+
return undefined;
|
|
349
|
+
}
|
|
350
|
+
toArray() {
|
|
351
|
+
return Array.from(this);
|
|
352
|
+
}
|
|
353
|
+
count() {
|
|
354
|
+
let count = 0;
|
|
355
|
+
for (const _ of this) {
|
|
356
|
+
count++;
|
|
357
|
+
}
|
|
358
|
+
return count;
|
|
359
|
+
}
|
|
360
|
+
*[Symbol.iterator]() {
|
|
361
|
+
for (const item of this.sourceItems) {
|
|
362
|
+
if (this.predicateList.every(p => p(item))) {
|
|
363
|
+
yield item;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* BoundedContext-specific QueryBuilder with domain filters.
|
|
370
|
+
* Supports indexed lookups for performance when filters allow.
|
|
371
|
+
*/
|
|
372
|
+
class BcQueryBuilderImpl extends QueryBuilderImpl {
|
|
373
|
+
constructor(items, fqnProvider, indexes, predicates = []) {
|
|
374
|
+
super(items, fqnProvider, predicates);
|
|
375
|
+
this.indexes = indexes;
|
|
376
|
+
}
|
|
377
|
+
where(predicate) {
|
|
378
|
+
return new BcQueryBuilderImpl(this.sourceItems, this.fqnProvider, this.indexes, [...this.predicateList, predicate]);
|
|
379
|
+
}
|
|
380
|
+
withName(pattern) {
|
|
381
|
+
return super.withName(pattern);
|
|
382
|
+
}
|
|
383
|
+
withFqn(pattern) {
|
|
384
|
+
return super.withFqn(pattern);
|
|
385
|
+
}
|
|
386
|
+
inDomain(domain) {
|
|
387
|
+
const domainName = typeof domain === 'string' ? domain : domain.name;
|
|
388
|
+
return this.where(bc => bc.domain?.ref?.name === domainName);
|
|
389
|
+
}
|
|
390
|
+
withTeam(team) {
|
|
391
|
+
const teamName = typeof team === 'string' ? team : team.name;
|
|
392
|
+
// Use index for initial filtering if no predicates yet
|
|
393
|
+
if (this.predicateList.length === 0) {
|
|
394
|
+
const indexed = this.indexes.byTeam.get(teamName) ?? [];
|
|
395
|
+
return new BcQueryBuilderImpl(indexed, this.fqnProvider, this.indexes);
|
|
396
|
+
}
|
|
397
|
+
// Add predicate to existing chain
|
|
398
|
+
return this.where(bc => effectiveTeam(bc)?.name === teamName);
|
|
399
|
+
}
|
|
400
|
+
withClassification(classification) {
|
|
401
|
+
const classificationName = typeof classification === 'string' ? classification : classification.name;
|
|
402
|
+
// Use index for initial filtering if no predicates yet
|
|
403
|
+
if (this.predicateList.length === 0) {
|
|
404
|
+
const indexed = this.indexes.byClassification.get(classificationName) ?? [];
|
|
405
|
+
return new BcQueryBuilderImpl(indexed, this.fqnProvider, this.indexes);
|
|
406
|
+
}
|
|
407
|
+
// Add predicate to existing chain
|
|
408
|
+
return this.where(bc => effectiveClassification(bc)?.name === classificationName);
|
|
409
|
+
}
|
|
410
|
+
withMetadata(key, value) {
|
|
411
|
+
// Use index for initial filtering if no predicates yet and no value specified
|
|
412
|
+
if (this.predicateList.length === 0 && value === undefined) {
|
|
413
|
+
const indexed = this.indexes.byMetadataKey.get(key) ?? [];
|
|
414
|
+
return new BcQueryBuilderImpl(indexed, this.fqnProvider, this.indexes);
|
|
415
|
+
}
|
|
416
|
+
// Add predicate to existing chain
|
|
417
|
+
return this.where(bc => {
|
|
418
|
+
const metadata = metadataAsMap(bc);
|
|
419
|
+
const metaValue = metadata.get(key);
|
|
420
|
+
if (metaValue === undefined) {
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
return value === undefined || metaValue === value;
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Escapes special regex characters in a string.
|
|
429
|
+
*/
|
|
430
|
+
function escapeRegex(str) {
|
|
431
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Augments BoundedContext instances with SDK-resolved properties.
|
|
435
|
+
* Called during model loading to enrich the AST.
|
|
436
|
+
*
|
|
437
|
+
* Properties use natural names (not sdk* prefix) per PRS:
|
|
438
|
+
* "Properties are discoverable in IDE autocomplete. The SDK enriches the AST
|
|
439
|
+
* during load, so `bc.classification` just works—no imports needed."
|
|
440
|
+
*
|
|
441
|
+
* Note: We use getters to avoid computing values until accessed.
|
|
442
|
+
* Note: We use Object.defineProperty to avoid modifying the original interface.
|
|
443
|
+
*
|
|
444
|
+
* @param bc - BoundedContext to augment
|
|
445
|
+
*/
|
|
446
|
+
export function augmentBoundedContext(bc) {
|
|
447
|
+
const fqnProvider = new QualifiedNameProvider();
|
|
448
|
+
// Define computed properties with getters for lazy evaluation
|
|
449
|
+
// Only include properties that add value beyond direct AST access:
|
|
450
|
+
// - effectiveClassification/effectiveTeam: array precedence resolution
|
|
451
|
+
// - metadataMap: array to Map conversion
|
|
452
|
+
// - fqn: computed qualified name
|
|
453
|
+
// - helper methods: hasClassification, hasTeam, hasMetadata
|
|
454
|
+
Object.defineProperties(bc, {
|
|
455
|
+
effectiveClassification: {
|
|
456
|
+
get: () => effectiveClassification(bc),
|
|
457
|
+
enumerable: true,
|
|
458
|
+
configurable: true,
|
|
459
|
+
},
|
|
460
|
+
effectiveTeam: {
|
|
461
|
+
get: () => effectiveTeam(bc),
|
|
462
|
+
enumerable: true,
|
|
463
|
+
configurable: true,
|
|
464
|
+
},
|
|
465
|
+
metadataMap: {
|
|
466
|
+
get: () => metadataAsMap(bc),
|
|
467
|
+
enumerable: true,
|
|
468
|
+
configurable: true,
|
|
469
|
+
},
|
|
470
|
+
fqn: {
|
|
471
|
+
get: () => {
|
|
472
|
+
if (bc.$container && (isModel(bc.$container) || isNamespaceDeclaration(bc.$container))) {
|
|
473
|
+
return fqnProvider.getQualifiedName(bc.$container, bc.name);
|
|
474
|
+
}
|
|
475
|
+
return bc.name;
|
|
476
|
+
},
|
|
477
|
+
enumerable: true,
|
|
478
|
+
configurable: true,
|
|
479
|
+
},
|
|
480
|
+
// Helper methods
|
|
481
|
+
hasClassification: {
|
|
482
|
+
value: (name) => {
|
|
483
|
+
const classification = effectiveClassification(bc);
|
|
484
|
+
if (!classification)
|
|
485
|
+
return false;
|
|
486
|
+
const targetName = typeof name === 'string' ? name : name?.name;
|
|
487
|
+
if (!targetName)
|
|
488
|
+
return false;
|
|
489
|
+
return classification.name === targetName;
|
|
490
|
+
},
|
|
491
|
+
enumerable: false,
|
|
492
|
+
configurable: true,
|
|
493
|
+
},
|
|
494
|
+
hasTeam: {
|
|
495
|
+
value: (name) => {
|
|
496
|
+
const team = effectiveTeam(bc);
|
|
497
|
+
if (!team)
|
|
498
|
+
return false;
|
|
499
|
+
const targetName = typeof name === 'string' ? name : name?.name;
|
|
500
|
+
if (!targetName)
|
|
501
|
+
return false;
|
|
502
|
+
return team.name === targetName;
|
|
503
|
+
},
|
|
504
|
+
enumerable: false,
|
|
505
|
+
configurable: true,
|
|
506
|
+
},
|
|
507
|
+
hasMetadata: {
|
|
508
|
+
value: (key, value) => {
|
|
509
|
+
const metadata = metadataAsMap(bc);
|
|
510
|
+
const metaValue = metadata.get(key);
|
|
511
|
+
if (metaValue === undefined)
|
|
512
|
+
return false;
|
|
513
|
+
return value === undefined || metaValue === value;
|
|
514
|
+
},
|
|
515
|
+
enumerable: false,
|
|
516
|
+
configurable: true,
|
|
517
|
+
},
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Augments Domain instances with SDK-resolved properties.
|
|
522
|
+
* Called during model loading to enrich the AST.
|
|
523
|
+
*
|
|
524
|
+
* Only includes properties that add value beyond direct AST access:
|
|
525
|
+
* - fqn: computed qualified name
|
|
526
|
+
* - hasType: helper method
|
|
527
|
+
*
|
|
528
|
+
* Direct access (no augmentation needed):
|
|
529
|
+
* - domain.description
|
|
530
|
+
* - domain.vision
|
|
531
|
+
* - domain.type?.ref
|
|
532
|
+
*
|
|
533
|
+
* @param domain - Domain to augment
|
|
534
|
+
*/
|
|
535
|
+
export function augmentDomain(domain) {
|
|
536
|
+
const fqnProvider = new QualifiedNameProvider();
|
|
537
|
+
Object.defineProperties(domain, {
|
|
538
|
+
fqn: {
|
|
539
|
+
get: () => {
|
|
540
|
+
if (domain.$container && (isModel(domain.$container) || isNamespaceDeclaration(domain.$container))) {
|
|
541
|
+
return fqnProvider.getQualifiedName(domain.$container, domain.name);
|
|
542
|
+
}
|
|
543
|
+
return domain.name;
|
|
544
|
+
},
|
|
545
|
+
enumerable: true,
|
|
546
|
+
configurable: true,
|
|
547
|
+
},
|
|
548
|
+
// Helper methods
|
|
549
|
+
hasType: {
|
|
550
|
+
value: (name) => {
|
|
551
|
+
const type = domain.type?.ref;
|
|
552
|
+
if (!type)
|
|
553
|
+
return false;
|
|
554
|
+
const targetName = typeof name === 'string' ? name : name?.name;
|
|
555
|
+
if (!targetName)
|
|
556
|
+
return false;
|
|
557
|
+
return type.name === targetName;
|
|
558
|
+
},
|
|
559
|
+
enumerable: false,
|
|
560
|
+
configurable: true,
|
|
561
|
+
},
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Augments Relationship instances with SDK helper methods.
|
|
566
|
+
* Called during model loading to enrich the AST.
|
|
567
|
+
*
|
|
568
|
+
* @param rel - Relationship to augment
|
|
569
|
+
* @param containingBc - BoundedContext containing this relationship (for 'this' resolution)
|
|
570
|
+
*/
|
|
571
|
+
export function augmentRelationship(rel, containingBc) {
|
|
572
|
+
Object.defineProperties(rel, {
|
|
573
|
+
leftContextName: {
|
|
574
|
+
get: () => {
|
|
575
|
+
if (isThisRef(rel.left)) {
|
|
576
|
+
return containingBc?.name ?? 'this';
|
|
577
|
+
}
|
|
578
|
+
return rel.left.link?.ref?.name ?? '';
|
|
579
|
+
},
|
|
580
|
+
enumerable: true,
|
|
581
|
+
configurable: true,
|
|
582
|
+
},
|
|
583
|
+
rightContextName: {
|
|
584
|
+
get: () => {
|
|
585
|
+
if (isThisRef(rel.right)) {
|
|
586
|
+
return containingBc?.name ?? 'this';
|
|
587
|
+
}
|
|
588
|
+
return rel.right.link?.ref?.name ?? '';
|
|
589
|
+
},
|
|
590
|
+
enumerable: true,
|
|
591
|
+
configurable: true,
|
|
592
|
+
},
|
|
593
|
+
isBidirectional: {
|
|
594
|
+
get: () => rel.arrow === '<->',
|
|
595
|
+
enumerable: true,
|
|
596
|
+
configurable: true,
|
|
597
|
+
},
|
|
598
|
+
// Helper methods for pattern matching (type-safe, no magic strings)
|
|
599
|
+
hasPattern: {
|
|
600
|
+
value: (pattern) => {
|
|
601
|
+
return rel.leftPatterns.some(p => matchesPattern(p, pattern)) ||
|
|
602
|
+
rel.rightPatterns.some(p => matchesPattern(p, pattern));
|
|
603
|
+
},
|
|
604
|
+
enumerable: false,
|
|
605
|
+
configurable: true,
|
|
606
|
+
},
|
|
607
|
+
hasLeftPattern: {
|
|
608
|
+
value: (pattern) => {
|
|
609
|
+
return rel.leftPatterns.some(p => matchesPattern(p, pattern));
|
|
610
|
+
},
|
|
611
|
+
enumerable: false,
|
|
612
|
+
configurable: true,
|
|
613
|
+
},
|
|
614
|
+
hasRightPattern: {
|
|
615
|
+
value: (pattern) => {
|
|
616
|
+
return rel.rightPatterns.some(p => matchesPattern(p, pattern));
|
|
617
|
+
},
|
|
618
|
+
enumerable: false,
|
|
619
|
+
configurable: true,
|
|
620
|
+
},
|
|
621
|
+
isUpstream: {
|
|
622
|
+
value: (side) => {
|
|
623
|
+
const patterns = side === 'left' ? rel.leftPatterns : rel.rightPatterns;
|
|
624
|
+
return patterns.some(p => isUpstreamPattern(p));
|
|
625
|
+
},
|
|
626
|
+
enumerable: false,
|
|
627
|
+
configurable: true,
|
|
628
|
+
},
|
|
629
|
+
isDownstream: {
|
|
630
|
+
value: (side) => {
|
|
631
|
+
const patterns = side === 'left' ? rel.leftPatterns : rel.rightPatterns;
|
|
632
|
+
return patterns.some(p => isDownstreamPattern(p));
|
|
633
|
+
},
|
|
634
|
+
enumerable: false,
|
|
635
|
+
configurable: true,
|
|
636
|
+
},
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Internal implementation of model augmentation.
|
|
641
|
+
* Called by ensureAugmented() which tracks augmentation state.
|
|
642
|
+
*
|
|
643
|
+
* @param model - Root Model node to augment
|
|
644
|
+
*/
|
|
645
|
+
function augmentModelInternal(model) {
|
|
646
|
+
for (const node of AstUtils.streamAllContents(model)) {
|
|
647
|
+
if (isBoundedContext(node)) {
|
|
648
|
+
augmentBoundedContext(node);
|
|
649
|
+
// Augment relationships inside this bounded context
|
|
650
|
+
for (const rel of node.relationships) {
|
|
651
|
+
augmentRelationship(rel, node);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
else if (isDomain(node)) {
|
|
655
|
+
augmentDomain(node);
|
|
656
|
+
}
|
|
657
|
+
else if (isContextMap(node)) {
|
|
658
|
+
// Augment relationships in context maps (no containing BC)
|
|
659
|
+
for (const rel of node.relationships) {
|
|
660
|
+
augmentRelationship(rel, undefined);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Augments all AST nodes in a model with SDK-resolved properties.
|
|
667
|
+
*
|
|
668
|
+
* This function walks the entire AST and adds lazy getters for resolved
|
|
669
|
+
* properties like `effectiveClassification`, `effectiveTeam`, etc.
|
|
670
|
+
*
|
|
671
|
+
* Idempotent - safe to call multiple times on the same model.
|
|
672
|
+
* Automatically called by `fromModel()`, `fromDocument()`, and `fromServices()`.
|
|
673
|
+
*
|
|
674
|
+
* @param model - Root Model node to augment
|
|
675
|
+
*/
|
|
676
|
+
export function augmentModel(model) {
|
|
677
|
+
ensureAugmented(model);
|
|
678
|
+
}
|
|
679
|
+
//# sourceMappingURL=query.js.map
|