@domainlang/cli 0.5.2 → 0.7.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/README.md +16 -16
- package/out/dependency-commands.d.ts +1 -1
- package/out/dependency-commands.js +52 -24
- package/out/dependency-commands.js.map +1 -1
- package/out/main.d.ts +1 -1
- package/out/main.js +1 -1
- package/out/main.js.map +1 -1
- package/out/services/dependency-analyzer.d.ts +59 -0
- package/out/services/dependency-analyzer.js +260 -0
- package/out/services/dependency-analyzer.js.map +1 -0
- package/out/services/dependency-resolver.d.ts +148 -0
- package/out/services/dependency-resolver.js +448 -0
- package/out/services/dependency-resolver.js.map +1 -0
- package/out/services/git-url-resolver.d.ts +158 -0
- package/out/services/git-url-resolver.js +408 -0
- package/out/services/git-url-resolver.js.map +1 -0
- package/out/services/governance-validator.d.ts +56 -0
- package/out/services/governance-validator.js +171 -0
- package/out/services/governance-validator.js.map +1 -0
- package/out/services/index.d.ts +12 -0
- package/out/services/index.js +13 -0
- package/out/services/index.js.map +1 -0
- package/out/services/semver.d.ts +98 -0
- package/out/services/semver.js +195 -0
- package/out/services/semver.js.map +1 -0
- package/out/services/types.d.ts +58 -0
- package/out/services/types.js +8 -0
- package/out/services/types.js.map +1 -0
- package/package.json +4 -3
- package/src/dependency-commands.ts +59 -24
- package/src/main.ts +1 -1
- package/src/services/dependency-analyzer.ts +329 -0
- package/src/services/dependency-resolver.ts +546 -0
- package/src/services/git-url-resolver.ts +504 -0
- package/src/services/governance-validator.ts +226 -0
- package/src/services/index.ts +13 -0
- package/src/services/semver.ts +213 -0
- package/src/services/types.ts +81 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Governance and Compliance Validation Service (CLI-only)
|
|
3
|
+
*
|
|
4
|
+
* Enforces organizational policies and best practices:
|
|
5
|
+
* - Allowed/blocked dependency sources
|
|
6
|
+
* - Version policy enforcement (no pre-release in production)
|
|
7
|
+
* - Team ownership validation
|
|
8
|
+
* - License compliance
|
|
9
|
+
* - Audit trail generation
|
|
10
|
+
*
|
|
11
|
+
* Governance policies are defined in the `governance` section of model.yaml:
|
|
12
|
+
*
|
|
13
|
+
* ```yaml
|
|
14
|
+
* governance:
|
|
15
|
+
* allowedSources:
|
|
16
|
+
* - github.com/acme
|
|
17
|
+
* requireStableVersions: true
|
|
18
|
+
* requireTeamOwnership: true
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type { LockFile, GovernancePolicy, GovernanceMetadata, GovernanceViolation } from './types.js';
|
|
23
|
+
import path from 'node:path';
|
|
24
|
+
import fs from 'node:fs/promises';
|
|
25
|
+
import YAML from 'yaml';
|
|
26
|
+
import { isPreRelease } from './semver.js';
|
|
27
|
+
|
|
28
|
+
/** Locked dependency entry from lock file */
|
|
29
|
+
interface LockedDependency {
|
|
30
|
+
resolved: string;
|
|
31
|
+
ref: string;
|
|
32
|
+
commit: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Validates dependencies against organizational governance policies.
|
|
37
|
+
*/
|
|
38
|
+
export class GovernanceValidator {
|
|
39
|
+
constructor(private readonly policy: GovernancePolicy) {}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Validates a lock file against governance policies.
|
|
43
|
+
*/
|
|
44
|
+
async validate(lockFile: LockFile, workspaceRoot: string): Promise<GovernanceViolation[]> {
|
|
45
|
+
const violations: GovernanceViolation[] = [];
|
|
46
|
+
|
|
47
|
+
// Validate each dependency
|
|
48
|
+
for (const [packageKey, locked] of Object.entries(lockFile.dependencies)) {
|
|
49
|
+
violations.push(
|
|
50
|
+
...this.validateAllowedSources(packageKey, locked),
|
|
51
|
+
...this.validateBlockedPackages(packageKey),
|
|
52
|
+
...this.validateVersionStability(packageKey, locked)
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Validate workspace metadata
|
|
57
|
+
if (this.policy.requireTeamOwnership) {
|
|
58
|
+
const metadata = await this.loadGovernanceMetadata(workspaceRoot);
|
|
59
|
+
if (!metadata.team || !metadata.contact) {
|
|
60
|
+
violations.push({
|
|
61
|
+
type: 'missing-metadata',
|
|
62
|
+
packageKey: 'workspace',
|
|
63
|
+
message: 'Missing required team ownership metadata in model.yaml',
|
|
64
|
+
severity: 'warning',
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return violations;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Checks if package source is allowed by policy.
|
|
74
|
+
*/
|
|
75
|
+
private validateAllowedSources(
|
|
76
|
+
packageKey: string,
|
|
77
|
+
locked: LockedDependency
|
|
78
|
+
): GovernanceViolation[] {
|
|
79
|
+
if (!this.policy.allowedSources || this.policy.allowedSources.length === 0) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const isAllowed = this.policy.allowedSources.some(
|
|
84
|
+
pattern => locked.resolved.includes(pattern) || packageKey.startsWith(pattern)
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (!isAllowed) {
|
|
88
|
+
return [{
|
|
89
|
+
type: 'blocked-source',
|
|
90
|
+
packageKey,
|
|
91
|
+
message: `Package from unauthorized source: ${locked.resolved}`,
|
|
92
|
+
severity: 'error',
|
|
93
|
+
}];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Checks if package is explicitly blocked by policy.
|
|
101
|
+
*/
|
|
102
|
+
private validateBlockedPackages(packageKey: string): GovernanceViolation[] {
|
|
103
|
+
if (!this.policy.blockedPackages) {
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const isBlocked = this.policy.blockedPackages.some(
|
|
108
|
+
pattern => packageKey.includes(pattern)
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
if (isBlocked) {
|
|
112
|
+
return [{
|
|
113
|
+
type: 'blocked-source',
|
|
114
|
+
packageKey,
|
|
115
|
+
message: `Package is blocked by governance policy`,
|
|
116
|
+
severity: 'error',
|
|
117
|
+
}];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Checks if package version meets stability requirements.
|
|
125
|
+
*/
|
|
126
|
+
private validateVersionStability(
|
|
127
|
+
packageKey: string,
|
|
128
|
+
locked: Pick<LockedDependency, 'ref'>
|
|
129
|
+
): GovernanceViolation[] {
|
|
130
|
+
if (!this.policy.requireStableVersions) {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (isPreRelease(locked.ref)) {
|
|
135
|
+
return [{
|
|
136
|
+
type: 'unstable-version',
|
|
137
|
+
packageKey,
|
|
138
|
+
message: `Pre-release ref not allowed: ${locked.ref}`,
|
|
139
|
+
severity: 'error',
|
|
140
|
+
}];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Loads governance metadata from model.yaml.
|
|
148
|
+
*/
|
|
149
|
+
async loadGovernanceMetadata(workspaceRoot: string): Promise<GovernanceMetadata> {
|
|
150
|
+
const manifestPath = path.join(workspaceRoot, 'model.yaml');
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const content = await fs.readFile(manifestPath, 'utf-8');
|
|
154
|
+
const manifest = YAML.parse(content) as {
|
|
155
|
+
metadata?: GovernanceMetadata;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return manifest.metadata ?? {};
|
|
159
|
+
} catch {
|
|
160
|
+
return {};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Generates an audit report for compliance tracking.
|
|
166
|
+
*/
|
|
167
|
+
async generateAuditReport(lockFile: LockFile, workspaceRoot: string): Promise<string> {
|
|
168
|
+
const metadata = await this.loadGovernanceMetadata(workspaceRoot);
|
|
169
|
+
const violations = await this.validate(lockFile, workspaceRoot);
|
|
170
|
+
|
|
171
|
+
// Build header section
|
|
172
|
+
const headerLines = [
|
|
173
|
+
'=== Dependency Audit Report ===',
|
|
174
|
+
'',
|
|
175
|
+
`Workspace: ${workspaceRoot}`,
|
|
176
|
+
`Team: ${metadata.team ?? 'N/A'}`,
|
|
177
|
+
`Contact: ${metadata.contact ?? 'N/A'}`,
|
|
178
|
+
`Domain: ${metadata.domain ?? 'N/A'}`,
|
|
179
|
+
'',
|
|
180
|
+
'Dependencies:',
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
// Build dependencies section
|
|
184
|
+
const depLines: string[] = [];
|
|
185
|
+
for (const [packageKey, locked] of Object.entries(lockFile.dependencies)) {
|
|
186
|
+
depLines.push(
|
|
187
|
+
` - ${packageKey}@${locked.ref}`,
|
|
188
|
+
` Source: ${locked.resolved}`,
|
|
189
|
+
` Commit: ${locked.commit}`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Build violations section
|
|
194
|
+
const violationLines = violations.length > 0
|
|
195
|
+
? [
|
|
196
|
+
'',
|
|
197
|
+
'Violations:',
|
|
198
|
+
...violations.map(v =>
|
|
199
|
+
` [${v.severity.toUpperCase()}] ${v.packageKey}: ${v.message}`
|
|
200
|
+
)
|
|
201
|
+
]
|
|
202
|
+
: ['', '\u2713 No policy violations detected'];
|
|
203
|
+
|
|
204
|
+
return [...headerLines, ...depLines, ...violationLines].join('\n');
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Loads governance policy from model.yaml governance section.
|
|
210
|
+
*/
|
|
211
|
+
export async function loadGovernancePolicy(workspaceRoot: string): Promise<GovernancePolicy> {
|
|
212
|
+
const manifestPath = path.join(workspaceRoot, 'model.yaml');
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const content = await fs.readFile(manifestPath, 'utf-8');
|
|
216
|
+
const manifest = YAML.parse(content) as {
|
|
217
|
+
governance?: GovernancePolicy;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// Return governance section or empty policy if not defined
|
|
221
|
+
return manifest.governance ?? {};
|
|
222
|
+
} catch {
|
|
223
|
+
// No manifest or parse error = permissive defaults
|
|
224
|
+
return {};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Services Index
|
|
3
|
+
*
|
|
4
|
+
* Exports all CLI-only services for package management.
|
|
5
|
+
* These services contain network operations and should never be used in LSP.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export * from './types.js';
|
|
9
|
+
export * from './semver.js';
|
|
10
|
+
export * from './git-url-resolver.js';
|
|
11
|
+
export * from './dependency-resolver.js';
|
|
12
|
+
export * from './dependency-analyzer.js';
|
|
13
|
+
export * from './governance-validator.js';
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic Versioning Utilities (CLI-only)
|
|
3
|
+
*
|
|
4
|
+
* Centralized SemVer parsing, comparison, and validation for the dependency system.
|
|
5
|
+
* All version-related logic should use these utilities to ensure consistency.
|
|
6
|
+
*
|
|
7
|
+
* Supported formats:
|
|
8
|
+
* - "1.0.0" or "v1.0.0" (tags)
|
|
9
|
+
* - "1.0.0-alpha.1" (pre-release)
|
|
10
|
+
* - "main", "develop" (branches)
|
|
11
|
+
* - "abc123def" (commit SHAs, 7-40 hex chars)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { SemVer, RefType, ParsedRef } from './types.js';
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Parsing
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Parses a version string into SemVer components.
|
|
22
|
+
* Returns undefined if not a valid SemVer.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* parseSemVer("v1.2.3") // { major: 1, minor: 2, patch: 3, original: "v1.2.3" }
|
|
26
|
+
* parseSemVer("1.0.0-alpha") // { major: 1, minor: 0, patch: 0, prerelease: "alpha", ... }
|
|
27
|
+
* parseSemVer("main") // undefined (not SemVer)
|
|
28
|
+
*/
|
|
29
|
+
export function parseSemVer(version: string): SemVer | undefined {
|
|
30
|
+
// Strip leading 'v' if present
|
|
31
|
+
const normalized = version.startsWith('v') ? version.slice(1) : version;
|
|
32
|
+
|
|
33
|
+
// Match semver pattern: major.minor.patch[-prerelease]
|
|
34
|
+
const match = normalized.match(/^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/);
|
|
35
|
+
if (!match) return undefined;
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
major: parseInt(match[1], 10),
|
|
39
|
+
minor: parseInt(match[2], 10),
|
|
40
|
+
patch: parseInt(match[3], 10),
|
|
41
|
+
preRelease: match[4],
|
|
42
|
+
original: version,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Detects the type of a git ref based on its format.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* detectRefType("v1.0.0") // 'tag'
|
|
51
|
+
* detectRefType("1.2.3") // 'tag'
|
|
52
|
+
* detectRefType("main") // 'branch'
|
|
53
|
+
* detectRefType("abc123def") // 'commit'
|
|
54
|
+
*/
|
|
55
|
+
export function detectRefType(ref: string): RefType {
|
|
56
|
+
// Commit SHA: 7-40 hex characters
|
|
57
|
+
if (/^[0-9a-f]{7,40}$/i.test(ref)) {
|
|
58
|
+
return 'commit';
|
|
59
|
+
}
|
|
60
|
+
// Tags typically start with 'v' followed by semver
|
|
61
|
+
if (/^v?\d+\.\d+\.\d+/.test(ref)) {
|
|
62
|
+
return 'tag';
|
|
63
|
+
}
|
|
64
|
+
// Everything else is treated as a branch
|
|
65
|
+
return 'branch';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Parses a ref string into a structured ParsedRef with type and optional SemVer.
|
|
70
|
+
*/
|
|
71
|
+
export function parseRef(ref: string): ParsedRef {
|
|
72
|
+
const type = detectRefType(ref);
|
|
73
|
+
const semver = type === 'tag' ? parseSemVer(ref) : undefined;
|
|
74
|
+
|
|
75
|
+
return { original: ref, type, semver };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ============================================================================
|
|
79
|
+
// Comparison
|
|
80
|
+
// ============================================================================
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Compares two SemVer versions.
|
|
84
|
+
* Returns: negative if a < b, positive if a > b, zero if equal.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* compareSemVer(parse("1.0.0"), parse("2.0.0")) // negative (a < b)
|
|
88
|
+
* compareSemVer(parse("1.5.0"), parse("1.2.0")) // positive (a > b)
|
|
89
|
+
* compareSemVer(parse("1.0.0-alpha"), parse("1.0.0")) // negative (prerelease < release)
|
|
90
|
+
*/
|
|
91
|
+
export function compareSemVer(a: SemVer, b: SemVer): number {
|
|
92
|
+
if (a.major !== b.major) return a.major - b.major;
|
|
93
|
+
if (a.minor !== b.minor) return a.minor - b.minor;
|
|
94
|
+
if (a.patch !== b.patch) return a.patch - b.patch;
|
|
95
|
+
|
|
96
|
+
// Pre-release versions are lower than release versions
|
|
97
|
+
if (a.preRelease && !b.preRelease) return -1;
|
|
98
|
+
if (!a.preRelease && b.preRelease) return 1;
|
|
99
|
+
if (a.preRelease && b.preRelease) {
|
|
100
|
+
return a.preRelease.localeCompare(b.preRelease);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Picks the latest from a list of SemVer refs.
|
|
108
|
+
* Returns the ref string (with original 'v' prefix if present).
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* pickLatestSemVer(["v1.0.0", "v1.5.0", "v1.2.0"]) // "v1.5.0"
|
|
112
|
+
*/
|
|
113
|
+
export function pickLatestSemVer(refs: string[]): string | undefined {
|
|
114
|
+
const parsed = refs
|
|
115
|
+
.map(ref => ({ ref, semver: parseSemVer(ref) }))
|
|
116
|
+
.filter((item): item is { ref: string; semver: SemVer } => item.semver !== undefined);
|
|
117
|
+
|
|
118
|
+
if (parsed.length === 0) return undefined;
|
|
119
|
+
|
|
120
|
+
parsed.sort((a, b) => compareSemVer(b.semver, a.semver)); // Descending
|
|
121
|
+
return parsed[0].ref;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Sorts version strings in descending order (newest first).
|
|
126
|
+
* Non-SemVer refs are sorted lexicographically at the end.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* sortVersionsDescending(["v1.0.0", "v2.0.0", "v1.5.0"]) // ["v2.0.0", "v1.5.0", "v1.0.0"]
|
|
130
|
+
*/
|
|
131
|
+
export function sortVersionsDescending(versions: string[]): string[] {
|
|
132
|
+
return [...versions].sort((a, b) => {
|
|
133
|
+
const semverA = parseSemVer(a);
|
|
134
|
+
const semverB = parseSemVer(b);
|
|
135
|
+
|
|
136
|
+
// Both are SemVer - compare semantically
|
|
137
|
+
if (semverA && semverB) {
|
|
138
|
+
return compareSemVer(semverB, semverA); // Descending
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// SemVer comes before non-SemVer
|
|
142
|
+
if (semverA && !semverB) return -1;
|
|
143
|
+
if (!semverA && semverB) return 1;
|
|
144
|
+
|
|
145
|
+
// Both non-SemVer - lexicographic
|
|
146
|
+
return b.localeCompare(a);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ============================================================================
|
|
151
|
+
// Validation
|
|
152
|
+
// ============================================================================
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Checks if a version/ref is a pre-release.
|
|
156
|
+
*
|
|
157
|
+
* Pre-release identifiers: alpha, beta, rc, pre, dev, snapshot
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* isPreRelease("v1.0.0") // false
|
|
161
|
+
* isPreRelease("v1.0.0-alpha") // true
|
|
162
|
+
* isPreRelease("v1.0.0-rc.1") // true
|
|
163
|
+
*/
|
|
164
|
+
export function isPreRelease(ref: string): boolean {
|
|
165
|
+
const semver = parseSemVer(ref);
|
|
166
|
+
if (semver?.preRelease) {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Also check for common pre-release patterns without proper SemVer
|
|
171
|
+
const clean = ref.replace(/^v/, '');
|
|
172
|
+
return /-(alpha|beta|rc|pre|dev|snapshot)/i.test(clean);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Checks if two SemVer versions are compatible (same major version).
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* areSameMajor(parse("1.0.0"), parse("1.5.0")) // true
|
|
180
|
+
* areSameMajor(parse("1.0.0"), parse("2.0.0")) // false
|
|
181
|
+
*/
|
|
182
|
+
export function areSameMajor(a: SemVer, b: SemVer): boolean {
|
|
183
|
+
return a.major === b.major;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Gets the major version number from a ref string.
|
|
188
|
+
* Returns undefined if not a valid SemVer.
|
|
189
|
+
*/
|
|
190
|
+
export function getMajorVersion(ref: string): number | undefined {
|
|
191
|
+
return parseSemVer(ref)?.major;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ============================================================================
|
|
195
|
+
// Filtering
|
|
196
|
+
// ============================================================================
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Filters refs to only stable versions (excludes pre-releases).
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* filterStableVersions(["v1.0.0", "v1.1.0-alpha", "v1.2.0"]) // ["v1.0.0", "v1.2.0"]
|
|
203
|
+
*/
|
|
204
|
+
export function filterStableVersions(refs: string[]): string[] {
|
|
205
|
+
return refs.filter(ref => !isPreRelease(ref));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Filters refs to only SemVer tags (excludes branches and commits).
|
|
210
|
+
*/
|
|
211
|
+
export function filterSemVerTags(refs: string[]): string[] {
|
|
212
|
+
return refs.filter(ref => detectRefType(ref) === 'tag' && parseSemVer(ref) !== undefined);
|
|
213
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for CLI-only package management services.
|
|
3
|
+
*
|
|
4
|
+
* These types support git-based dependency resolution and governance
|
|
5
|
+
* that only runs in the CLI context (never in LSP).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { RefType } from '@domainlang/language';
|
|
9
|
+
|
|
10
|
+
// Re-export types that are shared with language package
|
|
11
|
+
export type {
|
|
12
|
+
LockFile,
|
|
13
|
+
LockedDependency,
|
|
14
|
+
ModelManifest,
|
|
15
|
+
DependencySpec,
|
|
16
|
+
ExtendedDependencySpec,
|
|
17
|
+
PathAliases,
|
|
18
|
+
GovernancePolicy,
|
|
19
|
+
GovernanceMetadata,
|
|
20
|
+
GovernanceViolation,
|
|
21
|
+
DependencyTreeNode,
|
|
22
|
+
ReverseDependency,
|
|
23
|
+
VersionPolicy,
|
|
24
|
+
SemVer,
|
|
25
|
+
RefType,
|
|
26
|
+
ParsedRef,
|
|
27
|
+
} from '@domainlang/language';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Parsed git import URL information.
|
|
31
|
+
*/
|
|
32
|
+
export interface GitImportInfo {
|
|
33
|
+
/** Original import string */
|
|
34
|
+
original: string;
|
|
35
|
+
/** Detected platform (github, gitlab, bitbucket, generic) */
|
|
36
|
+
platform: 'github' | 'gitlab' | 'bitbucket' | 'generic';
|
|
37
|
+
/** Repository owner/organization */
|
|
38
|
+
owner: string;
|
|
39
|
+
/** Repository name */
|
|
40
|
+
repo: string;
|
|
41
|
+
/** Version/tag/branch/commit */
|
|
42
|
+
version: string;
|
|
43
|
+
/** Full repository URL without version */
|
|
44
|
+
repoUrl: string;
|
|
45
|
+
/** Entry point file (default: index.dlang) */
|
|
46
|
+
entryPoint: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Package configuration during dependency resolution.
|
|
51
|
+
*/
|
|
52
|
+
export interface ResolvingPackage {
|
|
53
|
+
name?: string;
|
|
54
|
+
version?: string;
|
|
55
|
+
entry?: string;
|
|
56
|
+
dependencies?: Record<string, string>;
|
|
57
|
+
overrides?: Record<string, string>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Dependency graph for resolution.
|
|
62
|
+
*/
|
|
63
|
+
export interface DependencyGraph {
|
|
64
|
+
nodes: Record<string, DependencyGraphNode>;
|
|
65
|
+
root: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Node in the dependency graph.
|
|
70
|
+
*/
|
|
71
|
+
export interface DependencyGraphNode {
|
|
72
|
+
packageKey: string;
|
|
73
|
+
refConstraint: string;
|
|
74
|
+
constraints?: Set<string>;
|
|
75
|
+
repoUrl?: string;
|
|
76
|
+
dependencies: Record<string, string>;
|
|
77
|
+
dependents: string[];
|
|
78
|
+
resolvedRef?: string;
|
|
79
|
+
refType?: RefType;
|
|
80
|
+
commitHash?: string;
|
|
81
|
+
}
|