@domainlang/language 0.1.20 → 0.1.82

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 (110) hide show
  1. package/out/domain-lang-module.d.ts +0 -2
  2. package/out/domain-lang-module.js +3 -11
  3. package/out/domain-lang-module.js.map +1 -1
  4. package/out/generated/ast.d.ts +19 -8
  5. package/out/generated/ast.js +10 -1
  6. package/out/generated/ast.js.map +1 -1
  7. package/out/generated/grammar.d.ts +1 -1
  8. package/out/generated/grammar.js +123 -28
  9. package/out/generated/grammar.js.map +1 -1
  10. package/out/generated/module.d.ts +1 -1
  11. package/out/generated/module.js +1 -1
  12. package/out/index.d.ts +0 -3
  13. package/out/index.js +0 -5
  14. package/out/index.js.map +1 -1
  15. package/out/lsp/hover/domain-lang-hover.js +4 -0
  16. package/out/lsp/hover/domain-lang-hover.js.map +1 -1
  17. package/out/sdk/index.d.ts +1 -1
  18. package/out/sdk/loader-node.d.ts +3 -7
  19. package/out/sdk/loader-node.js +9 -24
  20. package/out/sdk/loader-node.js.map +1 -1
  21. package/out/sdk/types.d.ts +21 -0
  22. package/out/services/dependency-analyzer.d.ts +39 -3
  23. package/out/services/dependency-analyzer.js +47 -22
  24. package/out/services/dependency-analyzer.js.map +1 -1
  25. package/out/services/dependency-resolver.d.ts +45 -68
  26. package/out/services/dependency-resolver.js +43 -243
  27. package/out/services/dependency-resolver.js.map +1 -1
  28. package/out/services/git-url-resolver.browser.d.ts +12 -4
  29. package/out/services/git-url-resolver.browser.js +1 -5
  30. package/out/services/git-url-resolver.browser.js.map +1 -1
  31. package/out/services/git-url-resolver.d.ts +56 -22
  32. package/out/services/git-url-resolver.js +36 -70
  33. package/out/services/git-url-resolver.js.map +1 -1
  34. package/out/services/governance-validator.d.ts +37 -1
  35. package/out/services/governance-validator.js +10 -4
  36. package/out/services/governance-validator.js.map +1 -1
  37. package/out/services/import-resolver.d.ts +6 -65
  38. package/out/services/import-resolver.js +5 -223
  39. package/out/services/import-resolver.js.map +1 -1
  40. package/out/services/performance-optimizer.d.ts +1 -1
  41. package/out/services/workspace-manager.d.ts +10 -57
  42. package/out/services/workspace-manager.js +21 -187
  43. package/out/services/workspace-manager.js.map +1 -1
  44. package/out/syntaxes/domain-lang.monarch.js +1 -1
  45. package/out/syntaxes/domain-lang.monarch.js.map +1 -1
  46. package/out/utils/import-utils.d.ts +12 -4
  47. package/out/utils/import-utils.js +135 -35
  48. package/out/utils/import-utils.js.map +1 -1
  49. package/out/validation/constants.d.ts +0 -103
  50. package/out/validation/constants.js +1 -140
  51. package/out/validation/constants.js.map +1 -1
  52. package/out/validation/domain.js +1 -46
  53. package/out/validation/domain.js.map +1 -1
  54. package/out/validation/import.d.ts +22 -46
  55. package/out/validation/import.js +85 -187
  56. package/out/validation/import.js.map +1 -1
  57. package/out/validation/maps.js +6 -10
  58. package/out/validation/maps.js.map +1 -1
  59. package/out/validation/metadata.js +1 -5
  60. package/out/validation/metadata.js.map +1 -1
  61. package/package.json +6 -8
  62. package/src/domain-lang-module.ts +6 -18
  63. package/src/domain-lang.langium +12 -7
  64. package/src/generated/ast.ts +20 -7
  65. package/src/generated/grammar.ts +123 -28
  66. package/src/generated/module.ts +1 -1
  67. package/src/index.ts +0 -7
  68. package/src/lsp/hover/domain-lang-hover.ts +2 -0
  69. package/src/sdk/index.ts +2 -0
  70. package/src/sdk/loader-node.ts +9 -29
  71. package/src/sdk/types.ts +23 -0
  72. package/src/services/dependency-analyzer.ts +84 -24
  73. package/src/services/dependency-resolver.ts +84 -301
  74. package/src/services/git-url-resolver.browser.ts +14 -9
  75. package/src/services/git-url-resolver.ts +93 -86
  76. package/src/services/governance-validator.ts +47 -5
  77. package/src/services/import-resolver.ts +8 -270
  78. package/src/services/performance-optimizer.ts +1 -1
  79. package/src/services/workspace-manager.ts +46 -237
  80. package/src/syntaxes/domain-lang.monarch.ts +1 -1
  81. package/src/utils/import-utils.ts +160 -38
  82. package/src/validation/constants.ts +1 -181
  83. package/src/validation/domain.ts +1 -54
  84. package/src/validation/import.ts +104 -228
  85. package/src/validation/maps.ts +6 -10
  86. package/src/validation/metadata.ts +1 -5
  87. package/out/lsp/domain-lang-code-actions.d.ts +0 -55
  88. package/out/lsp/domain-lang-code-actions.js +0 -143
  89. package/out/lsp/domain-lang-code-actions.js.map +0 -1
  90. package/out/lsp/domain-lang-workspace-manager.d.ts +0 -21
  91. package/out/lsp/domain-lang-workspace-manager.js +0 -93
  92. package/out/lsp/domain-lang-workspace-manager.js.map +0 -1
  93. package/out/lsp/manifest-diagnostics.d.ts +0 -82
  94. package/out/lsp/manifest-diagnostics.js +0 -230
  95. package/out/lsp/manifest-diagnostics.js.map +0 -1
  96. package/out/services/semver.d.ts +0 -98
  97. package/out/services/semver.js +0 -195
  98. package/out/services/semver.js.map +0 -1
  99. package/out/services/types.d.ts +0 -340
  100. package/out/services/types.js +0 -46
  101. package/out/services/types.js.map +0 -1
  102. package/out/validation/manifest.d.ts +0 -144
  103. package/out/validation/manifest.js +0 -327
  104. package/out/validation/manifest.js.map +0 -1
  105. package/src/lsp/domain-lang-code-actions.ts +0 -189
  106. package/src/lsp/domain-lang-workspace-manager.ts +0 -104
  107. package/src/lsp/manifest-diagnostics.ts +0 -290
  108. package/src/services/semver.ts +0 -213
  109. package/src/services/types.ts +0 -415
  110. package/src/validation/manifest.ts +0 -439
@@ -9,33 +9,56 @@
9
9
  * 2. Download all direct dependencies
10
10
  * 3. Parse each dependency's model.yaml
11
11
  * 4. Recursively discover transitive dependencies
12
- * 5. Resolve version constraints using "Latest Wins" strategy
12
+ * 5. Resolve version constraints (simple: use latest satisfying version)
13
13
  * 6. Generate lock file with pinned commit hashes
14
- *
15
- * Resolution Strategy ("Latest Wins"):
16
- * - SemVer tags (same major): Pick highest compatible version
17
- * - Same branch: No conflict, resolve once
18
- * - Commit pins: Error (explicit pins are intentional)
19
- * - Major version mismatch: Error
20
- * - Tag vs Branch: Error (incompatible intent)
21
14
  */
22
15
 
23
16
  import path from 'node:path';
24
17
  import fs from 'node:fs/promises';
25
18
  import YAML from 'yaml';
26
- import { GitUrlParser, GitUrlResolver } from './git-url-resolver.js';
27
- import { parseSemVer, pickLatestSemVer, detectRefType } from './semver.js';
28
- import type { SemVer, ResolvingPackage, LockFile, LockedDependency, DependencyGraph } from './types.js';
19
+ import { GitUrlParser, GitUrlResolver, PackageMetadata, LockFile, LockedDependency } from './git-url-resolver.js';
20
+
21
+ /**
22
+ * Dependency graph node representing a package and its dependencies.
23
+ */
24
+ export interface DependencyNode {
25
+ /** Package identifier (owner/repo) */
26
+ packageKey: string;
27
+ /** Version constraint from parent (e.g., "^1.0.0") */
28
+ versionConstraint: string;
29
+ /** Resolved version */
30
+ resolvedVersion?: string;
31
+ /** Resolved commit hash */
32
+ commitHash?: string;
33
+ /** Full git URL */
34
+ repoUrl?: string;
35
+ /** Direct dependencies of this package */
36
+ dependencies: Record<string, string>;
37
+ /** Parent packages that depend on this one */
38
+ dependents: string[];
39
+ }
29
40
 
41
+ /**
42
+ * Dependency graph for the entire workspace.
43
+ */
44
+ export interface DependencyGraph {
45
+ /** All discovered packages, keyed by owner/repo */
46
+ nodes: Record<string, DependencyNode>;
47
+ /** Root package name */
48
+ root: string;
49
+ }
50
+
51
+ /**
52
+ * Resolves dependencies and generates lock files.
53
+ */
54
+ export type { LockFile, PackageMetadata } from './git-url-resolver.js';
30
55
  export class DependencyResolver {
31
56
  private gitResolver: GitUrlResolver;
32
57
  private workspaceRoot: string;
33
58
 
34
59
  constructor(workspaceRoot: string, gitResolver?: GitUrlResolver) {
35
60
  this.workspaceRoot = workspaceRoot;
36
- // Per PRS-010: Project-local cache at .dlang/packages/
37
- const cacheDir = path.join(workspaceRoot, '.dlang', 'packages');
38
- this.gitResolver = gitResolver || new GitUrlResolver(cacheDir);
61
+ this.gitResolver = gitResolver || new GitUrlResolver();
39
62
  }
40
63
 
41
64
  /**
@@ -62,13 +85,6 @@ export class DependencyResolver {
62
85
  // Build dependency graph
63
86
  const graph = await this.buildDependencyGraph(rootConfig);
64
87
 
65
- // Apply overrides before conflict detection
66
- this.applyOverrides(graph, rootConfig.overrides);
67
-
68
- // Detect version conflicts and package-level cycles before resolving
69
- this.detectVersionConflicts(graph);
70
- this.detectPackageCycles(graph);
71
-
72
88
  // Resolve version constraints
73
89
  await this.resolveVersions(graph);
74
90
 
@@ -76,59 +92,22 @@ export class DependencyResolver {
76
92
  return this.generateLockFile(graph);
77
93
  }
78
94
 
79
- /**
80
- * Applies ref overrides from model.yaml to resolve conflicts explicitly.
81
- *
82
- * Overrides take precedence over all other constraints.
83
- *
84
- * @example
85
- * ```yaml
86
- * overrides:
87
- * domainlang/core: v2.0.0
88
- * ```
89
- */
90
- private applyOverrides(graph: DependencyGraph, overrides?: Record<string, string>): void {
91
- if (!overrides) return;
92
-
93
- for (const [pkg, overrideRef] of Object.entries(overrides)) {
94
- const node = graph.nodes[pkg];
95
- if (node) {
96
- // Override replaces all constraints with a single definitive ref
97
- node.constraints = new Set([overrideRef]);
98
- node.refConstraint = overrideRef;
99
-
100
- // Track that this was an override for messaging
101
- this.overrideMessages.push(`Override applied: ${pkg}@${overrideRef}`);
102
- }
103
- }
104
- }
105
-
106
- /** Override messages for CLI output */
107
- private overrideMessages: string[] = [];
108
-
109
- /**
110
- * Returns any override messages from the last dependency resolution.
111
- */
112
- getOverrideMessages(): string[] {
113
- return this.overrideMessages;
114
- }
115
-
116
95
  /**
117
96
  * Builds the complete dependency graph by recursively discovering transitive dependencies.
118
97
  */
119
- private async buildDependencyGraph(rootConfig: ResolvingPackage): Promise<DependencyGraph> {
98
+ private async buildDependencyGraph(rootConfig: PackageMetadata): Promise<DependencyGraph> {
120
99
  const graph: DependencyGraph = {
121
100
  nodes: {},
122
101
  root: rootConfig.name || 'root',
123
102
  };
124
103
 
125
104
  // Process root dependencies
126
- const queue: Array<{ packageKey: string; refConstraint: string; parent: string }> = [];
105
+ const queue: Array<{ packageKey: string; versionConstraint: string; parent: string }> = [];
127
106
 
128
- for (const [depName, refConstraint] of Object.entries(rootConfig.dependencies || {})) {
107
+ for (const [depName, versionConstraint] of Object.entries(rootConfig.dependencies || {})) {
129
108
  queue.push({
130
109
  packageKey: depName,
131
- refConstraint,
110
+ versionConstraint,
132
111
  parent: graph.root
133
112
  });
134
113
  }
@@ -139,17 +118,14 @@ export class DependencyResolver {
139
118
  while (queue.length > 0) {
140
119
  const entry = queue.shift();
141
120
  if (!entry) break;
142
- const { packageKey, refConstraint, parent } = entry;
121
+ const { packageKey, versionConstraint, parent } = entry;
143
122
 
144
123
  // Skip if already processed
145
124
  if (visited.has(packageKey)) {
146
- // Update dependents list and record constraint
147
- const existing = graph.nodes[packageKey];
148
- if (!existing.dependents.includes(parent)) {
149
- existing.dependents.push(parent);
125
+ // Update dependents list
126
+ if (!graph.nodes[packageKey].dependents.includes(parent)) {
127
+ graph.nodes[packageKey].dependents.push(parent);
150
128
  }
151
- if (!existing.constraints) existing.constraints = new Set<string>();
152
- existing.constraints.add(refConstraint);
153
129
  continue;
154
130
  }
155
131
  visited.add(packageKey);
@@ -157,7 +133,7 @@ export class DependencyResolver {
157
133
  // Parse package identifier
158
134
  const gitInfo = GitUrlParser.parse(packageKey);
159
135
 
160
- // Download package to get its model.yaml
136
+ // Download package to get its dlang.toml
161
137
  const packageUri = await this.gitResolver.resolve(packageKey);
162
138
  const packageDir = path.dirname(packageUri.fsPath);
163
139
 
@@ -167,18 +143,17 @@ export class DependencyResolver {
167
143
  // Add to graph
168
144
  graph.nodes[packageKey] = {
169
145
  packageKey,
170
- refConstraint,
171
- constraints: new Set<string>([refConstraint]),
146
+ versionConstraint,
172
147
  repoUrl: gitInfo.repoUrl,
173
148
  dependencies: packageConfig.dependencies || {},
174
149
  dependents: [parent],
175
150
  };
176
151
 
177
152
  // Queue transitive dependencies
178
- for (const [transDepName, transRefConstraint] of Object.entries(packageConfig.dependencies || {})) {
153
+ for (const [transDepName, transVersionConstraint] of Object.entries(packageConfig.dependencies || {})) {
179
154
  queue.push({
180
155
  packageKey: transDepName,
181
- refConstraint: transRefConstraint,
156
+ versionConstraint: transVersionConstraint,
182
157
  parent: packageKey,
183
158
  });
184
159
  }
@@ -188,52 +163,49 @@ export class DependencyResolver {
188
163
  }
189
164
 
190
165
  /**
191
- * Resolves ref constraints to specific commits.
166
+ * Resolves version constraints to specific versions.
192
167
  *
193
- * Simple algorithm: Use the ref specified in the constraint.
194
- * Detects refType (tag, branch, or commit) based on format.
168
+ * Simple algorithm: Use the latest version that satisfies all constraints.
169
+ * Future: Implement proper semantic versioning resolution.
195
170
  */
196
171
  private async resolveVersions(graph: DependencyGraph): Promise<void> {
197
172
  for (const [packageKey, node] of Object.entries(graph.nodes)) {
198
173
  // Parse package to get repo info
199
174
  const gitInfo = GitUrlParser.parse(packageKey);
200
175
 
201
- // Extract ref from constraint
202
- const ref = this.extractRefFromConstraint(node.refConstraint);
176
+ // For now, resolve to the version specified in the constraint
177
+ // Future: Implement proper semver range resolution
178
+ const version = this.extractVersionFromConstraint(node.versionConstraint);
203
179
 
204
- // Detect ref type based on format
205
- const refType = detectRefType(ref);
206
-
207
- // Resolve ref to commit hash
208
- const commitHash = await this.resolveCommitHash(gitInfo.repoUrl, ref);
180
+ // Resolve version to commit hash
181
+ const commitHash = await this.resolveCommitHash(gitInfo.repoUrl, version);
209
182
 
210
- node.resolvedRef = ref;
211
- node.refType = refType;
183
+ node.resolvedVersion = version;
212
184
  node.commitHash = commitHash;
213
185
  }
214
186
  }
215
187
 
216
188
  /**
217
- * Extracts a ref from a constraint string.
189
+ * Extracts a version from a constraint string.
218
190
  *
219
191
  * Examples:
220
- * - "^1.0.0" → "1.0.0" (treated as tag)
221
- * - "~2.3.0" → "2.3.0" (treated as tag)
222
- * - "1.5.0" → "1.5.0" (treated as tag)
223
- * - "main" → "main" (treated as branch)
224
- * - "abc123def" → "abc123def" (treated as commit)
192
+ * - "^1.0.0" → "1.0.0"
193
+ * - "~2.3.0" → "2.3.0"
194
+ * - "1.5.0" → "1.5.0"
225
195
  * - "owner/repo@1.0.0" → "1.0.0"
196
+ *
197
+ * Future: Implement proper semver range parsing and resolution.
226
198
  */
227
- private extractRefFromConstraint(constraint: string): string {
228
- // Remove semver operators (legacy support)
229
- let ref = constraint.replace(/^[\^~>=<]/, '');
199
+ private extractVersionFromConstraint(constraint: string): string {
200
+ // Remove semver operators
201
+ let version = constraint.replace(/^[\^~>=<]/, '');
230
202
 
231
- // Extract ref from full import URL if present
232
- if (ref.includes('@')) {
233
- ref = ref.split('@')[1];
203
+ // Extract version from full import URL if present
204
+ if (version.includes('@')) {
205
+ version = version.split('@')[1];
234
206
  }
235
207
 
236
- return ref || 'main';
208
+ return version || 'main';
237
209
  }
238
210
 
239
211
  /**
@@ -246,7 +218,7 @@ export class DependencyResolver {
246
218
  const uri = await this.gitResolver.resolve(gitInfo.original);
247
219
 
248
220
  // Extract commit hash from cache path
249
- // Per PRS-010: Project-local cache at .dlang/packages/{owner}/{repo}/{commit}/
221
+ // Cache format: ~/.dlang/cache/{platform}/{owner}/{repo}/{commit-hash}/
250
222
  const pathParts = uri.fsPath.split(path.sep);
251
223
  const commitHashIndex = pathParts.length - 2; // Second to last segment
252
224
  return pathParts[commitHashIndex];
@@ -259,16 +231,12 @@ export class DependencyResolver {
259
231
  const dependencies: Record<string, LockedDependency> = {};
260
232
 
261
233
  for (const [packageKey, node] of Object.entries(graph.nodes)) {
262
- if (!node.resolvedRef || !node.commitHash) {
263
- throw new Error(
264
- `Failed to resolve ref for '${packageKey}'.\n` +
265
- `Hint: Check that the package exists and the ref is valid.`
266
- );
234
+ if (!node.resolvedVersion || !node.commitHash) {
235
+ throw new Error(`Failed to resolve version for ${packageKey}`);
267
236
  }
268
237
 
269
238
  dependencies[packageKey] = {
270
- ref: node.resolvedRef,
271
- refType: node.refType ?? 'commit',
239
+ version: node.resolvedVersion,
272
240
  resolved: node.repoUrl || '',
273
241
  commit: node.commitHash,
274
242
  // Future: Calculate integrity hash
@@ -284,7 +252,7 @@ export class DependencyResolver {
284
252
  /**
285
253
  * Loads and parses a package's model.yaml file.
286
254
  */
287
- private async loadPackageConfig(packageDir: string): Promise<ResolvingPackage> {
255
+ private async loadPackageConfig(packageDir: string): Promise<PackageMetadata> {
288
256
  const yamlPath = path.join(packageDir, 'model.yaml');
289
257
 
290
258
  try {
@@ -308,225 +276,40 @@ export class DependencyResolver {
308
276
  * dependencies:
309
277
  * package-name:
310
278
  * source: owner/repo
311
- * ref: v1.0.0
279
+ * version: ^1.0.0
312
280
  */
313
- private parseYaml(content: string): ResolvingPackage {
281
+ private parseYaml(content: string): PackageMetadata {
314
282
  const parsed = YAML.parse(content) as {
315
283
  model?: {
316
284
  name?: string;
317
285
  version?: string;
318
286
  entry?: string;
319
287
  };
320
- dependencies?: Record<string, { source?: string; ref?: string }>;
321
- overrides?: Record<string, string>;
288
+ dependencies?: Record<string, { source?: string; version?: string }>;
322
289
  };
323
290
 
324
- const config: ResolvingPackage = {};
291
+ const config: PackageMetadata = {};
325
292
 
326
293
  if (parsed.model) {
327
294
  config.name = parsed.model.name;
328
295
  config.version = parsed.model.version;
329
- config.entry = parsed.model.entry;
296
+ config.main = parsed.model.entry;
330
297
  }
331
298
 
332
299
  if (parsed.dependencies) {
333
300
  config.dependencies = {};
334
301
  for (const [, depInfo] of Object.entries(parsed.dependencies)) {
335
302
  if (depInfo.source) {
336
- const refConstraint = depInfo.ref || 'main';
337
- // Store as "source@ref" for consistency with import resolution
338
- config.dependencies[depInfo.source] = refConstraint;
303
+ const versionConstraint = depInfo.version || 'main';
304
+ // Store as "source@version" for consistency with import resolution
305
+ config.dependencies[depInfo.source] = versionConstraint;
339
306
  }
340
307
  }
341
308
  }
342
309
 
343
- // Parse overrides section for explicit ref control
344
- if (parsed.overrides) {
345
- config.overrides = parsed.overrides;
346
- }
347
-
348
310
  return config;
349
311
  }
350
312
 
351
- /**
352
- * Detects ref conflicts and applies "Latest Wins" resolution strategy.
353
- *
354
- * Resolution Rules:
355
- * - SemVer tags (same major): Pick highest version automatically
356
- * - Same branch refs: No conflict, use single resolution
357
- * - Commit SHA conflicts: Error (explicit pins are intentional)
358
- * - Major version mismatch: Error (breaking change)
359
- * - Tag vs Branch: Error (incompatible intent)
360
- *
361
- * Modifies graph nodes in-place to set the resolved constraint.
362
- * Throws an error only for unresolvable conflicts.
363
- */
364
- private detectVersionConflicts(graph: DependencyGraph): void {
365
- const resolutionMessages: string[] = [];
366
-
367
- for (const [pkg, node] of Object.entries(graph.nodes)) {
368
- const constraints = node.constraints ?? new Set<string>([node.refConstraint]);
369
-
370
- if (constraints.size <= 1) continue; // No conflict
371
-
372
- const refs = Array.from(constraints);
373
- const refTypes = refs.map(ref => ({
374
- ref,
375
- type: detectRefType(ref),
376
- semver: parseSemVer(ref),
377
- }));
378
-
379
- // Check for mixed types (tag vs branch vs commit)
380
- const types = new Set(refTypes.map(r => r.type));
381
-
382
- // Case 1: All commits - must be exact match
383
- if (types.size === 1 && types.has('commit')) {
384
- this.throwConflictError(pkg, refs, node.dependents,
385
- 'Explicit commit pins cannot be automatically resolved.\n' +
386
- 'Add an override in model.yaml:\n\n' +
387
- ' overrides:\n' +
388
- ` ${pkg}: ${refs[0]}`
389
- );
390
- }
391
-
392
- // Case 2: Mixed types (tag vs branch or tag vs commit)
393
- if (types.size > 1) {
394
- this.throwConflictError(pkg, refs, node.dependents,
395
- 'Cannot mix ref types (tags, branches, commits).\n' +
396
- 'Add an override in model.yaml to specify which to use:\n\n' +
397
- ' overrides:\n' +
398
- ` ${pkg}: <ref>`
399
- );
400
- }
401
-
402
- // Case 3: All branches - must be same branch
403
- if (types.size === 1 && types.has('branch')) {
404
- const uniqueBranches = new Set(refs);
405
- if (uniqueBranches.size > 1) {
406
- this.throwConflictError(pkg, refs, node.dependents,
407
- 'Different branch refs cannot be automatically resolved.\n' +
408
- 'Add an override in model.yaml:\n\n' +
409
- ' overrides:\n' +
410
- ` ${pkg}: ${refs[0]}`
411
- );
412
- }
413
- // Same branch - no conflict, continue
414
- continue;
415
- }
416
-
417
- // Case 4: All SemVer tags - apply "Latest Wins"
418
- const semvers = refTypes
419
- .filter((r): r is typeof r & { semver: SemVer } => r.semver !== undefined)
420
- .map(r => r.semver);
421
-
422
- if (semvers.length !== refs.length) {
423
- // Some refs don't parse as SemVer - can't auto-resolve
424
- this.throwConflictError(pkg, refs, node.dependents,
425
- 'Not all refs are valid SemVer tags.\n' +
426
- 'Add an override in model.yaml:\n\n' +
427
- ' overrides:\n' +
428
- ` ${pkg}: <ref>`
429
- );
430
- }
431
-
432
- // Check major version compatibility
433
- const majors = new Set(semvers.map(s => s.major));
434
- if (majors.size > 1) {
435
- const majorList = Array.from(majors).sort().join(' vs ');
436
- this.throwConflictError(pkg, refs, node.dependents,
437
- `Major version mismatch (v${majorList}). This may indicate breaking changes.\n` +
438
- 'Add an override in model.yaml if you want to force a version:\n\n' +
439
- ' overrides:\n' +
440
- ` ${pkg}: ${refs[refs.length - 1]}`
441
- );
442
- }
443
-
444
- // All same major version - pick latest (Latest Wins!)
445
- const latest = pickLatestSemVer(refs);
446
- if (latest && latest !== node.refConstraint) {
447
- // Update the node to use the resolved ref
448
- node.refConstraint = latest;
449
-
450
- // Log the resolution for user feedback
451
- const otherRefs = refs.filter(r => r !== latest).join(', ');
452
- resolutionMessages.push(
453
- `Resolved ${pkg}: using ${latest} (satisfies ${otherRefs})`
454
- );
455
- }
456
- }
457
-
458
- // Store resolution messages for later output
459
- if (resolutionMessages.length > 0) {
460
- this.resolutionMessages = resolutionMessages;
461
- }
462
- }
463
-
464
- /**
465
- * Throws a formatted conflict error with actionable hints.
466
- */
467
- private throwConflictError(
468
- pkg: string,
469
- refs: string[],
470
- dependents: string[],
471
- hint: string
472
- ): never {
473
- const depLines = dependents.map((d, i) =>
474
- ` └─ ${d} requires ${pkg}@${refs[i] || refs[0]}`
475
- ).join('\n');
476
-
477
- throw new Error(
478
- `Dependency ref conflict for '${pkg}'\n` +
479
- depLines + '\n\n' +
480
- hint
481
- );
482
- }
483
-
484
- /** Resolution messages from "Latest Wins" auto-resolution */
485
- private resolutionMessages: string[] = [];
486
-
487
- /**
488
- * Returns any resolution messages from the last dependency resolution.
489
- * Useful for CLI output to inform users about auto-resolved conflicts.
490
- */
491
- getResolutionMessages(): string[] {
492
- return this.resolutionMessages;
493
- }
494
-
495
- /**
496
- * Detects package-level cycles in the dependency graph and throws a clear error.
497
- */
498
- private detectPackageCycles(graph: DependencyGraph): void {
499
- const GRAY = 1, BLACK = 2;
500
- const color: Record<string, number> = {};
501
- const stack: string[] = [];
502
-
503
- const visit = (pkg: string): void => {
504
- color[pkg] = GRAY;
505
- stack.push(pkg);
506
- const deps = Object.keys(graph.nodes[pkg]?.dependencies ?? {});
507
- for (const dep of deps) {
508
- if (!graph.nodes[dep]) continue; // Unknown dep will resolve later
509
- if (color[dep] === GRAY) {
510
- // Found a back edge: cycle
511
- const cycleStart = stack.indexOf(dep);
512
- const cyclePath = [...stack.slice(cycleStart), dep].join(' → ');
513
- throw new Error(
514
- `Cyclic package dependency detected:\n` +
515
- ` ${cyclePath}\n\n` +
516
- `Hint: Extract shared types into a separate package that both can depend on.`
517
- );
518
- }
519
- if (color[dep] !== BLACK) visit(dep);
520
- }
521
- stack.pop();
522
- color[pkg] = BLACK;
523
- };
524
-
525
- for (const pkg of Object.keys(graph.nodes)) {
526
- if (!color[pkg]) visit(pkg);
527
- }
528
- }
529
-
530
313
  /**
531
314
  * Loads an existing lock file from disk.
532
315
  */
@@ -1,10 +1,14 @@
1
1
  // Browser stub for GitUrlResolver
2
- // Git operations are not available in the browser environment
3
2
 
4
- import type { GitImportInfo } from './types.js';
5
-
6
- // Re-export the type for API consistency
7
- export type { GitImportInfo } from './types.js';
3
+ export interface GitImportInfo {
4
+ original: string;
5
+ platform: 'github' | 'gitlab' | 'bitbucket' | 'generic';
6
+ owner: string;
7
+ repo: string;
8
+ version: string;
9
+ repoUrl: string;
10
+ entryPoint: string;
11
+ }
8
12
 
9
13
  export class GitUrlResolver {
10
14
  constructor() {
@@ -12,11 +16,9 @@ export class GitUrlResolver {
12
16
  }
13
17
  }
14
18
 
19
+
15
20
  export const GitUrlParser = {
16
- parse(_importStr: string): GitImportInfo {
17
- throw new Error('GitUrlParser is not available in the browser.');
18
- },
19
- isGitUrl(_importStr: string): boolean {
21
+ parse() {
20
22
  throw new Error('GitUrlParser is not available in the browser.');
21
23
  }
22
24
  };
@@ -24,3 +26,6 @@ export const GitUrlParser = {
24
26
  export function loadLockFile(): void {
25
27
  throw new Error('loadLockFile is not available in the browser.');
26
28
  }
29
+
30
+ export type LockFile = unknown;
31
+ export type LockedDependency = unknown;