@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.
Files changed (212) hide show
  1. package/README.md +163 -0
  2. package/out/ast-augmentation.d.ts +6 -0
  3. package/out/ast-augmentation.js +2 -0
  4. package/out/ast-augmentation.js.map +1 -0
  5. package/out/domain-lang-module.d.ts +57 -0
  6. package/out/domain-lang-module.js +67 -0
  7. package/out/domain-lang-module.js.map +1 -0
  8. package/out/generated/ast.d.ts +759 -0
  9. package/out/generated/ast.js +556 -0
  10. package/out/generated/ast.js.map +1 -0
  11. package/out/generated/grammar.d.ts +6 -0
  12. package/out/generated/grammar.js +2407 -0
  13. package/out/generated/grammar.js.map +1 -0
  14. package/out/generated/module.d.ts +13 -0
  15. package/out/generated/module.js +21 -0
  16. package/out/generated/module.js.map +1 -0
  17. package/out/index.d.ts +16 -0
  18. package/out/index.js +22 -0
  19. package/out/index.js.map +1 -0
  20. package/out/lsp/domain-lang-code-actions.d.ts +55 -0
  21. package/out/lsp/domain-lang-code-actions.js +143 -0
  22. package/out/lsp/domain-lang-code-actions.js.map +1 -0
  23. package/out/lsp/domain-lang-completion.d.ts +37 -0
  24. package/out/lsp/domain-lang-completion.js +452 -0
  25. package/out/lsp/domain-lang-completion.js.map +1 -0
  26. package/out/lsp/domain-lang-formatter.d.ts +15 -0
  27. package/out/lsp/domain-lang-formatter.js +43 -0
  28. package/out/lsp/domain-lang-formatter.js.map +1 -0
  29. package/out/lsp/domain-lang-naming.d.ts +34 -0
  30. package/out/lsp/domain-lang-naming.js +49 -0
  31. package/out/lsp/domain-lang-naming.js.map +1 -0
  32. package/out/lsp/domain-lang-scope.d.ts +59 -0
  33. package/out/lsp/domain-lang-scope.js +102 -0
  34. package/out/lsp/domain-lang-scope.js.map +1 -0
  35. package/out/lsp/domain-lang-workspace-manager.d.ts +21 -0
  36. package/out/lsp/domain-lang-workspace-manager.js +93 -0
  37. package/out/lsp/domain-lang-workspace-manager.js.map +1 -0
  38. package/out/lsp/hover/ddd-pattern-explanations.d.ts +50 -0
  39. package/out/lsp/hover/ddd-pattern-explanations.js +196 -0
  40. package/out/lsp/hover/ddd-pattern-explanations.js.map +1 -0
  41. package/out/lsp/hover/domain-lang-hover.d.ts +19 -0
  42. package/out/lsp/hover/domain-lang-hover.js +302 -0
  43. package/out/lsp/hover/domain-lang-hover.js.map +1 -0
  44. package/out/lsp/hover/domain-lang-keywords.d.ts +13 -0
  45. package/out/lsp/hover/domain-lang-keywords.js +47 -0
  46. package/out/lsp/hover/domain-lang-keywords.js.map +1 -0
  47. package/out/lsp/manifest-diagnostics.d.ts +82 -0
  48. package/out/lsp/manifest-diagnostics.js +230 -0
  49. package/out/lsp/manifest-diagnostics.js.map +1 -0
  50. package/out/main-browser.d.ts +1 -0
  51. package/out/main-browser.js +11 -0
  52. package/out/main-browser.js.map +1 -0
  53. package/out/main.d.ts +1 -0
  54. package/out/main.js +74 -0
  55. package/out/main.js.map +1 -0
  56. package/out/sdk/ast-augmentation.d.ts +136 -0
  57. package/out/sdk/ast-augmentation.js +62 -0
  58. package/out/sdk/ast-augmentation.js.map +1 -0
  59. package/out/sdk/index.d.ts +94 -0
  60. package/out/sdk/index.js +97 -0
  61. package/out/sdk/index.js.map +1 -0
  62. package/out/sdk/indexes.d.ts +16 -0
  63. package/out/sdk/indexes.js +97 -0
  64. package/out/sdk/indexes.js.map +1 -0
  65. package/out/sdk/loader-node.d.ts +51 -0
  66. package/out/sdk/loader-node.js +119 -0
  67. package/out/sdk/loader-node.js.map +1 -0
  68. package/out/sdk/loader.d.ts +49 -0
  69. package/out/sdk/loader.js +85 -0
  70. package/out/sdk/loader.js.map +1 -0
  71. package/out/sdk/patterns.d.ts +93 -0
  72. package/out/sdk/patterns.js +123 -0
  73. package/out/sdk/patterns.js.map +1 -0
  74. package/out/sdk/query.d.ts +90 -0
  75. package/out/sdk/query.js +679 -0
  76. package/out/sdk/query.js.map +1 -0
  77. package/out/sdk/resolution.d.ts +52 -0
  78. package/out/sdk/resolution.js +68 -0
  79. package/out/sdk/resolution.js.map +1 -0
  80. package/out/sdk/types.d.ts +280 -0
  81. package/out/sdk/types.js +8 -0
  82. package/out/sdk/types.js.map +1 -0
  83. package/out/services/dependency-analyzer.d.ts +58 -0
  84. package/out/services/dependency-analyzer.js +254 -0
  85. package/out/services/dependency-analyzer.js.map +1 -0
  86. package/out/services/dependency-resolver.d.ts +146 -0
  87. package/out/services/dependency-resolver.js +452 -0
  88. package/out/services/dependency-resolver.js.map +1 -0
  89. package/out/services/git-url-resolver.browser.d.ts +10 -0
  90. package/out/services/git-url-resolver.browser.js +19 -0
  91. package/out/services/git-url-resolver.browser.js.map +1 -0
  92. package/out/services/git-url-resolver.d.ts +158 -0
  93. package/out/services/git-url-resolver.js +416 -0
  94. package/out/services/git-url-resolver.js.map +1 -0
  95. package/out/services/governance-validator.d.ts +44 -0
  96. package/out/services/governance-validator.js +153 -0
  97. package/out/services/governance-validator.js.map +1 -0
  98. package/out/services/import-resolver.d.ts +77 -0
  99. package/out/services/import-resolver.js +240 -0
  100. package/out/services/import-resolver.js.map +1 -0
  101. package/out/services/performance-optimizer.d.ts +60 -0
  102. package/out/services/performance-optimizer.js +140 -0
  103. package/out/services/performance-optimizer.js.map +1 -0
  104. package/out/services/relationship-inference.d.ts +11 -0
  105. package/out/services/relationship-inference.js +98 -0
  106. package/out/services/relationship-inference.js.map +1 -0
  107. package/out/services/semver.d.ts +98 -0
  108. package/out/services/semver.js +195 -0
  109. package/out/services/semver.js.map +1 -0
  110. package/out/services/types.d.ts +340 -0
  111. package/out/services/types.js +46 -0
  112. package/out/services/types.js.map +1 -0
  113. package/out/services/workspace-manager.d.ts +123 -0
  114. package/out/services/workspace-manager.js +489 -0
  115. package/out/services/workspace-manager.js.map +1 -0
  116. package/out/syntaxes/domain-lang.monarch.d.ts +76 -0
  117. package/out/syntaxes/domain-lang.monarch.js +29 -0
  118. package/out/syntaxes/domain-lang.monarch.js.map +1 -0
  119. package/out/utils/import-utils.d.ts +49 -0
  120. package/out/utils/import-utils.js +128 -0
  121. package/out/utils/import-utils.js.map +1 -0
  122. package/out/validation/bounded-context.d.ts +11 -0
  123. package/out/validation/bounded-context.js +79 -0
  124. package/out/validation/bounded-context.js.map +1 -0
  125. package/out/validation/classification.d.ts +3 -0
  126. package/out/validation/classification.js +3 -0
  127. package/out/validation/classification.js.map +1 -0
  128. package/out/validation/constants.d.ts +180 -0
  129. package/out/validation/constants.js +235 -0
  130. package/out/validation/constants.js.map +1 -0
  131. package/out/validation/domain-lang-validator.d.ts +2 -0
  132. package/out/validation/domain-lang-validator.js +27 -0
  133. package/out/validation/domain-lang-validator.js.map +1 -0
  134. package/out/validation/domain.d.ts +11 -0
  135. package/out/validation/domain.js +63 -0
  136. package/out/validation/domain.js.map +1 -0
  137. package/out/validation/import.d.ts +68 -0
  138. package/out/validation/import.js +237 -0
  139. package/out/validation/import.js.map +1 -0
  140. package/out/validation/manifest.d.ts +144 -0
  141. package/out/validation/manifest.js +327 -0
  142. package/out/validation/manifest.js.map +1 -0
  143. package/out/validation/maps.d.ts +21 -0
  144. package/out/validation/maps.js +60 -0
  145. package/out/validation/maps.js.map +1 -0
  146. package/out/validation/metadata.d.ts +7 -0
  147. package/out/validation/metadata.js +16 -0
  148. package/out/validation/metadata.js.map +1 -0
  149. package/out/validation/model.d.ts +12 -0
  150. package/out/validation/model.js +29 -0
  151. package/out/validation/model.js.map +1 -0
  152. package/out/validation/relationships.d.ts +12 -0
  153. package/out/validation/relationships.js +94 -0
  154. package/out/validation/relationships.js.map +1 -0
  155. package/out/validation/shared.d.ts +6 -0
  156. package/out/validation/shared.js +12 -0
  157. package/out/validation/shared.js.map +1 -0
  158. package/package.json +110 -0
  159. package/src/ast-augmentation.ts +5 -0
  160. package/src/domain-lang-module.ts +112 -0
  161. package/src/domain-lang.langium +351 -0
  162. package/src/generated/ast.ts +986 -0
  163. package/src/generated/grammar.ts +2409 -0
  164. package/src/generated/module.ts +25 -0
  165. package/src/index.ts +24 -0
  166. package/src/lsp/domain-lang-code-actions.ts +189 -0
  167. package/src/lsp/domain-lang-completion.ts +514 -0
  168. package/src/lsp/domain-lang-formatter.ts +51 -0
  169. package/src/lsp/domain-lang-naming.ts +56 -0
  170. package/src/lsp/domain-lang-scope.ts +137 -0
  171. package/src/lsp/domain-lang-workspace-manager.ts +104 -0
  172. package/src/lsp/hover/ddd-pattern-explanations.ts +237 -0
  173. package/src/lsp/hover/domain-lang-hover.ts +338 -0
  174. package/src/lsp/hover/domain-lang-keywords.ts +50 -0
  175. package/src/lsp/manifest-diagnostics.ts +290 -0
  176. package/src/main-browser.ts +15 -0
  177. package/src/main.ts +85 -0
  178. package/src/sdk/README.md +297 -0
  179. package/src/sdk/ast-augmentation.ts +157 -0
  180. package/src/sdk/index.ts +126 -0
  181. package/src/sdk/indexes.ts +155 -0
  182. package/src/sdk/loader-node.ts +146 -0
  183. package/src/sdk/loader.ts +99 -0
  184. package/src/sdk/patterns.ts +147 -0
  185. package/src/sdk/query.ts +802 -0
  186. package/src/sdk/resolution.ts +78 -0
  187. package/src/sdk/types.ts +323 -0
  188. package/src/services/dependency-analyzer.ts +321 -0
  189. package/src/services/dependency-resolver.ts +551 -0
  190. package/src/services/git-url-resolver.browser.ts +26 -0
  191. package/src/services/git-url-resolver.ts +517 -0
  192. package/src/services/governance-validator.ts +177 -0
  193. package/src/services/import-resolver.ts +292 -0
  194. package/src/services/performance-optimizer.ts +170 -0
  195. package/src/services/relationship-inference.ts +121 -0
  196. package/src/services/semver.ts +213 -0
  197. package/src/services/types.ts +415 -0
  198. package/src/services/workspace-manager.ts +607 -0
  199. package/src/syntaxes/domain-lang.monarch.ts +29 -0
  200. package/src/utils/import-utils.ts +152 -0
  201. package/src/validation/bounded-context.ts +99 -0
  202. package/src/validation/classification.ts +5 -0
  203. package/src/validation/constants.ts +304 -0
  204. package/src/validation/domain-lang-validator.ts +33 -0
  205. package/src/validation/domain.ts +77 -0
  206. package/src/validation/import.ts +295 -0
  207. package/src/validation/manifest.ts +439 -0
  208. package/src/validation/maps.ts +76 -0
  209. package/src/validation/metadata.ts +18 -0
  210. package/src/validation/model.ts +37 -0
  211. package/src/validation/relationships.ts +154 -0
  212. package/src/validation/shared.ts +14 -0
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Index building for O(1) lookups in the Model Query SDK.
3
+ * Builds maps for FQN, name, team, strategic classification, and metadata lookups.
4
+ *
5
+ * Indexes are built once during model loading and enable fast queries.
6
+ */
7
+
8
+ import type { AstNode } from 'langium';
9
+ import { AstUtils } from 'langium';
10
+ import type {
11
+ BoundedContext,
12
+ Classification,
13
+ ContextMap,
14
+ Domain,
15
+ DomainMap,
16
+ Metadata,
17
+ Model,
18
+ NamespaceDeclaration,
19
+ Team,
20
+ } from '../generated/ast.js';
21
+ import {
22
+ isBoundedContext,
23
+ isClassification,
24
+ isContextMap,
25
+ isDomain,
26
+ isDomainMap,
27
+ isMetadata,
28
+ isNamespaceDeclaration,
29
+ isTeam,
30
+ } from '../generated/ast.js';
31
+ import { QualifiedNameProvider } from '../lsp/domain-lang-naming.js';
32
+ import type { ModelIndexes } from './types.js';
33
+ import {
34
+ metadataAsMap,
35
+ effectiveClassification,
36
+ effectiveTeam,
37
+ } from './resolution.js';
38
+
39
+ /**
40
+ * Builds all indexes for a model.
41
+ * Called once during model loading.
42
+ *
43
+ * @param model - Root model node
44
+ * @returns ModelIndexes containing all lookup maps
45
+ */
46
+ export function buildIndexes(model: Model): ModelIndexes {
47
+ const byFqn = new Map<string, AstNode>();
48
+ const byName = new Map<string, AstNode[]>();
49
+ const byTeam = new Map<string, BoundedContext[]>();
50
+ const byClassification = new Map<string, BoundedContext[]>();
51
+ const byMetadataKey = new Map<string, BoundedContext[]>();
52
+
53
+ const fqnProvider = new QualifiedNameProvider();
54
+
55
+ // Stream all AST nodes and index them
56
+ for (const node of AstUtils.streamAllContents(model)) {
57
+ // Index named types only
58
+ if (!hasName(node)) {
59
+ continue;
60
+ }
61
+
62
+ const name = node.name;
63
+ const fqn = fqnProvider.getQualifiedName(node.$container, name);
64
+
65
+ // Index by FQN (unique)
66
+ byFqn.set(fqn, node);
67
+
68
+ // Index by simple name (may have duplicates)
69
+ const nameList = byName.get(name) ?? [];
70
+ nameList.push(node);
71
+ byName.set(name, nameList);
72
+
73
+ // BoundedContext-specific indexes
74
+ if (isBoundedContext(node)) {
75
+ indexBoundedContext(node, byTeam, byClassification, byMetadataKey);
76
+ }
77
+ }
78
+
79
+ return {
80
+ byFqn,
81
+ byName,
82
+ byTeam,
83
+ byClassification,
84
+ byMetadataKey,
85
+ };
86
+ }
87
+
88
+ /**
89
+ * Indexes a BoundedContext by team, strategic classification, and metadata.
90
+ *
91
+ * @param bc - BoundedContext node
92
+ * @param byTeam - Team index map
93
+ * @param byClassification - Strategic classification index map
94
+ * @param byMetadataKey - Metadata key index map
95
+ */
96
+ function indexBoundedContext(
97
+ bc: BoundedContext,
98
+ byTeam: Map<string, BoundedContext[]>,
99
+ byClassification: Map<string, BoundedContext[]>,
100
+ byMetadataKey: Map<string, BoundedContext[]>
101
+ ): void {
102
+ // Index by team
103
+ const team = effectiveTeam(bc);
104
+ if (team?.name) {
105
+ const teamList = byTeam.get(team.name) ?? [];
106
+ teamList.push(bc);
107
+ byTeam.set(team.name, teamList);
108
+ }
109
+
110
+ // Index by strategic classification
111
+ const classification = effectiveClassification(bc);
112
+ if (classification?.name) {
113
+ const classificationList = byClassification.get(classification.name) ?? [];
114
+ classificationList.push(bc);
115
+ byClassification.set(classification.name, classificationList);
116
+ }
117
+
118
+ // Index by metadata keys
119
+ const metadata = metadataAsMap(bc);
120
+ for (const key of metadata.keys()) {
121
+ const metaList = byMetadataKey.get(key) ?? [];
122
+ metaList.push(bc);
123
+ byMetadataKey.set(key, metaList);
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Type guard for named AST nodes.
129
+ * Only these types are indexed by name.
130
+ */
131
+ function hasName(node: AstNode): node is NamedNode {
132
+ return (
133
+ isDomain(node) ||
134
+ isBoundedContext(node) ||
135
+ isTeam(node) ||
136
+ isClassification(node) ||
137
+ isContextMap(node) ||
138
+ isDomainMap(node) ||
139
+ isNamespaceDeclaration(node) ||
140
+ isMetadata(node)
141
+ );
142
+ }
143
+
144
+ /**
145
+ * Union type of all named AST nodes.
146
+ */
147
+ type NamedNode =
148
+ | Domain
149
+ | BoundedContext
150
+ | Team
151
+ | Classification
152
+ | ContextMap
153
+ | DomainMap
154
+ | NamespaceDeclaration
155
+ | Metadata;
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Node.js-specific loader for DomainLang models.
3
+ *
4
+ * **WARNING: This module is NOT browser-compatible.**
5
+ *
6
+ * For browser environments, use:
7
+ * - `fromDocument()` with documents from the LSP
8
+ * - `fromModel()` with pre-parsed models
9
+ * - `loadModelFromText()` for in-memory parsing
10
+ *
11
+ * This loader creates **isolated Langium services** for standalone CLI usage.
12
+ * It does NOT integrate with an existing LSP workspace.
13
+ *
14
+ * For full workspace management with cross-file imports:
15
+ * - Use the WorkspaceManager service
16
+ * - Or host the LSP server and use its services
17
+ *
18
+ * @module sdk/loader-node
19
+ */
20
+
21
+ import { DocumentState, URI } from 'langium';
22
+ import { NodeFileSystem } from 'langium/node';
23
+ import type { Model } from '../generated/ast.js';
24
+ import { isModel } from '../generated/ast.js';
25
+ import { createDomainLangServices } from '../domain-lang-module.js';
26
+ import type { LoadOptions, QueryContext } from './types.js';
27
+ import { fromModel, augmentModel } from './query.js';
28
+ import { ensureImportGraphFromDocument } from '../utils/import-utils.js';
29
+
30
+ /**
31
+ * Loads a DomainLang model from a file on disk.
32
+ *
33
+ * **Node.js only** - uses file system APIs.
34
+ *
35
+ * Supports multi-file models with imports: all imported files are
36
+ * automatically loaded and linked. Use `documents` in the result
37
+ * to see all loaded files.
38
+ *
39
+ * @param entryFile - Path to the entry .dlang file
40
+ * @param options - Optional load configuration
41
+ * @returns QueryContext with model, documents, and query API
42
+ * @throws Error if file cannot be loaded or parsing fails
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * import { loadModel } from '@domainlang/language/sdk/loader-node';
47
+ *
48
+ * const { query, model, documents } = await loadModel('./domains.dlang', {
49
+ * workspaceDir: process.cwd()
50
+ * });
51
+ *
52
+ * // Query spans all imported files
53
+ * for (const bc of query.boundedContexts()) {
54
+ * console.log(bc.name);
55
+ * }
56
+ *
57
+ * console.log(`Loaded ${documents.length} files`);
58
+ * ```
59
+ */
60
+ export async function loadModel(
61
+ entryFile: string,
62
+ options?: LoadOptions
63
+ ): Promise<QueryContext> {
64
+ // Resolve absolute path
65
+ const path = await import('path');
66
+ const absolutePath = path.isAbsolute(entryFile)
67
+ ? entryFile
68
+ : path.resolve(options?.workspaceDir ?? process.cwd(), entryFile);
69
+
70
+ // Create or reuse services
71
+ const servicesObj = options?.services
72
+ ? { shared: options.services.shared, DomainLang: options.services }
73
+ : createDomainLangServices(NodeFileSystem);
74
+
75
+ const services = servicesObj.DomainLang;
76
+ const shared = servicesObj.shared;
77
+
78
+ // Initialize workspace if directory provided
79
+ if (options?.workspaceDir) {
80
+ const workspaceManager = services.imports.WorkspaceManager;
81
+ await workspaceManager.initialize(options.workspaceDir);
82
+ }
83
+
84
+ // Read file content and create document
85
+ const fs = await import('fs/promises');
86
+ const fileContent = await fs.readFile(absolutePath, 'utf-8');
87
+ const uri = URI.file(absolutePath);
88
+
89
+ // Use proper Langium document creation
90
+ const document = shared.workspace.LangiumDocumentFactory.fromString<Model>(
91
+ fileContent,
92
+ uri
93
+ );
94
+
95
+ // Register document and build it
96
+ shared.workspace.LangiumDocuments.addDocument(document);
97
+ await shared.workspace.DocumentBuilder.build([document], { validation: false });
98
+
99
+ // Traverse import graph to load all imported files
100
+ const importedUris = await ensureImportGraphFromDocument(
101
+ document,
102
+ shared.workspace.LangiumDocuments
103
+ );
104
+
105
+ // Build all imported documents with validation
106
+ const allDocuments = Array.from(shared.workspace.LangiumDocuments.all);
107
+ await shared.workspace.DocumentBuilder.build(allDocuments, { validation: true });
108
+
109
+ // Wait for entry document to be fully processed
110
+ if (document.state < DocumentState.Validated) {
111
+ throw new Error(`Document not fully processed: ${absolutePath}`);
112
+ }
113
+
114
+ // Check for parsing errors
115
+ if (document.parseResult.lexerErrors.length > 0) {
116
+ const errors = document.parseResult.lexerErrors.map(e => e.message).join('\n ');
117
+ throw new Error(`Lexer errors in ${entryFile}:\n ${errors}`);
118
+ }
119
+
120
+ if (document.parseResult.parserErrors.length > 0) {
121
+ const errors = document.parseResult.parserErrors.map(e => e.message).join('\n ');
122
+ throw new Error(`Parser errors in ${entryFile}:\n ${errors}`);
123
+ }
124
+
125
+ const model = document.parseResult.value;
126
+ if (!isModel(model)) {
127
+ throw new Error(`Document root is not a Model: ${entryFile}`);
128
+ }
129
+
130
+ // Augment AST nodes with SDK properties for all loaded models
131
+ for (const doc of allDocuments) {
132
+ const docModel = doc.parseResult.value;
133
+ if (isModel(docModel)) {
134
+ augmentModel(docModel);
135
+ }
136
+ }
137
+
138
+ // Collect all document URIs from the import graph
139
+ const documentUris: URI[] = Array.from(importedUris).map(uriStr => URI.parse(uriStr));
140
+
141
+ return {
142
+ model,
143
+ documents: documentUris,
144
+ query: fromModel(model),
145
+ };
146
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Browser-safe loader for in-memory model parsing.
3
+ *
4
+ * This module provides `loadModelFromText()` which works in both
5
+ * browser and Node.js environments by using Langium's EmptyFileSystem.
6
+ *
7
+ * For file-based loading in Node.js CLI tools, use:
8
+ * ```typescript
9
+ * import { loadModel } from '@domainlang/language/sdk/loader-node';
10
+ * ```
11
+ *
12
+ * For LSP/validation code that already has a linked AST, use the sync entry points:
13
+ * - `fromDocument()` - from a LangiumDocument
14
+ * - `fromModel()` - from a Model AST node
15
+ * - `fromServices()` - from DomainLangServices container
16
+ *
17
+ * @module sdk/loader
18
+ */
19
+
20
+ import { EmptyFileSystem, URI } from 'langium';
21
+ import type { Model } from '../generated/ast.js';
22
+ import { isModel } from '../generated/ast.js';
23
+ import { createDomainLangServices } from '../domain-lang-module.js';
24
+ import type { LoadOptions, QueryContext } from './types.js';
25
+ import { augmentModel, fromModel } from './query.js';
26
+
27
+ /**
28
+ * Loads a DomainLang model from a text string.
29
+ *
30
+ * **Browser-safe** - uses in-memory file system (EmptyFileSystem).
31
+ *
32
+ * Useful for:
33
+ * - Testing
34
+ * - REPL environments
35
+ * - Web-based editors
36
+ * - Any environment without file system access
37
+ *
38
+ * @param text - DomainLang source code
39
+ * @param options - Optional load configuration
40
+ * @returns QueryContext with model and query API
41
+ * @throws Error if parsing fails
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * import { loadModelFromText } from '@domainlang/language/sdk';
46
+ *
47
+ * const { query } = await loadModelFromText(`
48
+ * Domain Sales { vision: "Handle sales" }
49
+ * bc OrderContext for Sales
50
+ * `);
51
+ *
52
+ * const sales = query.domain('Sales');
53
+ * console.log(sales?.resolvedVision);
54
+ * ```
55
+ */
56
+ export async function loadModelFromText(
57
+ text: string,
58
+ options?: LoadOptions
59
+ ): Promise<QueryContext> {
60
+ // Create or reuse services (use EmptyFileSystem for in-memory parsing)
61
+ const servicesObj = options?.services
62
+ ? { shared: options.services.shared, DomainLang: options.services }
63
+ : createDomainLangServices(EmptyFileSystem);
64
+
65
+ const shared = servicesObj.shared;
66
+
67
+ // Create document from text with a virtual URI
68
+ const uri = URI.parse('memory:///model.dlang');
69
+ const document = shared.workspace.LangiumDocumentFactory.fromString<Model>(text, uri);
70
+
71
+ // Register and build document
72
+ shared.workspace.LangiumDocuments.addDocument(document);
73
+ await shared.workspace.DocumentBuilder.build([document], { validation: true });
74
+
75
+ // Check for parsing errors
76
+ if (document.parseResult.lexerErrors.length > 0) {
77
+ const errors = document.parseResult.lexerErrors.map(e => e.message).join('\n ');
78
+ throw new Error(`Lexer errors:\n ${errors}`);
79
+ }
80
+
81
+ if (document.parseResult.parserErrors.length > 0) {
82
+ const errors = document.parseResult.parserErrors.map(e => e.message).join('\n ');
83
+ throw new Error(`Parser errors:\n ${errors}`);
84
+ }
85
+
86
+ const model = document.parseResult.value;
87
+ if (!isModel(model)) {
88
+ throw new Error(`Document root is not a Model`);
89
+ }
90
+
91
+ // Augment AST nodes with SDK properties
92
+ augmentModel(model);
93
+
94
+ return {
95
+ model,
96
+ documents: [document.uri],
97
+ query: fromModel(model),
98
+ };
99
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Integration Patterns - Type-safe constants for DDD integration patterns.
3
+ *
4
+ * Use these constants instead of magic strings when checking relationship patterns.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { Pattern } from '../sdk/patterns.js';
9
+ *
10
+ * // Instead of: hasPattern('SK') or hasPattern('SharedKernel')
11
+ * // Use:
12
+ * if (relationship.hasPattern(Pattern.SharedKernel)) {
13
+ * // ...
14
+ * }
15
+ * ```
16
+ */
17
+
18
+ /**
19
+ * Integration pattern abbreviations as used in the grammar.
20
+ * These are the canonical abbreviations recognized by DomainLang.
21
+ */
22
+ export const Pattern = {
23
+ // Upstream patterns (provider side)
24
+ /** Open Host Service - exposes a clean API for consumers */
25
+ OHS: 'OHS',
26
+ /** Published Language - shared data format/protocol */
27
+ PL: 'PL',
28
+
29
+ // Downstream patterns (consumer side)
30
+ /** Conformist - accepts upstream model without translation */
31
+ CF: 'CF',
32
+ /** Anti-Corruption Layer - translates upstream model */
33
+ ACL: 'ACL',
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',
48
+ } as const;
49
+
50
+ /**
51
+ * Full names for integration patterns.
52
+ * Used when patterns are spelled out in documentation blocks.
53
+ */
54
+ export const PatternFullName = {
55
+ OHS: 'OpenHostService',
56
+ PL: 'PublishedLanguage',
57
+ CF: 'Conformist',
58
+ ACL: 'AntiCorruptionLayer',
59
+ SK: 'SharedKernel',
60
+ P: 'Partnership',
61
+ } as const;
62
+
63
+ /**
64
+ * Mapping from abbreviations to full names and vice versa.
65
+ */
66
+ export const PatternAliases: Record<string, readonly string[]> = {
67
+ // Abbreviation -> [abbreviation, fullName]
68
+ OHS: ['OHS', 'OpenHostService'],
69
+ PL: ['PL', 'PublishedLanguage'],
70
+ CF: ['CF', 'Conformist'],
71
+ ACL: ['ACL', 'AntiCorruptionLayer'],
72
+ SK: ['SK', 'SharedKernel'],
73
+ P: ['P', 'Partnership'],
74
+
75
+ // Full names map to same
76
+ OpenHostService: ['OHS', 'OpenHostService'],
77
+ PublishedLanguage: ['PL', 'PublishedLanguage'],
78
+ Conformist: ['CF', 'Conformist'],
79
+ AntiCorruptionLayer: ['ACL', 'AntiCorruptionLayer'],
80
+ SharedKernel: ['SK', 'SharedKernel'],
81
+ Partnership: ['P', 'Partnership'],
82
+ };
83
+
84
+ /**
85
+ * Type representing any valid integration pattern (abbreviation or full name).
86
+ */
87
+ export type IntegrationPattern =
88
+ | typeof Pattern[keyof typeof Pattern]
89
+ | typeof PatternFullName[keyof typeof PatternFullName];
90
+
91
+ /**
92
+ * Checks if a pattern string matches any of the expected aliases.
93
+ * Handles both abbreviations and full names case-insensitively.
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
98
+ */
99
+ export function matchesPattern(actual: string, expected: string): boolean {
100
+ const normalizedActual = actual.trim();
101
+ const aliases = PatternAliases[expected];
102
+
103
+ if (aliases) {
104
+ return aliases.some(alias =>
105
+ alias.toLowerCase() === normalizedActual.toLowerCase()
106
+ );
107
+ }
108
+
109
+ // Fallback: direct comparison
110
+ return normalizedActual.toLowerCase() === expected.toLowerCase();
111
+ }
112
+
113
+ /**
114
+ * Patterns that are typically on the upstream (provider) side.
115
+ */
116
+ export const UpstreamPatterns: readonly string[] = ['OHS', 'OpenHostService', 'PL', 'PublishedLanguage'];
117
+
118
+ /**
119
+ * Patterns that are typically on the downstream (consumer) side.
120
+ */
121
+ export const DownstreamPatterns: readonly string[] = ['CF', 'Conformist', 'ACL', 'AntiCorruptionLayer'];
122
+
123
+ /**
124
+ * Patterns that require mutual/bidirectional relationships.
125
+ */
126
+ export const MutualPatterns: readonly string[] = ['SK', 'SharedKernel', 'P', 'Partnership'];
127
+
128
+ /**
129
+ * Checks if a pattern is typically an upstream pattern.
130
+ */
131
+ export function isUpstreamPattern(pattern: string): boolean {
132
+ return UpstreamPatterns.some(p => matchesPattern(pattern, p));
133
+ }
134
+
135
+ /**
136
+ * Checks if a pattern is typically a downstream pattern.
137
+ */
138
+ export function isDownstreamPattern(pattern: string): boolean {
139
+ return DownstreamPatterns.some(p => matchesPattern(pattern, p));
140
+ }
141
+
142
+ /**
143
+ * Checks if a pattern requires bidirectional relationships.
144
+ */
145
+ export function isMutualPattern(pattern: string): boolean {
146
+ return MutualPatterns.some(p => matchesPattern(pattern, p));
147
+ }