@domainlang/language 0.7.0 → 0.9.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.
Files changed (88) hide show
  1. package/out/domain-lang-module.d.ts +2 -0
  2. package/out/domain-lang-module.js +21 -2
  3. package/out/domain-lang-module.js.map +1 -1
  4. package/out/lsp/domain-lang-completion.d.ts +142 -1
  5. package/out/lsp/domain-lang-completion.js +620 -22
  6. package/out/lsp/domain-lang-completion.js.map +1 -1
  7. package/out/lsp/domain-lang-document-symbol-provider.d.ts +79 -0
  8. package/out/lsp/domain-lang-document-symbol-provider.js +210 -0
  9. package/out/lsp/domain-lang-document-symbol-provider.js.map +1 -0
  10. package/out/lsp/domain-lang-index-manager.d.ts +34 -5
  11. package/out/lsp/domain-lang-index-manager.js +66 -27
  12. package/out/lsp/domain-lang-index-manager.js.map +1 -1
  13. package/out/lsp/domain-lang-node-kind-provider.d.ts +27 -0
  14. package/out/lsp/domain-lang-node-kind-provider.js +87 -0
  15. package/out/lsp/domain-lang-node-kind-provider.js.map +1 -0
  16. package/out/lsp/domain-lang-scope-provider.d.ts +53 -20
  17. package/out/lsp/domain-lang-scope-provider.js +119 -44
  18. package/out/lsp/domain-lang-scope-provider.js.map +1 -1
  19. package/out/lsp/domain-lang-workspace-manager.d.ts +23 -2
  20. package/out/lsp/domain-lang-workspace-manager.js +51 -6
  21. package/out/lsp/domain-lang-workspace-manager.js.map +1 -1
  22. package/out/lsp/hover/domain-lang-hover.d.ts +16 -6
  23. package/out/lsp/hover/domain-lang-hover.js +160 -134
  24. package/out/lsp/hover/domain-lang-hover.js.map +1 -1
  25. package/out/lsp/hover/hover-builders.d.ts +57 -0
  26. package/out/lsp/hover/hover-builders.js +171 -0
  27. package/out/lsp/hover/hover-builders.js.map +1 -0
  28. package/out/main.js +2 -1
  29. package/out/main.js.map +1 -1
  30. package/out/sdk/index.d.ts +31 -11
  31. package/out/sdk/index.js +30 -11
  32. package/out/sdk/index.js.map +1 -1
  33. package/out/sdk/loader-node.d.ts +2 -0
  34. package/out/sdk/loader-node.js +3 -1
  35. package/out/sdk/loader-node.js.map +1 -1
  36. package/out/sdk/loader.d.ts +55 -2
  37. package/out/sdk/loader.js +87 -28
  38. package/out/sdk/loader.js.map +1 -1
  39. package/out/sdk/query.js +14 -11
  40. package/out/sdk/query.js.map +1 -1
  41. package/out/sdk/validator.d.ts +134 -0
  42. package/out/sdk/validator.js +249 -0
  43. package/out/sdk/validator.js.map +1 -0
  44. package/out/services/package-boundary-detector.d.ts +101 -0
  45. package/out/services/package-boundary-detector.js +211 -0
  46. package/out/services/package-boundary-detector.js.map +1 -0
  47. package/out/services/performance-optimizer.js +6 -2
  48. package/out/services/performance-optimizer.js.map +1 -1
  49. package/out/services/types.d.ts +24 -0
  50. package/out/services/types.js.map +1 -1
  51. package/out/services/workspace-manager.d.ts +73 -6
  52. package/out/services/workspace-manager.js +210 -57
  53. package/out/services/workspace-manager.js.map +1 -1
  54. package/out/utils/import-utils.d.ts +9 -6
  55. package/out/utils/import-utils.js +26 -15
  56. package/out/utils/import-utils.js.map +1 -1
  57. package/out/validation/constants.d.ts +7 -0
  58. package/out/validation/constants.js +21 -3
  59. package/out/validation/constants.js.map +1 -1
  60. package/out/validation/import.d.ts +11 -1
  61. package/out/validation/import.js +42 -14
  62. package/out/validation/import.js.map +1 -1
  63. package/out/validation/maps.js +50 -1
  64. package/out/validation/maps.js.map +1 -1
  65. package/package.json +8 -9
  66. package/src/domain-lang-module.ts +24 -3
  67. package/src/lsp/domain-lang-completion.ts +736 -27
  68. package/src/lsp/domain-lang-document-symbol-provider.ts +254 -0
  69. package/src/lsp/domain-lang-index-manager.ts +79 -27
  70. package/src/lsp/domain-lang-node-kind-provider.ts +119 -0
  71. package/src/lsp/domain-lang-scope-provider.ts +171 -55
  72. package/src/lsp/domain-lang-workspace-manager.ts +64 -6
  73. package/src/lsp/hover/domain-lang-hover.ts +189 -131
  74. package/src/lsp/hover/hover-builders.ts +208 -0
  75. package/src/main.ts +3 -1
  76. package/src/sdk/index.ts +33 -11
  77. package/src/sdk/loader-node.ts +6 -1
  78. package/src/sdk/loader.ts +125 -34
  79. package/src/sdk/query.ts +15 -11
  80. package/src/sdk/validator.ts +358 -0
  81. package/src/services/package-boundary-detector.ts +238 -0
  82. package/src/services/performance-optimizer.ts +6 -2
  83. package/src/services/types.ts +25 -0
  84. package/src/services/workspace-manager.ts +259 -62
  85. package/src/utils/import-utils.ts +27 -15
  86. package/src/validation/constants.ts +23 -6
  87. package/src/validation/import.ts +49 -14
  88. package/src/validation/maps.ts +59 -2
@@ -78,35 +78,36 @@ export class ImportValidator {
78
78
  return;
79
79
  }
80
80
 
81
- const alias = imp.uri.split('/')[0];
82
- const dependency = this.getDependency(manifest, alias);
81
+ // Find the matching dependency by key (owner/package format)
82
+ const match = this.findDependency(manifest, imp.uri);
83
83
 
84
- if (!dependency) {
85
- accept('error', ValidationMessages.IMPORT_NOT_IN_MANIFEST(alias), {
84
+ if (!match) {
85
+ accept('error', ValidationMessages.IMPORT_NOT_IN_MANIFEST(imp.uri), {
86
86
  node: imp,
87
87
  property: 'uri',
88
88
  codeDescription: buildCodeDescription('language.md', 'imports'),
89
- data: { code: IssueCodes.ImportNotInManifest, alias }
89
+ data: { code: IssueCodes.ImportNotInManifest, alias: imp.uri }
90
90
  });
91
91
  return;
92
92
  }
93
93
 
94
- this.validateDependencyConfig(dependency, alias, accept, imp);
94
+ const { key, dependency } = match;
95
+ this.validateDependencyConfig(dependency, key, accept, imp);
95
96
 
96
97
  // External source dependencies require lock file and cached packages
97
98
  if (dependency.source) {
98
99
  const lockFile = await this.workspaceManager.getLockFile();
99
100
  if (!lockFile) {
100
- accept('error', ValidationMessages.IMPORT_NOT_INSTALLED(alias), {
101
+ accept('error', ValidationMessages.IMPORT_NOT_INSTALLED(key), {
101
102
  node: imp,
102
103
  property: 'uri',
103
104
  codeDescription: buildCodeDescription('language.md', 'imports'),
104
- data: { code: IssueCodes.ImportNotInstalled, alias }
105
+ data: { code: IssueCodes.ImportNotInstalled, alias: key }
105
106
  });
106
107
  return;
107
108
  }
108
109
 
109
- await this.validateCachedPackage(dependency, alias, lockFile, accept, imp);
110
+ await this.validateCachedPackage(dependency, key, lockFile, accept, imp);
110
111
  }
111
112
  }
112
113
 
@@ -186,20 +187,54 @@ export class ImportValidator {
186
187
  }
187
188
 
188
189
  /**
189
- * Gets the normalized dependency configuration for an alias.
190
+ * Finds the dependency configuration that matches the import specifier.
191
+ *
192
+ * Dependencies can be keyed as:
193
+ * - owner/package (recommended, matches "owner/package" or "owner/package/subpath")
194
+ * - short-alias (matches "short-alias" or "short-alias/subpath")
195
+ *
196
+ * @returns The matching key and normalized dependency, or undefined if not found
197
+ */
198
+ private findDependency(
199
+ manifest: ModelManifest,
200
+ specifier: string
201
+ ): { key: string; dependency: ExtendedDependencySpec } | undefined {
202
+ const dependencies = manifest.dependencies;
203
+ if (!dependencies) {
204
+ return undefined;
205
+ }
206
+
207
+ // Sort keys by length descending to match most specific first
208
+ const sortedKeys = Object.keys(dependencies).sort((a, b) => b.length - a.length);
209
+
210
+ for (const key of sortedKeys) {
211
+ // Exact match or prefix match (key followed by /)
212
+ if (specifier === key || specifier.startsWith(`${key}/`)) {
213
+ const dependency = this.getDependency(manifest, key);
214
+ if (dependency) {
215
+ return { key, dependency };
216
+ }
217
+ }
218
+ }
219
+
220
+ return undefined;
221
+ }
222
+
223
+ /**
224
+ * Gets the normalized dependency configuration for a key.
190
225
  */
191
- private getDependency(manifest: ModelManifest, alias: string): ExtendedDependencySpec | undefined {
192
- const dep = manifest.dependencies?.[alias];
226
+ private getDependency(manifest: ModelManifest, key: string): ExtendedDependencySpec | undefined {
227
+ const dep = manifest.dependencies?.[key];
193
228
  if (!dep) {
194
229
  return undefined;
195
230
  }
196
231
 
197
232
  if (typeof dep === 'string') {
198
- return { source: alias, ref: dep };
233
+ return { source: key, ref: dep };
199
234
  }
200
235
 
201
236
  if (!dep.source && !dep.path) {
202
- return { ...dep, source: alias };
237
+ return { ...dep, source: key };
203
238
  }
204
239
 
205
240
  return dep;
@@ -1,5 +1,6 @@
1
1
  import type { ValidationAcceptor } from 'langium';
2
- import type { ContextMap, DomainMap } from '../generated/ast.js';
2
+ import type { ContextMap, DomainMap, Relationship, BoundedContextRef } from '../generated/ast.js';
3
+ import { isThisRef } from '../generated/ast.js';
3
4
  import { ValidationMessages, buildCodeDescription, IssueCodes } from './constants.js';
4
5
 
5
6
  /**
@@ -121,10 +122,66 @@ function validateDomainMapReferences(
121
122
  }
122
123
  }
123
124
 
125
+ /**
126
+ * Gets a canonical name for a BoundedContextRef for comparison purposes.
127
+ */
128
+ function getRefKey(ref: BoundedContextRef): string {
129
+ if (isThisRef(ref)) {
130
+ return 'this';
131
+ }
132
+ return ref.link?.$refText ?? '';
133
+ }
134
+
135
+ /**
136
+ * Builds a canonical key for a relationship for duplicate detection.
137
+ * The key captures both endpoints, arrow direction, and integration patterns.
138
+ */
139
+ function buildRelationshipKey(rel: Relationship): string {
140
+ const left = getRefKey(rel.left);
141
+ const right = getRefKey(rel.right);
142
+ const leftPatterns = (rel.leftPatterns ?? []).slice().sort((a, b) => a.localeCompare(b)).join(',');
143
+ const rightPatterns = (rel.rightPatterns ?? []).slice().sort((a, b) => a.localeCompare(b)).join(',');
144
+ return `[${leftPatterns}]${left}${rel.arrow}[${rightPatterns}]${right}`;
145
+ }
146
+
147
+ /**
148
+ * Validates that a context map does not contain duplicate relationships.
149
+ * Two relationships are considered duplicate if they have the same endpoints,
150
+ * direction, and integration patterns.
151
+ *
152
+ * @param map - The context map to validate
153
+ * @param accept - The validation acceptor for reporting issues
154
+ */
155
+ function validateNoDuplicateRelationships(
156
+ map: ContextMap,
157
+ accept: ValidationAcceptor
158
+ ): void {
159
+ if (!map.relationships || map.relationships.length < 2) return;
160
+
161
+ const seen = new Map<string, number>();
162
+ for (let i = 0; i < map.relationships.length; i++) {
163
+ const rel = map.relationships[i];
164
+ const key = buildRelationshipKey(rel);
165
+
166
+ if (seen.has(key)) {
167
+ accept('warning', ValidationMessages.CONTEXT_MAP_DUPLICATE_RELATIONSHIP(
168
+ getRefKey(rel.left), getRefKey(rel.right)
169
+ ), {
170
+ node: rel,
171
+ property: 'arrow',
172
+ codeDescription: buildCodeDescription('language.md', 'context-maps')
173
+ });
174
+ } else {
175
+ seen.set(key, i);
176
+ }
177
+ }
178
+ }
179
+
124
180
  export const contextMapChecks = [
125
181
  validateContextMapHasContexts,
126
182
  validateContextMapReferences,
127
- validateContextMapHasRelationships
183
+ validateContextMapHasRelationships,
184
+ validateNoDuplicateRelationships
128
185
  ];
129
186
 
130
187
  export const domainMapChecks = [