@domainlang/language 0.11.0 → 0.13.0
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/out/ast-augmentation.d.ts +7 -2
- package/out/diagram/context-map-diagram-generator.d.ts +72 -0
- package/out/diagram/context-map-diagram-generator.js +405 -0
- package/out/diagram/context-map-diagram-generator.js.map +1 -0
- package/out/diagram/context-map-layout-configurator.d.ts +15 -0
- package/out/diagram/context-map-layout-configurator.js +39 -0
- package/out/diagram/context-map-layout-configurator.js.map +1 -0
- package/out/diagram/elk-layout-factory.d.ts +43 -0
- package/out/diagram/elk-layout-factory.js +64 -0
- package/out/diagram/elk-layout-factory.js.map +1 -0
- package/out/domain-lang-module.d.ts +7 -0
- package/out/domain-lang-module.js +11 -2
- package/out/domain-lang-module.js.map +1 -1
- package/out/generated/ast.d.ts +323 -51
- package/out/generated/ast.js +194 -33
- package/out/generated/ast.js.map +1 -1
- package/out/generated/grammar.js +418 -172
- package/out/generated/grammar.js.map +1 -1
- package/out/index.d.ts +3 -0
- package/out/index.js +4 -0
- package/out/index.js.map +1 -1
- package/out/lsp/domain-lang-code-lens-provider.d.ts +8 -0
- package/out/lsp/domain-lang-code-lens-provider.js +48 -0
- package/out/lsp/domain-lang-code-lens-provider.js.map +1 -0
- package/out/lsp/domain-lang-completion.js +39 -15
- package/out/lsp/domain-lang-completion.js.map +1 -1
- package/out/lsp/domain-lang-document-symbol-provider.js +5 -5
- package/out/lsp/domain-lang-document-symbol-provider.js.map +1 -1
- package/out/lsp/domain-lang-formatter.js +32 -0
- package/out/lsp/domain-lang-formatter.js.map +1 -1
- package/out/lsp/domain-lang-index-manager.d.ts +2 -3
- package/out/lsp/domain-lang-index-manager.js +5 -8
- package/out/lsp/domain-lang-index-manager.js.map +1 -1
- package/out/lsp/domain-lang-workspace-manager.d.ts +1 -1
- package/out/lsp/domain-lang-workspace-manager.js +2 -26
- package/out/lsp/domain-lang-workspace-manager.js.map +1 -1
- package/out/lsp/explain.js +9 -3
- package/out/lsp/explain.js.map +1 -1
- package/out/lsp/hover/domain-lang-hover.js +13 -11
- package/out/lsp/hover/domain-lang-hover.js.map +1 -1
- package/out/lsp/hover/domain-lang-keywords.js +29 -26
- package/out/lsp/hover/domain-lang-keywords.js.map +1 -1
- package/out/lsp/tool-handlers.js +63 -57
- package/out/lsp/tool-handlers.js.map +1 -1
- package/out/sdk/ast-augmentation.d.ts +29 -21
- package/out/sdk/ast-augmentation.js +11 -7
- package/out/sdk/ast-augmentation.js.map +1 -1
- package/out/sdk/index.d.ts +2 -2
- package/out/sdk/index.js +1 -1
- package/out/sdk/index.js.map +1 -1
- package/out/sdk/loader-node.js +2 -2
- package/out/sdk/loader-node.js.map +1 -1
- package/out/sdk/patterns.d.ts +50 -61
- package/out/sdk/patterns.js +92 -62
- package/out/sdk/patterns.js.map +1 -1
- package/out/sdk/query.js +54 -43
- package/out/sdk/query.js.map +1 -1
- package/out/sdk/serializers.js +20 -7
- package/out/sdk/serializers.js.map +1 -1
- package/out/sdk/types.d.ts +87 -18
- package/out/sdk/types.js.map +1 -1
- package/out/sdk/validator.js +48 -64
- package/out/sdk/validator.js.map +1 -1
- package/out/services/performance-optimizer.d.ts +3 -3
- package/out/services/performance-optimizer.js +1 -3
- package/out/services/performance-optimizer.js.map +1 -1
- package/out/services/relationship-inference.d.ts +4 -4
- package/out/services/relationship-inference.js +34 -46
- package/out/services/relationship-inference.js.map +1 -1
- package/out/syntaxes/domain-lang.monarch.js +1 -1
- package/out/syntaxes/domain-lang.monarch.js.map +1 -1
- package/out/utils/import-utils.d.ts +6 -20
- package/out/utils/import-utils.js +3 -63
- package/out/utils/import-utils.js.map +1 -1
- package/out/validation/constants.d.ts +23 -6
- package/out/validation/constants.js +24 -7
- package/out/validation/constants.js.map +1 -1
- package/out/validation/maps.js +10 -4
- package/out/validation/maps.js.map +1 -1
- package/out/validation/relationships.d.ts +4 -8
- package/out/validation/relationships.js +96 -48
- package/out/validation/relationships.js.map +1 -1
- package/package.json +5 -2
- package/src/ast-augmentation.ts +7 -2
- package/src/diagram/context-map-diagram-generator.ts +513 -0
- package/src/diagram/context-map-layout-configurator.ts +43 -0
- package/src/diagram/elk-layout-factory.ts +83 -0
- package/src/domain-lang-module.ts +19 -2
- package/src/domain-lang.langium +62 -26
- package/src/generated/ast.ts +413 -63
- package/src/generated/grammar.ts +418 -172
- package/src/index.ts +5 -0
- package/src/lsp/domain-lang-code-lens-provider.ts +54 -0
- package/src/lsp/domain-lang-completion.ts +42 -15
- package/src/lsp/domain-lang-document-symbol-provider.ts +5 -5
- package/src/lsp/domain-lang-formatter.ts +34 -0
- package/src/lsp/domain-lang-index-manager.ts +6 -9
- package/src/lsp/domain-lang-workspace-manager.ts +3 -29
- package/src/lsp/explain.ts +10 -2
- package/src/lsp/hover/domain-lang-hover.ts +10 -8
- package/src/lsp/hover/domain-lang-keywords.ts +27 -24
- package/src/lsp/tool-handlers.ts +61 -47
- package/src/sdk/ast-augmentation.ts +30 -21
- package/src/sdk/index.ts +11 -1
- package/src/sdk/loader-node.ts +2 -2
- package/src/sdk/patterns.ts +114 -76
- package/src/sdk/query.ts +57 -48
- package/src/sdk/serializers.ts +20 -7
- package/src/sdk/types.ts +92 -17
- package/src/sdk/validator.ts +52 -69
- package/src/services/performance-optimizer.ts +4 -6
- package/src/services/relationship-inference.ts +43 -54
- package/src/utils/import-utils.ts +9 -74
- package/src/validation/constants.ts +32 -9
- package/src/validation/maps.ts +12 -4
- package/src/validation/relationships.ts +150 -71
|
@@ -46,15 +46,19 @@
|
|
|
46
46
|
* - `fqn` - Computed fully qualified name
|
|
47
47
|
* - `hasType(name)` - Check type matches
|
|
48
48
|
*
|
|
49
|
-
* **Properties added to
|
|
50
|
-
* - `
|
|
51
|
-
* - `
|
|
52
|
-
* - `
|
|
49
|
+
* **Properties added to DirectionalRelationship:**
|
|
50
|
+
* - `isBidirectional` - Check if relationship is bidirectional (`<->`)
|
|
51
|
+
* - `leftContextName` - Resolved name of left context (handles `this`)
|
|
52
|
+
* - `rightContextName` - Resolved name of right context (handles `this`)
|
|
53
|
+
* - `hasPattern(patternType)` - Check if pattern exists on either side
|
|
54
|
+
* - `hasLeftPattern(patternType)` - Check left patterns
|
|
55
|
+
* - `hasRightPattern(patternType)` - Check right patterns
|
|
53
56
|
* - `isUpstream(side)` - Check if side is upstream
|
|
54
57
|
* - `isDownstream(side)` - Check if side is downstream
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* - `
|
|
58
|
+
*
|
|
59
|
+
* **Properties added to SymmetricRelationship:**
|
|
60
|
+
* - `leftContextName` - Resolved name of left context (handles `this`)
|
|
61
|
+
* - `rightContextName` - Resolved name of right context (handles `this`)
|
|
58
62
|
*
|
|
59
63
|
* @module sdk/ast-augmentation
|
|
60
64
|
*/
|
|
@@ -109,9 +113,9 @@ declare module '../generated/ast.js' {
|
|
|
109
113
|
}
|
|
110
114
|
|
|
111
115
|
/**
|
|
112
|
-
* Augmented
|
|
116
|
+
* Augmented DirectionalRelationship with SDK helper methods.
|
|
113
117
|
*/
|
|
114
|
-
interface
|
|
118
|
+
interface DirectionalRelationship {
|
|
115
119
|
/** Resolved name of left context (handles 'this') */
|
|
116
120
|
readonly leftContextName: string;
|
|
117
121
|
/** Resolved name of right context (handles 'this') */
|
|
@@ -120,36 +124,41 @@ declare module '../generated/ast.js' {
|
|
|
120
124
|
readonly isBidirectional: boolean;
|
|
121
125
|
|
|
122
126
|
/**
|
|
123
|
-
* Checks if the relationship has a specific
|
|
124
|
-
* Accepts
|
|
125
|
-
* @param pattern - Pattern abbreviation or full name
|
|
127
|
+
* Checks if the relationship has a specific side pattern on either side.
|
|
128
|
+
* Accepts pattern $type names like 'OpenHostService', 'Conformist', etc.
|
|
126
129
|
*/
|
|
127
|
-
hasPattern(
|
|
130
|
+
hasPattern(patternType: string): boolean;
|
|
128
131
|
|
|
129
132
|
/**
|
|
130
|
-
* Checks if the left side has a specific
|
|
131
|
-
* @param pattern - Pattern abbreviation or full name
|
|
133
|
+
* Checks if the left side has a specific side pattern.
|
|
132
134
|
*/
|
|
133
|
-
hasLeftPattern(
|
|
135
|
+
hasLeftPattern(patternType: string): boolean;
|
|
134
136
|
|
|
135
137
|
/**
|
|
136
|
-
* Checks if the right side has a specific
|
|
137
|
-
* @param pattern - Pattern abbreviation or full name
|
|
138
|
+
* Checks if the right side has a specific side pattern.
|
|
138
139
|
*/
|
|
139
|
-
hasRightPattern(
|
|
140
|
+
hasRightPattern(patternType: string): boolean;
|
|
140
141
|
|
|
141
142
|
/**
|
|
142
143
|
* Checks if the specified side is upstream (provider) in this relationship.
|
|
143
|
-
* @param side - 'left' or 'right'
|
|
144
144
|
*/
|
|
145
145
|
isUpstream(side: 'left' | 'right'): boolean;
|
|
146
146
|
|
|
147
147
|
/**
|
|
148
148
|
* Checks if the specified side is downstream (consumer) in this relationship.
|
|
149
|
-
* @param side - 'left' or 'right'
|
|
150
149
|
*/
|
|
151
150
|
isDownstream(side: 'left' | 'right'): boolean;
|
|
152
151
|
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Augmented SymmetricRelationship with SDK helper methods.
|
|
155
|
+
*/
|
|
156
|
+
interface SymmetricRelationship {
|
|
157
|
+
/** Resolved name of left context (handles 'this') */
|
|
158
|
+
readonly leftContextName: string;
|
|
159
|
+
/** Resolved name of right context (handles 'this') */
|
|
160
|
+
readonly rightContextName: string;
|
|
161
|
+
}
|
|
153
162
|
}
|
|
154
163
|
|
|
155
164
|
// Export nothing - this file is purely for type augmentation
|
package/src/sdk/index.ts
CHANGED
|
@@ -116,14 +116,19 @@ export { fromModel, fromDocument, fromServices, augmentModel } from './query.js'
|
|
|
116
116
|
export {
|
|
117
117
|
Pattern,
|
|
118
118
|
PatternFullName,
|
|
119
|
+
PatternAbbreviation,
|
|
119
120
|
PatternAliases,
|
|
120
121
|
matchesPattern,
|
|
121
122
|
isUpstreamPattern,
|
|
122
123
|
isDownstreamPattern,
|
|
123
124
|
isMutualPattern,
|
|
125
|
+
isUpstreamSidePattern,
|
|
126
|
+
isDownstreamSidePattern,
|
|
127
|
+
isBBoMSidePattern,
|
|
128
|
+
getPatternAbbreviation,
|
|
124
129
|
UpstreamPatterns,
|
|
125
130
|
DownstreamPatterns,
|
|
126
|
-
|
|
131
|
+
SymmetricPatterns,
|
|
127
132
|
} from './patterns.js';
|
|
128
133
|
|
|
129
134
|
export type { IntegrationPattern } from './patterns.js';
|
|
@@ -140,6 +145,11 @@ export type {
|
|
|
140
145
|
LoadOptions,
|
|
141
146
|
BcQueryBuilder,
|
|
142
147
|
RelationshipView,
|
|
148
|
+
RelationshipSide,
|
|
149
|
+
DirectionalRelationshipView,
|
|
150
|
+
DirectionalKind,
|
|
151
|
+
SymmetricRelationshipView,
|
|
152
|
+
SymmetricKind,
|
|
143
153
|
} from './types.js';
|
|
144
154
|
|
|
145
155
|
// Serializers for tool responses (browser-safe)
|
package/src/sdk/loader-node.ts
CHANGED
|
@@ -62,7 +62,7 @@ export async function loadModel(
|
|
|
62
62
|
options?: LoadOptions
|
|
63
63
|
): Promise<QueryContext> {
|
|
64
64
|
// Resolve absolute path
|
|
65
|
-
const path = await import('path');
|
|
65
|
+
const path = await import('node:path');
|
|
66
66
|
const absolutePath = path.isAbsolute(entryFile)
|
|
67
67
|
? entryFile
|
|
68
68
|
: path.resolve(options?.workspaceDir ?? process.cwd(), entryFile);
|
|
@@ -82,7 +82,7 @@ export async function loadModel(
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
// Read file content and create document
|
|
85
|
-
const fs = await import('fs/promises');
|
|
85
|
+
const fs = await import('node:fs/promises');
|
|
86
86
|
const fileContent = await fs.readFile(absolutePath, 'utf-8');
|
|
87
87
|
const uri = URI.file(absolutePath);
|
|
88
88
|
|
package/src/sdk/patterns.ts
CHANGED
|
@@ -1,147 +1,185 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* }
|
|
15
|
-
* ```
|
|
16
|
-
*/
|
|
1
|
+
import type { SidePattern, SymmetricPattern } from '../generated/ast.js';
|
|
2
|
+
import {
|
|
3
|
+
isOpenHostService,
|
|
4
|
+
isPublishedLanguage,
|
|
5
|
+
isConformist,
|
|
6
|
+
isAntiCorruptionLayer,
|
|
7
|
+
isSupplier,
|
|
8
|
+
isCustomer,
|
|
9
|
+
isBigBallOfMud,
|
|
10
|
+
isSharedKernel,
|
|
11
|
+
isPartnership,
|
|
12
|
+
isSeparateWays,
|
|
13
|
+
} from '../generated/ast.js';
|
|
17
14
|
|
|
18
15
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
16
|
+
* Pattern constants for programmatic use.
|
|
17
|
+
* Values match the AST $type names.
|
|
21
18
|
*/
|
|
22
19
|
export const Pattern = {
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
// Mutual patterns (both sides)
|
|
36
|
-
/** Shared Kernel - shared code/model ownership */
|
|
37
|
-
SK: 'SK',
|
|
38
|
-
/** Partnership - coordinated development */
|
|
39
|
-
P: 'P',
|
|
40
|
-
|
|
41
|
-
// Relationship types
|
|
42
|
-
/** Customer/Supplier - negotiated contract */
|
|
43
|
-
CustomerSupplier: 'Customer/Supplier',
|
|
44
|
-
/** Separate Ways - no integration */
|
|
45
|
-
SeparateWays: 'Separate Ways',
|
|
46
|
-
/** Big Ball of Mud - legacy or unstructured */
|
|
47
|
-
BigBallOfMud: 'Big Ball of Mud',
|
|
20
|
+
// Side patterns (directional)
|
|
21
|
+
OHS: 'OpenHostService',
|
|
22
|
+
PL: 'PublishedLanguage',
|
|
23
|
+
CF: 'Conformist',
|
|
24
|
+
ACL: 'AntiCorruptionLayer',
|
|
25
|
+
S: 'Supplier',
|
|
26
|
+
C: 'Customer',
|
|
27
|
+
BBoM: 'BigBallOfMud',
|
|
28
|
+
// Symmetric patterns
|
|
29
|
+
SK: 'SharedKernel',
|
|
30
|
+
P: 'Partnership',
|
|
31
|
+
SW: 'SeparateWays',
|
|
48
32
|
} as const;
|
|
49
33
|
|
|
50
34
|
/**
|
|
51
|
-
*
|
|
52
|
-
* Used when patterns are spelled out in documentation blocks.
|
|
35
|
+
* Mapping from short abbreviation to full $type name.
|
|
53
36
|
*/
|
|
54
|
-
export const PatternFullName = {
|
|
37
|
+
export const PatternFullName: Record<string, string> = {
|
|
55
38
|
OHS: 'OpenHostService',
|
|
56
39
|
PL: 'PublishedLanguage',
|
|
57
40
|
CF: 'Conformist',
|
|
58
41
|
ACL: 'AntiCorruptionLayer',
|
|
42
|
+
S: 'Supplier',
|
|
43
|
+
C: 'Customer',
|
|
44
|
+
BBoM: 'BigBallOfMud',
|
|
59
45
|
SK: 'SharedKernel',
|
|
60
46
|
P: 'Partnership',
|
|
61
|
-
|
|
47
|
+
SW: 'SeparateWays',
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Mapping from $type name to short abbreviation.
|
|
52
|
+
*/
|
|
53
|
+
export const PatternAbbreviation: Record<string, string> = {
|
|
54
|
+
OpenHostService: 'OHS',
|
|
55
|
+
PublishedLanguage: 'PL',
|
|
56
|
+
Conformist: 'CF',
|
|
57
|
+
AntiCorruptionLayer: 'ACL',
|
|
58
|
+
Supplier: 'S',
|
|
59
|
+
Customer: 'C',
|
|
60
|
+
BigBallOfMud: 'BBoM',
|
|
61
|
+
SharedKernel: 'SK',
|
|
62
|
+
Partnership: 'P',
|
|
63
|
+
SeparateWays: 'SW',
|
|
64
|
+
};
|
|
62
65
|
|
|
63
66
|
/**
|
|
64
|
-
*
|
|
67
|
+
* All short+long forms that map to a given canonical $type name.
|
|
65
68
|
*/
|
|
66
69
|
export const PatternAliases: Record<string, readonly string[]> = {
|
|
67
|
-
// Abbreviation -> [abbreviation, fullName]
|
|
68
70
|
OHS: ['OHS', 'OpenHostService'],
|
|
69
71
|
PL: ['PL', 'PublishedLanguage'],
|
|
70
72
|
CF: ['CF', 'Conformist'],
|
|
71
73
|
ACL: ['ACL', 'AntiCorruptionLayer'],
|
|
74
|
+
S: ['S', 'Supplier'],
|
|
75
|
+
C: ['C', 'Customer'],
|
|
76
|
+
BBoM: ['BBoM', 'BigBallOfMud'],
|
|
72
77
|
SK: ['SK', 'SharedKernel'],
|
|
73
78
|
P: ['P', 'Partnership'],
|
|
74
|
-
|
|
75
|
-
// Full names map to same
|
|
79
|
+
SW: ['SW', 'SeparateWays'],
|
|
76
80
|
OpenHostService: ['OHS', 'OpenHostService'],
|
|
77
81
|
PublishedLanguage: ['PL', 'PublishedLanguage'],
|
|
78
82
|
Conformist: ['CF', 'Conformist'],
|
|
79
83
|
AntiCorruptionLayer: ['ACL', 'AntiCorruptionLayer'],
|
|
84
|
+
Supplier: ['S', 'Supplier'],
|
|
85
|
+
Customer: ['C', 'Customer'],
|
|
86
|
+
BigBallOfMud: ['BBoM', 'BigBallOfMud'],
|
|
80
87
|
SharedKernel: ['SK', 'SharedKernel'],
|
|
81
88
|
Partnership: ['P', 'Partnership'],
|
|
89
|
+
SeparateWays: ['SW', 'SeparateWays'],
|
|
82
90
|
};
|
|
83
91
|
|
|
84
|
-
/**
|
|
85
|
-
|
|
86
|
-
*/
|
|
87
|
-
export type IntegrationPattern =
|
|
88
|
-
| typeof Pattern[keyof typeof Pattern]
|
|
89
|
-
| typeof PatternFullName[keyof typeof PatternFullName];
|
|
92
|
+
/** Union of all pattern type names */
|
|
93
|
+
export type IntegrationPattern = typeof Pattern[keyof typeof Pattern];
|
|
90
94
|
|
|
91
95
|
/**
|
|
92
|
-
* Checks if a pattern
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
* @param actual - Pattern string from the AST
|
|
96
|
-
* @param expected - Pattern to match (abbreviation or full name)
|
|
97
|
-
* @returns true if patterns match
|
|
96
|
+
* Checks if a pattern name matches an expected pattern.
|
|
97
|
+
* Works with both $type names and short abbreviations.
|
|
98
98
|
*/
|
|
99
99
|
export function matchesPattern(actual: string, expected: string): boolean {
|
|
100
100
|
const normalizedActual = actual.trim();
|
|
101
101
|
const aliases = PatternAliases[expected];
|
|
102
|
-
|
|
103
102
|
if (aliases) {
|
|
104
103
|
return aliases.some(alias =>
|
|
105
104
|
alias.toLowerCase() === normalizedActual.toLowerCase()
|
|
106
105
|
);
|
|
107
106
|
}
|
|
108
|
-
|
|
109
|
-
// Fallback: direct comparison
|
|
110
107
|
return normalizedActual.toLowerCase() === expected.toLowerCase();
|
|
111
108
|
}
|
|
112
109
|
|
|
110
|
+
/** Side patterns that belong on the upstream side */
|
|
111
|
+
export const UpstreamPatterns: readonly string[] = ['OpenHostService', 'PublishedLanguage', 'Supplier'];
|
|
112
|
+
/** Side patterns that belong on the downstream side */
|
|
113
|
+
export const DownstreamPatterns: readonly string[] = ['Conformist', 'AntiCorruptionLayer', 'Customer'];
|
|
114
|
+
/** Symmetric patterns (mutual) */
|
|
115
|
+
export const SymmetricPatterns: readonly string[] = ['SharedKernel', 'Partnership', 'SeparateWays'];
|
|
116
|
+
|
|
113
117
|
/**
|
|
114
|
-
*
|
|
118
|
+
* Checks if a side pattern AST node is an upstream pattern.
|
|
115
119
|
*/
|
|
116
|
-
export
|
|
120
|
+
export function isUpstreamSidePattern(pattern: SidePattern): boolean {
|
|
121
|
+
return isOpenHostService(pattern) || isPublishedLanguage(pattern) || isSupplier(pattern);
|
|
122
|
+
}
|
|
117
123
|
|
|
118
124
|
/**
|
|
119
|
-
*
|
|
125
|
+
* Checks if a side pattern AST node is a downstream pattern.
|
|
120
126
|
*/
|
|
121
|
-
export
|
|
127
|
+
export function isDownstreamSidePattern(pattern: SidePattern): boolean {
|
|
128
|
+
return isConformist(pattern) || isAntiCorruptionLayer(pattern) || isCustomer(pattern);
|
|
129
|
+
}
|
|
122
130
|
|
|
123
131
|
/**
|
|
124
|
-
*
|
|
132
|
+
* Checks if a side pattern AST node is a Big Ball of Mud.
|
|
125
133
|
*/
|
|
126
|
-
export
|
|
134
|
+
export function isBBoMSidePattern(pattern: SidePattern): boolean {
|
|
135
|
+
return isBigBallOfMud(pattern);
|
|
136
|
+
}
|
|
127
137
|
|
|
128
138
|
/**
|
|
129
|
-
* Checks if a pattern is
|
|
139
|
+
* Checks if a pattern string name is an upstream pattern.
|
|
130
140
|
*/
|
|
131
141
|
export function isUpstreamPattern(pattern: string): boolean {
|
|
132
142
|
return UpstreamPatterns.some(p => matchesPattern(pattern, p));
|
|
133
143
|
}
|
|
134
144
|
|
|
135
145
|
/**
|
|
136
|
-
* Checks if a pattern is
|
|
146
|
+
* Checks if a pattern string name is a downstream pattern.
|
|
137
147
|
*/
|
|
138
148
|
export function isDownstreamPattern(pattern: string): boolean {
|
|
139
149
|
return DownstreamPatterns.some(p => matchesPattern(pattern, p));
|
|
140
150
|
}
|
|
141
151
|
|
|
142
152
|
/**
|
|
143
|
-
* Checks if a pattern
|
|
153
|
+
* Checks if a pattern string name is a mutual/symmetric pattern.
|
|
144
154
|
*/
|
|
145
155
|
export function isMutualPattern(pattern: string): boolean {
|
|
146
|
-
return
|
|
156
|
+
return SymmetricPatterns.some(p => matchesPattern(pattern, p));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Gets the short abbreviation for a pattern $type name.
|
|
161
|
+
*/
|
|
162
|
+
export function getPatternAbbreviation(typeName: string): string {
|
|
163
|
+
return PatternAbbreviation[typeName] ?? typeName;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Checks if a symmetric pattern is a Shared Kernel.
|
|
168
|
+
*/
|
|
169
|
+
export function isSharedKernelPattern(pattern: SymmetricPattern): boolean {
|
|
170
|
+
return isSharedKernel(pattern);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Checks if a symmetric pattern is a Partnership.
|
|
175
|
+
*/
|
|
176
|
+
export function isPartnershipPattern(pattern: SymmetricPattern): boolean {
|
|
177
|
+
return isPartnership(pattern);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Checks if a symmetric pattern is Separate Ways.
|
|
182
|
+
*/
|
|
183
|
+
export function isSeparateWaysPattern(pattern: SymmetricPattern): boolean {
|
|
184
|
+
return isSeparateWays(pattern);
|
|
147
185
|
}
|
package/src/sdk/query.ts
CHANGED
|
@@ -21,12 +21,16 @@ import {
|
|
|
21
21
|
isBoundedContext,
|
|
22
22
|
isClassification,
|
|
23
23
|
isContextMap,
|
|
24
|
+
isDirectionalRelationship,
|
|
24
25
|
isDomain,
|
|
25
26
|
isDomainMap,
|
|
26
27
|
isModel,
|
|
27
28
|
isNamespaceDeclaration,
|
|
29
|
+
isSymmetricRelationship,
|
|
28
30
|
isTeam,
|
|
29
31
|
isThisRef,
|
|
32
|
+
isSupplier,
|
|
33
|
+
isCustomer,
|
|
30
34
|
} from '../generated/ast.js';
|
|
31
35
|
import { QualifiedNameProvider } from '../lsp/domain-lang-naming.js';
|
|
32
36
|
import type { DomainLangServices } from '../domain-lang-module.js';
|
|
@@ -39,6 +43,7 @@ import {
|
|
|
39
43
|
import { isDownstreamPattern, isUpstreamPattern, matchesPattern } from './patterns.js';
|
|
40
44
|
import type {
|
|
41
45
|
BcQueryBuilder,
|
|
46
|
+
DirectionalKind,
|
|
42
47
|
ModelIndexes,
|
|
43
48
|
Query,
|
|
44
49
|
QueryBuilder,
|
|
@@ -331,14 +336,39 @@ class QueryImpl implements Query {
|
|
|
331
336
|
return undefined;
|
|
332
337
|
}
|
|
333
338
|
|
|
339
|
+
if (isSymmetricRelationship(rel)) {
|
|
340
|
+
// Grammar invariant: either `arrow === '><'` (SeparateWays shorthand, pattern undefined)
|
|
341
|
+
// OR `pattern` is set (explicit [SK]/[P]/[SW] brackets). Never both; never neither
|
|
342
|
+
// (validation rejects a bare "A B" without pattern or arrow).
|
|
343
|
+
// The fallback to 'SeparateWays' correctly handles the `><` case.
|
|
344
|
+
return {
|
|
345
|
+
type: 'symmetric' as const,
|
|
346
|
+
kind: (rel.pattern?.$type ?? 'SeparateWays') as 'SharedKernel' | 'Partnership' | 'SeparateWays',
|
|
347
|
+
left: { context: left, patterns: [] },
|
|
348
|
+
right: { context: right, patterns: [] },
|
|
349
|
+
source,
|
|
350
|
+
astNode: rel,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const arrow = rel.arrow as '->' | '<-' | '<->';
|
|
355
|
+
const leftSide = { context: left, patterns: rel.leftPatterns };
|
|
356
|
+
const rightSide = { context: right, patterns: rel.rightPatterns };
|
|
357
|
+
const hasSupplier = rel.leftPatterns.some(isSupplier) || rel.rightPatterns.some(isSupplier);
|
|
358
|
+
const hasCustomer = rel.leftPatterns.some(isCustomer) || rel.rightPatterns.some(isCustomer);
|
|
359
|
+
const kind: DirectionalKind = arrow === '<->'
|
|
360
|
+
? 'Bidirectional'
|
|
361
|
+
: (hasSupplier || hasCustomer) ? 'CustomerSupplier' : 'UpstreamDownstream';
|
|
362
|
+
const upstreamSide = arrow === '->' ? leftSide : arrow === '<-' ? rightSide : undefined;
|
|
363
|
+
const downstreamSide = arrow === '->' ? rightSide : arrow === '<-' ? leftSide : undefined;
|
|
334
364
|
return {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
arrow
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
365
|
+
type: 'directional' as const,
|
|
366
|
+
kind,
|
|
367
|
+
arrow,
|
|
368
|
+
left: leftSide,
|
|
369
|
+
right: rightSide,
|
|
370
|
+
upstream: upstreamSide,
|
|
371
|
+
downstream: downstreamSide,
|
|
342
372
|
source,
|
|
343
373
|
astNode: rel,
|
|
344
374
|
};
|
|
@@ -358,41 +388,6 @@ class QueryImpl implements Query {
|
|
|
358
388
|
return ref.link?.ref;
|
|
359
389
|
}
|
|
360
390
|
|
|
361
|
-
/**
|
|
362
|
-
* Infers relationship type from integration patterns.
|
|
363
|
-
* Simple heuristic based on common DDD pattern combinations.
|
|
364
|
-
*/
|
|
365
|
-
private inferRelationshipType(rel: Relationship): string | undefined {
|
|
366
|
-
const leftPatterns = rel.leftPatterns;
|
|
367
|
-
const rightPatterns = rel.rightPatterns;
|
|
368
|
-
|
|
369
|
-
// Partnership: Bidirectional with P or SK
|
|
370
|
-
if (rel.arrow === '<->' && (leftPatterns.includes('P') || leftPatterns.includes('SK'))) {
|
|
371
|
-
return 'Partnership';
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// Shared Kernel: SK pattern
|
|
375
|
-
if (leftPatterns.includes('SK') || rightPatterns.includes('SK')) {
|
|
376
|
-
return 'SharedKernel';
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Customer-Supplier: OHS + CF
|
|
380
|
-
if (leftPatterns.includes('OHS') && rightPatterns.includes('CF')) {
|
|
381
|
-
return 'CustomerSupplier';
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// Upstream-Downstream: directional arrow
|
|
385
|
-
if (rel.arrow === '->') {
|
|
386
|
-
return 'UpstreamDownstream';
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// Separate Ways
|
|
390
|
-
if (rel.arrow === '><') {
|
|
391
|
-
return 'SeparateWays';
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
return undefined;
|
|
395
|
-
}
|
|
396
391
|
}
|
|
397
392
|
|
|
398
393
|
/**
|
|
@@ -726,38 +721,52 @@ export function augmentRelationship(rel: Relationship, containingBc?: BoundedCon
|
|
|
726
721
|
// Helper methods for pattern matching (type-safe, no magic strings)
|
|
727
722
|
hasPattern: {
|
|
728
723
|
value: (pattern: string): boolean => {
|
|
729
|
-
|
|
730
|
-
|
|
724
|
+
if (isDirectionalRelationship(rel)) {
|
|
725
|
+
return rel.leftPatterns.some(p => matchesPattern(p.$type, pattern)) ||
|
|
726
|
+
rel.rightPatterns.some(p => matchesPattern(p.$type, pattern));
|
|
727
|
+
}
|
|
728
|
+
if (isSymmetricRelationship(rel) && rel.pattern) {
|
|
729
|
+
return matchesPattern(rel.pattern.$type, pattern);
|
|
730
|
+
}
|
|
731
|
+
return false;
|
|
731
732
|
},
|
|
732
733
|
enumerable: false,
|
|
733
734
|
configurable: true,
|
|
734
735
|
},
|
|
735
736
|
hasLeftPattern: {
|
|
736
737
|
value: (pattern: string): boolean => {
|
|
737
|
-
|
|
738
|
+
if (isDirectionalRelationship(rel)) {
|
|
739
|
+
return rel.leftPatterns.some(p => matchesPattern(p.$type, pattern));
|
|
740
|
+
}
|
|
741
|
+
return false;
|
|
738
742
|
},
|
|
739
743
|
enumerable: false,
|
|
740
744
|
configurable: true,
|
|
741
745
|
},
|
|
742
746
|
hasRightPattern: {
|
|
743
747
|
value: (pattern: string): boolean => {
|
|
744
|
-
|
|
748
|
+
if (isDirectionalRelationship(rel)) {
|
|
749
|
+
return rel.rightPatterns.some(p => matchesPattern(p.$type, pattern));
|
|
750
|
+
}
|
|
751
|
+
return false;
|
|
745
752
|
},
|
|
746
753
|
enumerable: false,
|
|
747
754
|
configurable: true,
|
|
748
755
|
},
|
|
749
756
|
isUpstream: {
|
|
750
757
|
value: (side: 'left' | 'right'): boolean => {
|
|
758
|
+
if (!isDirectionalRelationship(rel)) return false;
|
|
751
759
|
const patterns = side === 'left' ? rel.leftPatterns : rel.rightPatterns;
|
|
752
|
-
return patterns.some(p => isUpstreamPattern(p));
|
|
760
|
+
return patterns.some(p => isUpstreamPattern(p.$type));
|
|
753
761
|
},
|
|
754
762
|
enumerable: false,
|
|
755
763
|
configurable: true,
|
|
756
764
|
},
|
|
757
765
|
isDownstream: {
|
|
758
766
|
value: (side: 'left' | 'right'): boolean => {
|
|
767
|
+
if (!isDirectionalRelationship(rel)) return false;
|
|
759
768
|
const patterns = side === 'left' ? rel.leftPatterns : rel.rightPatterns;
|
|
760
|
-
return patterns.some(p => isDownstreamPattern(p));
|
|
769
|
+
return patterns.some(p => isDownstreamPattern(p.$type));
|
|
761
770
|
},
|
|
762
771
|
enumerable: false,
|
|
763
772
|
configurable: true,
|
package/src/sdk/serializers.ts
CHANGED
|
@@ -165,18 +165,31 @@ export function serializeNode(node: AstNode, query: Query): Record<string, unkno
|
|
|
165
165
|
* @returns Serialized relationship object
|
|
166
166
|
*/
|
|
167
167
|
export function serializeRelationship(view: RelationshipView): Record<string, unknown> {
|
|
168
|
-
|
|
169
|
-
const
|
|
170
|
-
|
|
168
|
+
const leftName = view.left.context.name;
|
|
169
|
+
const rightName = view.right.context.name;
|
|
170
|
+
if (view.type === 'symmetric') {
|
|
171
|
+
const patternDisplay = view.kind === 'SeparateWays' ? '><' : `[${view.kind}]`;
|
|
172
|
+
return {
|
|
173
|
+
type: 'symmetric',
|
|
174
|
+
name: `${leftName} ${patternDisplay} ${rightName}`,
|
|
175
|
+
left: leftName,
|
|
176
|
+
right: rightName,
|
|
177
|
+
kind: view.kind,
|
|
178
|
+
source: view.source,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
171
181
|
return {
|
|
172
|
-
|
|
182
|
+
type: 'directional',
|
|
183
|
+
kind: view.kind,
|
|
173
184
|
name: `${leftName} ${view.arrow} ${rightName}`,
|
|
174
185
|
left: leftName,
|
|
175
186
|
right: rightName,
|
|
176
187
|
arrow: view.arrow,
|
|
177
|
-
leftPatterns: view.
|
|
178
|
-
rightPatterns: view.
|
|
179
|
-
|
|
188
|
+
leftPatterns: view.left.patterns.map(p => p.$type),
|
|
189
|
+
rightPatterns: view.right.patterns.map(p => p.$type),
|
|
190
|
+
upstreamPatterns: view.upstream?.patterns.map(p => p.$type),
|
|
191
|
+
downstreamPatterns: view.downstream?.patterns.map(p => p.$type),
|
|
192
|
+
source: view.source,
|
|
180
193
|
};
|
|
181
194
|
}
|
|
182
195
|
|