@contractspec/bundle.workspace 1.45.3 → 1.45.5
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/dist/adapters/fs.d.ts +2 -1
- package/dist/adapters/fs.d.ts.map +1 -1
- package/dist/adapters/fs.js +1 -1
- package/dist/adapters/fs.js.map +1 -1
- package/dist/adapters/git.js +29 -0
- package/dist/adapters/git.js.map +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +1 -1
- package/dist/index.d.ts +10 -7
- package/dist/index.js +4 -2
- package/dist/ports/git.d.ts +14 -1
- package/dist/ports/git.d.ts.map +1 -1
- package/dist/ports/index.d.ts +1 -1
- package/dist/services/doctor/checks/config.js +132 -0
- package/dist/services/doctor/checks/config.js.map +1 -1
- package/dist/services/hooks/hooks-service.d.ts +24 -0
- package/dist/services/hooks/hooks-service.d.ts.map +1 -0
- package/dist/services/hooks/hooks-service.js +126 -0
- package/dist/services/hooks/hooks-service.js.map +1 -0
- package/dist/services/hooks/index.d.ts +10 -0
- package/dist/services/hooks/index.d.ts.map +1 -0
- package/dist/services/hooks/index.js +12 -0
- package/dist/services/hooks/index.js.map +1 -0
- package/dist/services/hooks/types.d.ts +56 -0
- package/dist/services/hooks/types.d.ts.map +1 -0
- package/dist/services/index.d.ts +4 -1
- package/dist/services/index.js +2 -0
- package/dist/services/setup/config-generators.d.ts.map +1 -1
- package/dist/services/setup/config-generators.js +14 -0
- package/dist/services/setup/config-generators.js.map +1 -1
- package/dist/services/upgrade/index.d.ts +10 -0
- package/dist/services/upgrade/index.d.ts.map +1 -0
- package/dist/services/upgrade/index.js +15 -0
- package/dist/services/upgrade/index.js.map +1 -0
- package/dist/services/upgrade/types.d.ts +78 -0
- package/dist/services/upgrade/types.d.ts.map +1 -0
- package/dist/services/upgrade/upgrade-service.d.ts +38 -0
- package/dist/services/upgrade/upgrade-service.d.ts.map +1 -0
- package/dist/services/upgrade/upgrade-service.js +201 -0
- package/dist/services/upgrade/upgrade-service.js.map +1 -0
- package/dist/services/versioning/conventional-commits.d.ts +95 -0
- package/dist/services/versioning/conventional-commits.d.ts.map +1 -0
- package/dist/services/versioning/conventional-commits.js +184 -0
- package/dist/services/versioning/conventional-commits.js.map +1 -0
- package/dist/services/versioning/index.d.ts +4 -3
- package/dist/services/versioning/index.js +13 -2
- package/dist/services/versioning/index.js.map +1 -1
- package/dist/services/versioning/types.d.ts +9 -7
- package/dist/services/versioning/types.d.ts.map +1 -1
- package/dist/services/versioning/versioning-service.d.ts +43 -1
- package/dist/services/versioning/versioning-service.d.ts.map +1 -1
- package/dist/services/versioning/versioning-service.js +144 -2
- package/dist/services/versioning/versioning-service.js.map +1 -1
- package/package.json +7 -7
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { detectPackageManager, findWorkspaceRoot } from "../../adapters/workspace.js";
|
|
2
|
+
|
|
3
|
+
//#region src/services/upgrade/upgrade-service.ts
|
|
4
|
+
const LATEST_SCHEMA_URL = `https://${process.env["CONTRACTSPEC_API_HOST"] ?? "api.contractspec.io"}/schemas/contractsrc.json`;
|
|
5
|
+
/**
|
|
6
|
+
* Analyze what upgrades are available.
|
|
7
|
+
*/
|
|
8
|
+
async function analyzeUpgrades(adapters, options) {
|
|
9
|
+
const { fs, logger } = adapters;
|
|
10
|
+
const workspaceRoot = findWorkspaceRoot(options.workspaceRoot);
|
|
11
|
+
const packageManager = detectPackageManager(workspaceRoot);
|
|
12
|
+
logger.info("Analyzing available upgrades...", {
|
|
13
|
+
workspaceRoot,
|
|
14
|
+
packageManager
|
|
15
|
+
});
|
|
16
|
+
const packages = await analyzePackages(fs, workspaceRoot);
|
|
17
|
+
const configUpgrades = await analyzeConfig(fs, workspaceRoot);
|
|
18
|
+
return {
|
|
19
|
+
packageManager,
|
|
20
|
+
packages,
|
|
21
|
+
configUpgrades,
|
|
22
|
+
hasUpgrades: packages.length > 0 || configUpgrades.length > 0
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Analyze installed packages for potential updates.
|
|
27
|
+
* Checks for all packages in the workspace that match ContractSpec patterns.
|
|
28
|
+
*/
|
|
29
|
+
async function analyzePackages(fs, workspaceRoot) {
|
|
30
|
+
const packageJsonPath = fs.join(workspaceRoot, "package.json");
|
|
31
|
+
if (!await fs.exists(packageJsonPath)) return [];
|
|
32
|
+
try {
|
|
33
|
+
const content = await fs.readFile(packageJsonPath);
|
|
34
|
+
const packageJson = JSON.parse(content);
|
|
35
|
+
const deps = packageJson.dependencies ?? {};
|
|
36
|
+
const devDeps = packageJson.devDependencies ?? {};
|
|
37
|
+
const packages = [];
|
|
38
|
+
const allDeps = {
|
|
39
|
+
...deps,
|
|
40
|
+
...devDeps
|
|
41
|
+
};
|
|
42
|
+
for (const [name, version] of Object.entries(allDeps)) if (name.startsWith("@contractspec/") || name === "contractspec" || name.startsWith("@lssm/")) packages.push({
|
|
43
|
+
name,
|
|
44
|
+
currentVersion: version,
|
|
45
|
+
isDevDependency: !!devDeps[name]
|
|
46
|
+
});
|
|
47
|
+
return packages;
|
|
48
|
+
} catch {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Analyze configuration for upgrade opportunities.
|
|
54
|
+
*/
|
|
55
|
+
async function analyzeConfig(fs, workspaceRoot) {
|
|
56
|
+
const configPath = fs.join(workspaceRoot, ".contractsrc.json");
|
|
57
|
+
if (!await fs.exists(configPath)) return [];
|
|
58
|
+
try {
|
|
59
|
+
const content = await fs.readFile(configPath);
|
|
60
|
+
const config = JSON.parse(content);
|
|
61
|
+
const upgrades = [];
|
|
62
|
+
const currentSchema = config["$schema"];
|
|
63
|
+
if (!currentSchema || currentSchema !== LATEST_SCHEMA_URL) upgrades.push({
|
|
64
|
+
key: "$schema",
|
|
65
|
+
currentValue: currentSchema,
|
|
66
|
+
suggestedValue: LATEST_SCHEMA_URL,
|
|
67
|
+
isNew: !currentSchema
|
|
68
|
+
});
|
|
69
|
+
if (!config["versioning"]) upgrades.push({
|
|
70
|
+
key: "versioning",
|
|
71
|
+
currentValue: void 0,
|
|
72
|
+
suggestedValue: getDefaultVersioningConfig(),
|
|
73
|
+
isNew: true
|
|
74
|
+
});
|
|
75
|
+
else if (config["versioning"]["integrateWithChangesets"] === void 0) upgrades.push({
|
|
76
|
+
key: "versioning.integrateWithChangesets",
|
|
77
|
+
currentValue: void 0,
|
|
78
|
+
suggestedValue: false,
|
|
79
|
+
isNew: true
|
|
80
|
+
});
|
|
81
|
+
if (!config["hooks"]) upgrades.push({
|
|
82
|
+
key: "hooks",
|
|
83
|
+
currentValue: void 0,
|
|
84
|
+
suggestedValue: getDefaultHooksConfig(),
|
|
85
|
+
isNew: true
|
|
86
|
+
});
|
|
87
|
+
return upgrades;
|
|
88
|
+
} catch {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Apply configuration upgrades.
|
|
94
|
+
*
|
|
95
|
+
* Note: Package upgrades must be applied by the app layer (CLI, VSCode)
|
|
96
|
+
* since they require running npm/bun/yarn commands which is platform-specific.
|
|
97
|
+
*/
|
|
98
|
+
async function applyConfigUpgrades(adapters, options) {
|
|
99
|
+
const { fs, logger } = adapters;
|
|
100
|
+
const workspaceRoot = findWorkspaceRoot(options.workspaceRoot);
|
|
101
|
+
if (options.dryRun) logger.info("Dry run - no changes will be made");
|
|
102
|
+
const configPath = fs.join(workspaceRoot, ".contractsrc.json");
|
|
103
|
+
if (!await fs.exists(configPath)) return {
|
|
104
|
+
success: false,
|
|
105
|
+
packagesUpgraded: 0,
|
|
106
|
+
configSectionsUpgraded: 0,
|
|
107
|
+
error: "No .contractsrc.json found",
|
|
108
|
+
summary: "No configuration file to upgrade"
|
|
109
|
+
};
|
|
110
|
+
try {
|
|
111
|
+
const content = await fs.readFile(configPath);
|
|
112
|
+
const config = JSON.parse(content);
|
|
113
|
+
let sectionsUpgraded = 0;
|
|
114
|
+
const currentSchema = config["$schema"];
|
|
115
|
+
if (!currentSchema || currentSchema !== LATEST_SCHEMA_URL) {
|
|
116
|
+
config["$schema"] = LATEST_SCHEMA_URL;
|
|
117
|
+
sectionsUpgraded++;
|
|
118
|
+
}
|
|
119
|
+
if (!config["versioning"]) {
|
|
120
|
+
config["versioning"] = getDefaultVersioningConfig();
|
|
121
|
+
sectionsUpgraded++;
|
|
122
|
+
} else {
|
|
123
|
+
const versioning = config["versioning"];
|
|
124
|
+
if (versioning["integrateWithChangesets"] === void 0) {
|
|
125
|
+
versioning["integrateWithChangesets"] = false;
|
|
126
|
+
sectionsUpgraded++;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (!config["hooks"]) {
|
|
130
|
+
config["hooks"] = getDefaultHooksConfig();
|
|
131
|
+
sectionsUpgraded++;
|
|
132
|
+
}
|
|
133
|
+
if (sectionsUpgraded === 0) return {
|
|
134
|
+
success: true,
|
|
135
|
+
packagesUpgraded: 0,
|
|
136
|
+
configSectionsUpgraded: 0,
|
|
137
|
+
summary: "Configuration is already up to date"
|
|
138
|
+
};
|
|
139
|
+
if (!options.dryRun) {
|
|
140
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
141
|
+
logger.info("Configuration upgraded", { sectionsUpgraded });
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
success: true,
|
|
145
|
+
packagesUpgraded: 0,
|
|
146
|
+
configSectionsUpgraded: sectionsUpgraded,
|
|
147
|
+
summary: options.dryRun ? `Would upgrade ${sectionsUpgraded} config section(s)` : `Upgraded ${sectionsUpgraded} config section(s)`
|
|
148
|
+
};
|
|
149
|
+
} catch (error) {
|
|
150
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
151
|
+
return {
|
|
152
|
+
success: false,
|
|
153
|
+
packagesUpgraded: 0,
|
|
154
|
+
configSectionsUpgraded: 0,
|
|
155
|
+
error: msg,
|
|
156
|
+
summary: `Failed to upgrade config: ${msg}`
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Get the command to upgrade packages.
|
|
162
|
+
*
|
|
163
|
+
* Returns the shell command string that the app layer should execute.
|
|
164
|
+
*/
|
|
165
|
+
function getPackageUpgradeCommand(packageManager, packages, useLatest) {
|
|
166
|
+
const pkgList = useLatest ? packages.map((p) => `${p.name}@latest`).join(" ") : packages.map((p) => p.name).join(" ");
|
|
167
|
+
switch (packageManager) {
|
|
168
|
+
case "bun": return `bun add ${pkgList}`;
|
|
169
|
+
case "pnpm": return `pnpm add ${pkgList}`;
|
|
170
|
+
case "yarn": return `yarn add ${pkgList}`;
|
|
171
|
+
default: return `npm install ${pkgList}`;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Get default versioning configuration.
|
|
176
|
+
*/
|
|
177
|
+
function getDefaultVersioningConfig() {
|
|
178
|
+
return {
|
|
179
|
+
autoBump: false,
|
|
180
|
+
bumpStrategy: "impact",
|
|
181
|
+
changelogTiers: [
|
|
182
|
+
"spec",
|
|
183
|
+
"library",
|
|
184
|
+
"monorepo"
|
|
185
|
+
],
|
|
186
|
+
format: "keep-a-changelog",
|
|
187
|
+
commitChanges: false,
|
|
188
|
+
createTags: false,
|
|
189
|
+
integrateWithChangesets: true
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Get default hooks configuration.
|
|
194
|
+
*/
|
|
195
|
+
function getDefaultHooksConfig() {
|
|
196
|
+
return { "pre-commit": ["contractspec validate", "contractspec integrity check"] };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
//#endregion
|
|
200
|
+
export { analyzeUpgrades, applyConfigUpgrades, getDefaultHooksConfig, getDefaultVersioningConfig, getPackageUpgradeCommand };
|
|
201
|
+
//# sourceMappingURL=upgrade-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrade-service.js","names":["packages: PackageUpgradeInfo[]","upgrades: ConfigUpgradeInfo[]"],"sources":["../../../src/services/upgrade/upgrade-service.ts"],"sourcesContent":["/**\n * Upgrade service.\n *\n * Analyzes and applies upgrades to ContractSpec SDK packages and configuration.\n * This service is platform-agnostic and can be used by CLI, VSCode, or other apps.\n *\n * @module services/upgrade\n */\n\nimport type { FsAdapter } from '../../ports/fs';\nimport type { LoggerAdapter } from '../../ports/logger';\nimport {\n detectPackageManager,\n findWorkspaceRoot,\n} from '../../adapters/workspace';\nimport type {\n UpgradeOptions,\n UpgradeAnalysisResult,\n UpgradeApplyResult,\n PackageUpgradeInfo,\n ConfigUpgradeInfo,\n} from './types';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Constants\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Latest schema URL. */\nconst API_HOST = process.env['CONTRACTSPEC_API_HOST'] ?? 'api.contractspec.io';\nconst LATEST_SCHEMA_URL = `https://${API_HOST}/schemas/contractsrc.json`;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Adapters Type\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface ServiceAdapters {\n fs: FsAdapter;\n logger: LoggerAdapter;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Analysis\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Analyze what upgrades are available.\n */\nexport async function analyzeUpgrades(\n adapters: ServiceAdapters,\n options: UpgradeOptions\n): Promise<UpgradeAnalysisResult> {\n const { fs, logger } = adapters;\n const workspaceRoot = findWorkspaceRoot(options.workspaceRoot);\n const packageManager = detectPackageManager(workspaceRoot);\n\n logger.info('Analyzing available upgrades...', {\n workspaceRoot,\n packageManager,\n });\n\n const packages = await analyzePackages(fs, workspaceRoot);\n const configUpgrades = await analyzeConfig(fs, workspaceRoot);\n\n const hasUpgrades = packages.length > 0 || configUpgrades.length > 0;\n\n return {\n packageManager,\n packages,\n configUpgrades,\n hasUpgrades,\n };\n}\n\n/**\n * Analyze installed packages for potential updates.\n * Checks for all packages in the workspace that match ContractSpec patterns.\n */\nasync function analyzePackages(\n fs: FsAdapter,\n workspaceRoot: string\n): Promise<PackageUpgradeInfo[]> {\n const packageJsonPath = fs.join(workspaceRoot, 'package.json');\n\n if (!(await fs.exists(packageJsonPath))) {\n return [];\n }\n\n try {\n const content = await fs.readFile(packageJsonPath);\n const packageJson = JSON.parse(content) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n\n const deps = packageJson.dependencies ?? {};\n const devDeps = packageJson.devDependencies ?? {};\n\n const packages: PackageUpgradeInfo[] = [];\n const allDeps = { ...deps, ...devDeps };\n\n for (const [name, version] of Object.entries(allDeps)) {\n // Check for ContractSpec related packages\n // Matches @contractspec/*, contractspec, @lssm/* (if internal)\n if (\n name.startsWith('@contractspec/') ||\n name === 'contractspec' ||\n name.startsWith('@lssm/')\n ) {\n packages.push({\n name,\n currentVersion: version,\n isDevDependency: !!devDeps[name],\n });\n }\n }\n\n return packages;\n } catch {\n return [];\n }\n}\n\n/**\n * Analyze configuration for upgrade opportunities.\n */\nasync function analyzeConfig(\n fs: FsAdapter,\n workspaceRoot: string\n): Promise<ConfigUpgradeInfo[]> {\n const configPath = fs.join(workspaceRoot, '.contractsrc.json');\n\n if (!(await fs.exists(configPath))) {\n return [];\n }\n\n try {\n const content = await fs.readFile(configPath);\n const config = JSON.parse(content) as Record<string, unknown>;\n\n const upgrades: ConfigUpgradeInfo[] = [];\n\n // Check $schema\n const currentSchema = config['$schema'] as string | undefined;\n if (!currentSchema || currentSchema !== LATEST_SCHEMA_URL) {\n upgrades.push({\n key: '$schema',\n currentValue: currentSchema,\n suggestedValue: LATEST_SCHEMA_URL,\n isNew: !currentSchema,\n });\n }\n\n // Check versioning\n if (!config['versioning']) {\n upgrades.push({\n key: 'versioning',\n currentValue: undefined,\n suggestedValue: getDefaultVersioningConfig(),\n isNew: true,\n });\n } else {\n const versioning = config['versioning'] as Record<string, unknown>;\n if (versioning['integrateWithChangesets'] === undefined) {\n upgrades.push({\n key: 'versioning.integrateWithChangesets',\n currentValue: undefined,\n suggestedValue: false,\n isNew: true,\n });\n }\n }\n\n // Check hooks\n if (!config['hooks']) {\n upgrades.push({\n key: 'hooks',\n currentValue: undefined,\n suggestedValue: getDefaultHooksConfig(),\n isNew: true,\n });\n }\n\n return upgrades;\n } catch {\n return [];\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Apply Upgrades\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Apply configuration upgrades.\n *\n * Note: Package upgrades must be applied by the app layer (CLI, VSCode)\n * since they require running npm/bun/yarn commands which is platform-specific.\n */\nexport async function applyConfigUpgrades(\n adapters: ServiceAdapters,\n options: UpgradeOptions\n): Promise<UpgradeApplyResult> {\n const { fs, logger } = adapters;\n const workspaceRoot = findWorkspaceRoot(options.workspaceRoot);\n\n if (options.dryRun) {\n logger.info('Dry run - no changes will be made');\n }\n\n const configPath = fs.join(workspaceRoot, '.contractsrc.json');\n\n if (!(await fs.exists(configPath))) {\n return {\n success: false,\n packagesUpgraded: 0,\n configSectionsUpgraded: 0,\n error: 'No .contractsrc.json found',\n summary: 'No configuration file to upgrade',\n };\n }\n\n try {\n const content = await fs.readFile(configPath);\n const config = JSON.parse(content) as Record<string, unknown>;\n\n let sectionsUpgraded = 0;\n\n // Update $schema\n const currentSchema = config['$schema'] as string | undefined;\n if (!currentSchema || currentSchema !== LATEST_SCHEMA_URL) {\n config['$schema'] = LATEST_SCHEMA_URL;\n sectionsUpgraded++;\n }\n\n // Add versioning if missing\n if (!config['versioning']) {\n config['versioning'] = getDefaultVersioningConfig();\n sectionsUpgraded++;\n } else {\n const versioning = config['versioning'] as Record<string, unknown>;\n if (versioning['integrateWithChangesets'] === undefined) {\n versioning['integrateWithChangesets'] = false;\n sectionsUpgraded++;\n }\n }\n\n // Add hooks if missing\n if (!config['hooks']) {\n config['hooks'] = getDefaultHooksConfig();\n sectionsUpgraded++;\n }\n\n if (sectionsUpgraded === 0) {\n return {\n success: true,\n packagesUpgraded: 0,\n configSectionsUpgraded: 0,\n summary: 'Configuration is already up to date',\n };\n }\n\n if (!options.dryRun) {\n await fs.writeFile(configPath, JSON.stringify(config, null, 2) + '\\n');\n logger.info('Configuration upgraded', { sectionsUpgraded });\n }\n\n return {\n success: true,\n packagesUpgraded: 0,\n configSectionsUpgraded: sectionsUpgraded,\n summary: options.dryRun\n ? `Would upgrade ${sectionsUpgraded} config section(s)`\n : `Upgraded ${sectionsUpgraded} config section(s)`,\n };\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n success: false,\n packagesUpgraded: 0,\n configSectionsUpgraded: 0,\n error: msg,\n summary: `Failed to upgrade config: ${msg}`,\n };\n }\n}\n\n/**\n * Get the command to upgrade packages.\n *\n * Returns the shell command string that the app layer should execute.\n */\nexport function getPackageUpgradeCommand(\n packageManager: string,\n packages: PackageUpgradeInfo[],\n useLatest: boolean\n): string {\n const pkgList = useLatest\n ? packages.map((p) => `${p.name}@latest`).join(' ')\n : packages.map((p) => p.name).join(' ');\n\n switch (packageManager) {\n case 'bun':\n return `bun add ${pkgList}`;\n case 'pnpm':\n return `pnpm add ${pkgList}`;\n case 'yarn':\n return `yarn add ${pkgList}`;\n default:\n return `npm install ${pkgList}`;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Default Configs\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Get default versioning configuration.\n */\nexport function getDefaultVersioningConfig(): Record<string, unknown> {\n return {\n autoBump: false,\n bumpStrategy: 'impact',\n changelogTiers: ['spec', 'library', 'monorepo'],\n format: 'keep-a-changelog',\n commitChanges: false,\n createTags: false,\n integrateWithChangesets: true,\n };\n}\n\n/**\n * Get default hooks configuration.\n */\nexport function getDefaultHooksConfig(): Record<string, string[]> {\n return {\n 'pre-commit': ['contractspec validate', 'contractspec integrity check'],\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { ChangeEntry, VersionBumpType } from "@contractspec/lib.contracts";
|
|
2
|
+
|
|
3
|
+
//#region src/services/versioning/conventional-commits.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Parsed conventional commit message.
|
|
7
|
+
*/
|
|
8
|
+
interface ConventionalCommit {
|
|
9
|
+
/** Commit type (feat, fix, chore, etc.) */
|
|
10
|
+
type: string;
|
|
11
|
+
/** Optional scope (e.g., auth, api) */
|
|
12
|
+
scope?: string;
|
|
13
|
+
/** Whether this is a breaking change */
|
|
14
|
+
breaking: boolean;
|
|
15
|
+
/** Commit description */
|
|
16
|
+
description: string;
|
|
17
|
+
/** Commit body (optional) */
|
|
18
|
+
body?: string;
|
|
19
|
+
/** Breaking change description (if any) */
|
|
20
|
+
breakingDescription?: string;
|
|
21
|
+
/** Full original message */
|
|
22
|
+
raw: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Commit type to bump type mapping.
|
|
26
|
+
*/
|
|
27
|
+
type CommitTypeBumpMap = Record<string, VersionBumpType | null>;
|
|
28
|
+
/**
|
|
29
|
+
* Default mapping of commit types to version bump types.
|
|
30
|
+
*
|
|
31
|
+
* - `feat` → minor (new feature)
|
|
32
|
+
* - `fix` → patch (bug fix)
|
|
33
|
+
* - `perf` → patch (performance improvement)
|
|
34
|
+
* - `refactor` → patch (code refactoring)
|
|
35
|
+
* - `docs` → null (no version bump)
|
|
36
|
+
* - `style` → null (no version bump)
|
|
37
|
+
* - `test` → null (no version bump)
|
|
38
|
+
* - `chore` → null (no version bump)
|
|
39
|
+
* - `ci` → null (no version bump)
|
|
40
|
+
* - `build` → null (no version bump)
|
|
41
|
+
*/
|
|
42
|
+
declare const DEFAULT_COMMIT_TYPE_MAP: CommitTypeBumpMap;
|
|
43
|
+
/**
|
|
44
|
+
* Parse a conventional commit message.
|
|
45
|
+
*
|
|
46
|
+
* @param message - Full commit message (may include body)
|
|
47
|
+
* @returns Parsed commit or null if not a valid conventional commit
|
|
48
|
+
*/
|
|
49
|
+
declare function parseConventionalCommit(message: string): ConventionalCommit | null;
|
|
50
|
+
/**
|
|
51
|
+
* Check if a commit message follows conventional commit format.
|
|
52
|
+
*/
|
|
53
|
+
declare function isConventionalCommit(message: string): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Determine the version bump type from a conventional commit.
|
|
56
|
+
*
|
|
57
|
+
* @param commit - Parsed conventional commit
|
|
58
|
+
* @param typeMap - Optional custom type-to-bump mapping
|
|
59
|
+
* @returns Version bump type or null if no bump needed
|
|
60
|
+
*/
|
|
61
|
+
declare function getBumpTypeFromCommit(commit: ConventionalCommit, typeMap?: CommitTypeBumpMap): VersionBumpType | null;
|
|
62
|
+
/**
|
|
63
|
+
* Determine the highest bump type from multiple commits.
|
|
64
|
+
*
|
|
65
|
+
* @param commits - Array of parsed conventional commits
|
|
66
|
+
* @param typeMap - Optional custom type-to-bump mapping
|
|
67
|
+
* @returns Highest version bump type or null if no bump needed
|
|
68
|
+
*/
|
|
69
|
+
declare function getHighestBumpType(commits: ConventionalCommit[], typeMap?: CommitTypeBumpMap): VersionBumpType | null;
|
|
70
|
+
/**
|
|
71
|
+
* Convert a conventional commit to a changelog ChangeEntry.
|
|
72
|
+
*
|
|
73
|
+
* @param commit - Parsed conventional commit
|
|
74
|
+
* @returns ChangeEntry for changelog
|
|
75
|
+
*/
|
|
76
|
+
declare function commitToChangeEntry(commit: ConventionalCommit): ChangeEntry;
|
|
77
|
+
/**
|
|
78
|
+
* Convert multiple commits to an array of ChangeEntries.
|
|
79
|
+
*/
|
|
80
|
+
declare function commitsToChangeEntries(commits: ConventionalCommit[]): ChangeEntry[];
|
|
81
|
+
/**
|
|
82
|
+
* Filter commits by scope (for spec-level changes).
|
|
83
|
+
*
|
|
84
|
+
* @param commits - Array of parsed conventional commits
|
|
85
|
+
* @param scope - Scope to filter by (e.g., "auth", "api")
|
|
86
|
+
* @returns Commits matching the scope
|
|
87
|
+
*/
|
|
88
|
+
declare function filterCommitsByScope(commits: ConventionalCommit[], scope: string): ConventionalCommit[];
|
|
89
|
+
/**
|
|
90
|
+
* Filter commits that trigger version bumps.
|
|
91
|
+
*/
|
|
92
|
+
declare function filterBumpableCommits(commits: ConventionalCommit[], typeMap?: CommitTypeBumpMap): ConventionalCommit[];
|
|
93
|
+
//#endregion
|
|
94
|
+
export { CommitTypeBumpMap, ConventionalCommit, DEFAULT_COMMIT_TYPE_MAP, commitToChangeEntry, commitsToChangeEntries, filterBumpableCommits, filterCommitsByScope, getBumpTypeFromCommit, getHighestBumpType, isConventionalCommit, parseConventionalCommit };
|
|
95
|
+
//# sourceMappingURL=conventional-commits.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conventional-commits.d.ts","names":[],"sources":["../../../src/services/versioning/conventional-commits.ts"],"sourcesContent":[],"mappings":";;;;;;;AAwLkB,UAjKD,kBAAA,CAiKC;EAqCF;EAoDA,IAAA,EAAA,MAAA;EAiBA;EAUA,KAAA,CAAA,EAAA,MAAA;EACL;EACA,QAAA,EAAA,OAAA;EACR;EAAkB,WAAA,EAAA,MAAA;;;;;;;;;;;KApQT,iBAAA,GAAoB,eAAe;;;;;;;;;;;;;;;cAoBlC,yBAAyB;;;;;;;iBAyCtB,uBAAA,mBAEb;;;;iBAwCa,oBAAA;;;;;;;;iBAeA,qBAAA,SACN,8BACC,oBACR;;;;;;;;iBAiBa,kBAAA,UACL,gCACA,oBACR;;;;;;;iBAqCa,mBAAA,SAA4B,qBAAqB;;;;iBAoDjD,sBAAA,UACL,uBACR;;;;;;;;iBAea,oBAAA,UACL,sCAER;;;;iBAOa,qBAAA,UACL,gCACA,oBACR"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
//#region src/services/versioning/conventional-commits.ts
|
|
2
|
+
/**
|
|
3
|
+
* Default mapping of commit types to version bump types.
|
|
4
|
+
*
|
|
5
|
+
* - `feat` → minor (new feature)
|
|
6
|
+
* - `fix` → patch (bug fix)
|
|
7
|
+
* - `perf` → patch (performance improvement)
|
|
8
|
+
* - `refactor` → patch (code refactoring)
|
|
9
|
+
* - `docs` → null (no version bump)
|
|
10
|
+
* - `style` → null (no version bump)
|
|
11
|
+
* - `test` → null (no version bump)
|
|
12
|
+
* - `chore` → null (no version bump)
|
|
13
|
+
* - `ci` → null (no version bump)
|
|
14
|
+
* - `build` → null (no version bump)
|
|
15
|
+
*/
|
|
16
|
+
const DEFAULT_COMMIT_TYPE_MAP = {
|
|
17
|
+
feat: "minor",
|
|
18
|
+
fix: "patch",
|
|
19
|
+
perf: "patch",
|
|
20
|
+
refactor: "patch",
|
|
21
|
+
docs: null,
|
|
22
|
+
style: null,
|
|
23
|
+
test: null,
|
|
24
|
+
chore: null,
|
|
25
|
+
ci: null,
|
|
26
|
+
build: null,
|
|
27
|
+
revert: "patch"
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Regex pattern for parsing conventional commit messages.
|
|
31
|
+
*
|
|
32
|
+
* Matches: type(scope)!: description
|
|
33
|
+
* Groups:
|
|
34
|
+
* 1. type
|
|
35
|
+
* 2. scope (optional)
|
|
36
|
+
* 3. breaking indicator (optional !)
|
|
37
|
+
* 4. description
|
|
38
|
+
*/
|
|
39
|
+
const CONVENTIONAL_COMMIT_PATTERN = /^(\w+)(?:\(([^)]+)\))?(!)?\s*:\s*(.+)$/;
|
|
40
|
+
/**
|
|
41
|
+
* Pattern for detecting BREAKING CHANGE in commit body/footer.
|
|
42
|
+
*/
|
|
43
|
+
const BREAKING_CHANGE_PATTERN = /^BREAKING[ -]CHANGE:\s*(.+)$/im;
|
|
44
|
+
/**
|
|
45
|
+
* Parse a conventional commit message.
|
|
46
|
+
*
|
|
47
|
+
* @param message - Full commit message (may include body)
|
|
48
|
+
* @returns Parsed commit or null if not a valid conventional commit
|
|
49
|
+
*/
|
|
50
|
+
function parseConventionalCommit(message) {
|
|
51
|
+
const lines = message.split("\n");
|
|
52
|
+
const firstLine = lines[0]?.trim();
|
|
53
|
+
if (!firstLine) return null;
|
|
54
|
+
const match = firstLine.match(CONVENTIONAL_COMMIT_PATTERN);
|
|
55
|
+
if (!match) return null;
|
|
56
|
+
const [, type, scope, breakingIndicator, description] = match;
|
|
57
|
+
if (!type || !description) return null;
|
|
58
|
+
const body = lines.slice(1).join("\n").trim() || void 0;
|
|
59
|
+
const breakingMatch = body?.match(BREAKING_CHANGE_PATTERN);
|
|
60
|
+
const breakingDescription = breakingMatch?.[1];
|
|
61
|
+
return {
|
|
62
|
+
type: type.toLowerCase(),
|
|
63
|
+
scope: scope?.toLowerCase(),
|
|
64
|
+
breaking: !!breakingIndicator || !!breakingMatch,
|
|
65
|
+
description: description.trim(),
|
|
66
|
+
body,
|
|
67
|
+
breakingDescription,
|
|
68
|
+
raw: message
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Check if a commit message follows conventional commit format.
|
|
73
|
+
*/
|
|
74
|
+
function isConventionalCommit(message) {
|
|
75
|
+
return parseConventionalCommit(message) !== null;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Determine the version bump type from a conventional commit.
|
|
79
|
+
*
|
|
80
|
+
* @param commit - Parsed conventional commit
|
|
81
|
+
* @param typeMap - Optional custom type-to-bump mapping
|
|
82
|
+
* @returns Version bump type or null if no bump needed
|
|
83
|
+
*/
|
|
84
|
+
function getBumpTypeFromCommit(commit, typeMap = DEFAULT_COMMIT_TYPE_MAP) {
|
|
85
|
+
if (commit.breaking) return "major";
|
|
86
|
+
return typeMap[commit.type] ?? null;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Determine the highest bump type from multiple commits.
|
|
90
|
+
*
|
|
91
|
+
* @param commits - Array of parsed conventional commits
|
|
92
|
+
* @param typeMap - Optional custom type-to-bump mapping
|
|
93
|
+
* @returns Highest version bump type or null if no bump needed
|
|
94
|
+
*/
|
|
95
|
+
function getHighestBumpType(commits, typeMap = DEFAULT_COMMIT_TYPE_MAP) {
|
|
96
|
+
const bumpOrder = [
|
|
97
|
+
"major",
|
|
98
|
+
"minor",
|
|
99
|
+
"patch"
|
|
100
|
+
];
|
|
101
|
+
let highest = null;
|
|
102
|
+
for (const commit of commits) {
|
|
103
|
+
const bumpType = getBumpTypeFromCommit(commit, typeMap);
|
|
104
|
+
if (!bumpType) continue;
|
|
105
|
+
if (!highest) {
|
|
106
|
+
highest = bumpType;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const currentIndex = bumpOrder.indexOf(highest);
|
|
110
|
+
if (bumpOrder.indexOf(bumpType) < currentIndex) highest = bumpType;
|
|
111
|
+
}
|
|
112
|
+
return highest;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Convert a conventional commit to a changelog ChangeEntry.
|
|
116
|
+
*
|
|
117
|
+
* @param commit - Parsed conventional commit
|
|
118
|
+
* @returns ChangeEntry for changelog
|
|
119
|
+
*/
|
|
120
|
+
function commitToChangeEntry(commit) {
|
|
121
|
+
if (commit.breaking) return {
|
|
122
|
+
type: "breaking",
|
|
123
|
+
description: commit.breakingDescription ?? commit.description,
|
|
124
|
+
path: commit.scope
|
|
125
|
+
};
|
|
126
|
+
switch (commit.type) {
|
|
127
|
+
case "feat": return {
|
|
128
|
+
type: "added",
|
|
129
|
+
description: commit.description,
|
|
130
|
+
path: commit.scope
|
|
131
|
+
};
|
|
132
|
+
case "fix": return {
|
|
133
|
+
type: "fixed",
|
|
134
|
+
description: commit.description,
|
|
135
|
+
path: commit.scope
|
|
136
|
+
};
|
|
137
|
+
case "deprecate": return {
|
|
138
|
+
type: "deprecated",
|
|
139
|
+
description: commit.description,
|
|
140
|
+
path: commit.scope
|
|
141
|
+
};
|
|
142
|
+
case "remove": return {
|
|
143
|
+
type: "removed",
|
|
144
|
+
description: commit.description,
|
|
145
|
+
path: commit.scope
|
|
146
|
+
};
|
|
147
|
+
case "security": return {
|
|
148
|
+
type: "security",
|
|
149
|
+
description: commit.description,
|
|
150
|
+
path: commit.scope
|
|
151
|
+
};
|
|
152
|
+
default: return {
|
|
153
|
+
type: "changed",
|
|
154
|
+
description: commit.description,
|
|
155
|
+
path: commit.scope
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Convert multiple commits to an array of ChangeEntries.
|
|
161
|
+
*/
|
|
162
|
+
function commitsToChangeEntries(commits) {
|
|
163
|
+
return commits.map(commitToChangeEntry);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Filter commits by scope (for spec-level changes).
|
|
167
|
+
*
|
|
168
|
+
* @param commits - Array of parsed conventional commits
|
|
169
|
+
* @param scope - Scope to filter by (e.g., "auth", "api")
|
|
170
|
+
* @returns Commits matching the scope
|
|
171
|
+
*/
|
|
172
|
+
function filterCommitsByScope(commits, scope) {
|
|
173
|
+
return commits.filter((c) => c.scope?.toLowerCase() === scope.toLowerCase());
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Filter commits that trigger version bumps.
|
|
177
|
+
*/
|
|
178
|
+
function filterBumpableCommits(commits, typeMap = DEFAULT_COMMIT_TYPE_MAP) {
|
|
179
|
+
return commits.filter((c) => getBumpTypeFromCommit(c, typeMap) !== null);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
//#endregion
|
|
183
|
+
export { DEFAULT_COMMIT_TYPE_MAP, commitToChangeEntry, commitsToChangeEntries, filterBumpableCommits, filterCommitsByScope, getBumpTypeFromCommit, getHighestBumpType, isConventionalCommit, parseConventionalCommit };
|
|
184
|
+
//# sourceMappingURL=conventional-commits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conventional-commits.js","names":["DEFAULT_COMMIT_TYPE_MAP: CommitTypeBumpMap","bumpOrder: VersionBumpType[]","highest: VersionBumpType | null"],"sources":["../../../src/services/versioning/conventional-commits.ts"],"sourcesContent":["/**\n * Conventional commits parser and utilities.\n *\n * Parses conventional commit messages to determine version bump types\n * and extract change information for changelog generation.\n *\n * Supports:\n * - Standard conventional commits (feat, fix, chore, etc.)\n * - Breaking change detection (BREAKING CHANGE: or !)\n * - Scope extraction for spec-level changes\n *\n * @module versioning/conventional-commits\n */\n\nimport type { VersionBumpType, ChangeEntry } from '@contractspec/lib.contracts';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Parsed conventional commit message.\n */\nexport interface ConventionalCommit {\n /** Commit type (feat, fix, chore, etc.) */\n type: string;\n /** Optional scope (e.g., auth, api) */\n scope?: string;\n /** Whether this is a breaking change */\n breaking: boolean;\n /** Commit description */\n description: string;\n /** Commit body (optional) */\n body?: string;\n /** Breaking change description (if any) */\n breakingDescription?: string;\n /** Full original message */\n raw: string;\n}\n\n/**\n * Commit type to bump type mapping.\n */\nexport type CommitTypeBumpMap = Record<string, VersionBumpType | null>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Constants\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Default mapping of commit types to version bump types.\n *\n * - `feat` → minor (new feature)\n * - `fix` → patch (bug fix)\n * - `perf` → patch (performance improvement)\n * - `refactor` → patch (code refactoring)\n * - `docs` → null (no version bump)\n * - `style` → null (no version bump)\n * - `test` → null (no version bump)\n * - `chore` → null (no version bump)\n * - `ci` → null (no version bump)\n * - `build` → null (no version bump)\n */\nexport const DEFAULT_COMMIT_TYPE_MAP: CommitTypeBumpMap = {\n feat: 'minor',\n fix: 'patch',\n perf: 'patch',\n refactor: 'patch',\n docs: null,\n style: null,\n test: null,\n chore: null,\n ci: null,\n build: null,\n revert: 'patch',\n};\n\n/**\n * Regex pattern for parsing conventional commit messages.\n *\n * Matches: type(scope)!: description\n * Groups:\n * 1. type\n * 2. scope (optional)\n * 3. breaking indicator (optional !)\n * 4. description\n */\nconst CONVENTIONAL_COMMIT_PATTERN = /^(\\w+)(?:\\(([^)]+)\\))?(!)?\\s*:\\s*(.+)$/;\n\n/**\n * Pattern for detecting BREAKING CHANGE in commit body/footer.\n */\nconst BREAKING_CHANGE_PATTERN = /^BREAKING[ -]CHANGE:\\s*(.+)$/im;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Parser\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Parse a conventional commit message.\n *\n * @param message - Full commit message (may include body)\n * @returns Parsed commit or null if not a valid conventional commit\n */\nexport function parseConventionalCommit(\n message: string\n): ConventionalCommit | null {\n const lines = message.split('\\n');\n const firstLine = lines[0]?.trim();\n\n if (!firstLine) {\n return null;\n }\n\n const match = firstLine.match(CONVENTIONAL_COMMIT_PATTERN);\n if (!match) {\n return null;\n }\n\n const [, type, scope, breakingIndicator, description] = match;\n\n if (!type || !description) {\n return null;\n }\n\n // Extract body (everything after first line)\n const body = lines.slice(1).join('\\n').trim() || undefined;\n\n // Check for breaking change in body\n const breakingMatch = body?.match(BREAKING_CHANGE_PATTERN);\n const breakingDescription = breakingMatch?.[1];\n\n return {\n type: type.toLowerCase(),\n scope: scope?.toLowerCase(),\n breaking: !!breakingIndicator || !!breakingMatch,\n description: description.trim(),\n body,\n breakingDescription,\n raw: message,\n };\n}\n\n/**\n * Check if a commit message follows conventional commit format.\n */\nexport function isConventionalCommit(message: string): boolean {\n return parseConventionalCommit(message) !== null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Bump Type Determination\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Determine the version bump type from a conventional commit.\n *\n * @param commit - Parsed conventional commit\n * @param typeMap - Optional custom type-to-bump mapping\n * @returns Version bump type or null if no bump needed\n */\nexport function getBumpTypeFromCommit(\n commit: ConventionalCommit,\n typeMap: CommitTypeBumpMap = DEFAULT_COMMIT_TYPE_MAP\n): VersionBumpType | null {\n // Breaking changes always trigger major bump\n if (commit.breaking) {\n return 'major';\n }\n\n // Use type mapping\n return typeMap[commit.type] ?? null;\n}\n\n/**\n * Determine the highest bump type from multiple commits.\n *\n * @param commits - Array of parsed conventional commits\n * @param typeMap - Optional custom type-to-bump mapping\n * @returns Highest version bump type or null if no bump needed\n */\nexport function getHighestBumpType(\n commits: ConventionalCommit[],\n typeMap: CommitTypeBumpMap = DEFAULT_COMMIT_TYPE_MAP\n): VersionBumpType | null {\n const bumpOrder: VersionBumpType[] = ['major', 'minor', 'patch'];\n\n let highest: VersionBumpType | null = null;\n\n for (const commit of commits) {\n const bumpType = getBumpTypeFromCommit(commit, typeMap);\n\n if (!bumpType) continue;\n\n if (!highest) {\n highest = bumpType;\n continue;\n }\n\n // Check if this bump is higher precedence\n const currentIndex = bumpOrder.indexOf(highest);\n const newIndex = bumpOrder.indexOf(bumpType);\n\n if (newIndex < currentIndex) {\n highest = bumpType;\n }\n }\n\n return highest;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Change Entry Conversion\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Convert a conventional commit to a changelog ChangeEntry.\n *\n * @param commit - Parsed conventional commit\n * @returns ChangeEntry for changelog\n */\nexport function commitToChangeEntry(commit: ConventionalCommit): ChangeEntry {\n if (commit.breaking) {\n return {\n type: 'breaking',\n description: commit.breakingDescription ?? commit.description,\n path: commit.scope,\n };\n }\n\n switch (commit.type) {\n case 'feat':\n return {\n type: 'added',\n description: commit.description,\n path: commit.scope,\n };\n case 'fix':\n return {\n type: 'fixed',\n description: commit.description,\n path: commit.scope,\n };\n case 'deprecate':\n return {\n type: 'deprecated',\n description: commit.description,\n path: commit.scope,\n };\n case 'remove':\n return {\n type: 'removed',\n description: commit.description,\n path: commit.scope,\n };\n case 'security':\n return {\n type: 'security',\n description: commit.description,\n path: commit.scope,\n };\n default:\n return {\n type: 'changed',\n description: commit.description,\n path: commit.scope,\n };\n }\n}\n\n/**\n * Convert multiple commits to an array of ChangeEntries.\n */\nexport function commitsToChangeEntries(\n commits: ConventionalCommit[]\n): ChangeEntry[] {\n return commits.map(commitToChangeEntry);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Filtering\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Filter commits by scope (for spec-level changes).\n *\n * @param commits - Array of parsed conventional commits\n * @param scope - Scope to filter by (e.g., \"auth\", \"api\")\n * @returns Commits matching the scope\n */\nexport function filterCommitsByScope(\n commits: ConventionalCommit[],\n scope: string\n): ConventionalCommit[] {\n return commits.filter((c) => c.scope?.toLowerCase() === scope.toLowerCase());\n}\n\n/**\n * Filter commits that trigger version bumps.\n */\nexport function filterBumpableCommits(\n commits: ConventionalCommit[],\n typeMap: CommitTypeBumpMap = DEFAULT_COMMIT_TYPE_MAP\n): ConventionalCommit[] {\n return commits.filter((c) => getBumpTypeFromCommit(c, typeMap) !== null);\n}\n"],"mappings":";;;;;;;;;;;;;;;AA+DA,MAAaA,0BAA6C;CACxD,MAAM;CACN,KAAK;CACL,MAAM;CACN,UAAU;CACV,MAAM;CACN,OAAO;CACP,MAAM;CACN,OAAO;CACP,IAAI;CACJ,OAAO;CACP,QAAQ;CACT;;;;;;;;;;;AAYD,MAAM,8BAA8B;;;;AAKpC,MAAM,0BAA0B;;;;;;;AAYhC,SAAgB,wBACd,SAC2B;CAC3B,MAAM,QAAQ,QAAQ,MAAM,KAAK;CACjC,MAAM,YAAY,MAAM,IAAI,MAAM;AAElC,KAAI,CAAC,UACH,QAAO;CAGT,MAAM,QAAQ,UAAU,MAAM,4BAA4B;AAC1D,KAAI,CAAC,MACH,QAAO;CAGT,MAAM,GAAG,MAAM,OAAO,mBAAmB,eAAe;AAExD,KAAI,CAAC,QAAQ,CAAC,YACZ,QAAO;CAIT,MAAM,OAAO,MAAM,MAAM,EAAE,CAAC,KAAK,KAAK,CAAC,MAAM,IAAI;CAGjD,MAAM,gBAAgB,MAAM,MAAM,wBAAwB;CAC1D,MAAM,sBAAsB,gBAAgB;AAE5C,QAAO;EACL,MAAM,KAAK,aAAa;EACxB,OAAO,OAAO,aAAa;EAC3B,UAAU,CAAC,CAAC,qBAAqB,CAAC,CAAC;EACnC,aAAa,YAAY,MAAM;EAC/B;EACA;EACA,KAAK;EACN;;;;;AAMH,SAAgB,qBAAqB,SAA0B;AAC7D,QAAO,wBAAwB,QAAQ,KAAK;;;;;;;;;AAc9C,SAAgB,sBACd,QACA,UAA6B,yBACL;AAExB,KAAI,OAAO,SACT,QAAO;AAIT,QAAO,QAAQ,OAAO,SAAS;;;;;;;;;AAUjC,SAAgB,mBACd,SACA,UAA6B,yBACL;CACxB,MAAMC,YAA+B;EAAC;EAAS;EAAS;EAAQ;CAEhE,IAAIC,UAAkC;AAEtC,MAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,WAAW,sBAAsB,QAAQ,QAAQ;AAEvD,MAAI,CAAC,SAAU;AAEf,MAAI,CAAC,SAAS;AACZ,aAAU;AACV;;EAIF,MAAM,eAAe,UAAU,QAAQ,QAAQ;AAG/C,MAFiB,UAAU,QAAQ,SAAS,GAE7B,aACb,WAAU;;AAId,QAAO;;;;;;;;AAaT,SAAgB,oBAAoB,QAAyC;AAC3E,KAAI,OAAO,SACT,QAAO;EACL,MAAM;EACN,aAAa,OAAO,uBAAuB,OAAO;EAClD,MAAM,OAAO;EACd;AAGH,SAAQ,OAAO,MAAf;EACE,KAAK,OACH,QAAO;GACL,MAAM;GACN,aAAa,OAAO;GACpB,MAAM,OAAO;GACd;EACH,KAAK,MACH,QAAO;GACL,MAAM;GACN,aAAa,OAAO;GACpB,MAAM,OAAO;GACd;EACH,KAAK,YACH,QAAO;GACL,MAAM;GACN,aAAa,OAAO;GACpB,MAAM,OAAO;GACd;EACH,KAAK,SACH,QAAO;GACL,MAAM;GACN,aAAa,OAAO;GACpB,MAAM,OAAO;GACd;EACH,KAAK,WACH,QAAO;GACL,MAAM;GACN,aAAa,OAAO;GACpB,MAAM,OAAO;GACd;EACH,QACE,QAAO;GACL,MAAM;GACN,aAAa,OAAO;GACpB,MAAM,OAAO;GACd;;;;;;AAOP,SAAgB,uBACd,SACe;AACf,QAAO,QAAQ,IAAI,oBAAoB;;;;;;;;;AAczC,SAAgB,qBACd,SACA,OACsB;AACtB,QAAO,QAAQ,QAAQ,MAAM,EAAE,OAAO,aAAa,KAAK,MAAM,aAAa,CAAC;;;;;AAM9E,SAAgB,sBACd,SACA,UAA6B,yBACP;AACtB,QAAO,QAAQ,QAAQ,MAAM,sBAAsB,GAAG,QAAQ,KAAK,KAAK"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { ChangeEntry, ChangelogDocBlock, ChangelogEntry, ChangelogGenerateOptions, ChangelogGenerateResult, ChangelogJsonExport, ChangelogTier, LibraryChangelogJson, SpecChangelogJson, SpecVersionAnalysis, VersionAnalyzeOptions, VersionAnalyzeResult, VersionBumpOptions, VersionBumpResult, VersionBumpType } from "./types.js";
|
|
2
|
-
import { analyzeVersions, applyVersionBump, generateChangelogs } from "./versioning-service.js";
|
|
1
|
+
import { ChangeEntry, ChangelogDocBlock, ChangelogEntry, ChangelogGenerateOptions, ChangelogGenerateResult, ChangelogJsonExport, ChangelogTier, LibraryChangelogJson, SpecChangelogJson, SpecVersionAnalysis, VersionAnalyzeOptions, VersionAnalyzeResult, VersionBumpOptions, VersionBumpResult, VersionBumpType, VersioningConfig } from "./types.js";
|
|
2
|
+
import { analyzeVersions, analyzeVersionsFromCommits, applyVersionBump, generateChangelogs } from "./versioning-service.js";
|
|
3
3
|
import { formatChangelogJson, formatConventionalChangelog, formatKeepAChangelog } from "./changelog-formatter.js";
|
|
4
|
+
import { CommitTypeBumpMap, ConventionalCommit, DEFAULT_COMMIT_TYPE_MAP, commitToChangeEntry, commitsToChangeEntries, filterBumpableCommits, filterCommitsByScope, getBumpTypeFromCommit, getHighestBumpType, isConventionalCommit, parseConventionalCommit } from "./conventional-commits.js";
|
|
4
5
|
|
|
5
6
|
//#region src/services/versioning/index.d.ts
|
|
6
7
|
declare namespace index_d_exports {
|
|
7
|
-
export { ChangeEntry, ChangelogDocBlock, ChangelogEntry, ChangelogGenerateOptions, ChangelogGenerateResult, ChangelogJsonExport, ChangelogTier, LibraryChangelogJson, SpecChangelogJson, SpecVersionAnalysis, VersionAnalyzeOptions, VersionAnalyzeResult, VersionBumpOptions, VersionBumpResult, VersionBumpType, analyzeVersions, applyVersionBump, formatChangelogJson, formatConventionalChangelog, formatKeepAChangelog, generateChangelogs };
|
|
8
|
+
export { ChangeEntry, ChangelogDocBlock, ChangelogEntry, ChangelogGenerateOptions, ChangelogGenerateResult, ChangelogJsonExport, ChangelogTier, CommitTypeBumpMap, ConventionalCommit, DEFAULT_COMMIT_TYPE_MAP, LibraryChangelogJson, SpecChangelogJson, SpecVersionAnalysis, VersionAnalyzeOptions, VersionAnalyzeResult, VersionBumpOptions, VersionBumpResult, VersionBumpType, VersioningConfig, analyzeVersions, analyzeVersionsFromCommits, applyVersionBump, commitToChangeEntry, commitsToChangeEntries, filterBumpableCommits, filterCommitsByScope, formatChangelogJson, formatConventionalChangelog, formatKeepAChangelog, generateChangelogs, getBumpTypeFromCommit, getHighestBumpType, isConventionalCommit, parseConventionalCommit };
|
|
8
9
|
}
|
|
9
10
|
//#endregion
|
|
10
11
|
export { index_d_exports };
|
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
import { __exportAll } from "../../_virtual/rolldown_runtime.js";
|
|
2
2
|
import { formatChangelogJson, formatConventionalChangelog, formatKeepAChangelog } from "./changelog-formatter.js";
|
|
3
|
-
import {
|
|
3
|
+
import { DEFAULT_COMMIT_TYPE_MAP, commitToChangeEntry, commitsToChangeEntries, filterBumpableCommits, filterCommitsByScope, getBumpTypeFromCommit, getHighestBumpType, isConventionalCommit, parseConventionalCommit } from "./conventional-commits.js";
|
|
4
|
+
import { analyzeVersions, analyzeVersionsFromCommits, applyVersionBump, generateChangelogs } from "./versioning-service.js";
|
|
4
5
|
|
|
5
6
|
//#region src/services/versioning/index.ts
|
|
6
7
|
var versioning_exports = /* @__PURE__ */ __exportAll({
|
|
8
|
+
DEFAULT_COMMIT_TYPE_MAP: () => DEFAULT_COMMIT_TYPE_MAP,
|
|
7
9
|
analyzeVersions: () => analyzeVersions,
|
|
10
|
+
analyzeVersionsFromCommits: () => analyzeVersionsFromCommits,
|
|
8
11
|
applyVersionBump: () => applyVersionBump,
|
|
12
|
+
commitToChangeEntry: () => commitToChangeEntry,
|
|
13
|
+
commitsToChangeEntries: () => commitsToChangeEntries,
|
|
14
|
+
filterBumpableCommits: () => filterBumpableCommits,
|
|
15
|
+
filterCommitsByScope: () => filterCommitsByScope,
|
|
9
16
|
formatChangelogJson: () => formatChangelogJson,
|
|
10
17
|
formatConventionalChangelog: () => formatConventionalChangelog,
|
|
11
18
|
formatKeepAChangelog: () => formatKeepAChangelog,
|
|
12
|
-
generateChangelogs: () => generateChangelogs
|
|
19
|
+
generateChangelogs: () => generateChangelogs,
|
|
20
|
+
getBumpTypeFromCommit: () => getBumpTypeFromCommit,
|
|
21
|
+
getHighestBumpType: () => getHighestBumpType,
|
|
22
|
+
isConventionalCommit: () => isConventionalCommit,
|
|
23
|
+
parseConventionalCommit: () => parseConventionalCommit
|
|
13
24
|
});
|
|
14
25
|
|
|
15
26
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/services/versioning/index.ts"],"sourcesContent":["/**\n * Versioning service module exports.\n *\n * Provides version analysis, version bumping, and changelog generation.\n */\n\nexport * from './types';\nexport {\n analyzeVersions,\n applyVersionBump,\n generateChangelogs,\n} from './versioning-service';\nexport {\n formatKeepAChangelog,\n formatConventionalChangelog,\n formatChangelogJson,\n} from './changelog-formatter';\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/services/versioning/index.ts"],"sourcesContent":["/**\n * Versioning service module exports.\n *\n * Provides version analysis, version bumping, and changelog generation.\n */\n\nexport * from './types';\nexport {\n analyzeVersions,\n applyVersionBump,\n generateChangelogs,\n analyzeVersionsFromCommits,\n} from './versioning-service';\nexport {\n formatKeepAChangelog,\n formatConventionalChangelog,\n formatChangelogJson,\n} from './changelog-formatter';\nexport * from './conventional-commits';\n"],"mappings":""}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChangeEntry, ChangelogDocBlock, ChangelogEntry as ChangelogEntry$1, ChangelogTier, VersionBumpType } from "@contractspec/lib.contracts";
|
|
1
|
+
import { ChangeEntry as ChangeEntry$1, ChangelogDocBlock, ChangelogEntry as ChangelogEntry$1, ChangelogTier, VersionBumpType as VersionBumpType$1, VersioningConfig } from "@contractspec/lib.contracts";
|
|
2
2
|
|
|
3
3
|
//#region src/services/versioning/types.d.ts
|
|
4
4
|
|
|
@@ -20,13 +20,15 @@ interface VersionBumpOptions {
|
|
|
20
20
|
/** Spec file path to bump */
|
|
21
21
|
specPath: string;
|
|
22
22
|
/** Bump type (auto-detected if not specified) */
|
|
23
|
-
bumpType?: VersionBumpType;
|
|
23
|
+
bumpType?: VersionBumpType$1;
|
|
24
24
|
/** Change description for changelog entry */
|
|
25
25
|
changeDescription?: string;
|
|
26
26
|
/** Additional change entries */
|
|
27
|
-
changes?: ChangeEntry[];
|
|
27
|
+
changes?: ChangeEntry$1[];
|
|
28
28
|
/** Dry run (don't write changes) */
|
|
29
29
|
dryRun?: boolean;
|
|
30
|
+
/** Versioning config (for changesets integration) */
|
|
31
|
+
config?: VersioningConfig;
|
|
30
32
|
}
|
|
31
33
|
/** Options for changelog generation */
|
|
32
34
|
interface ChangelogGenerateOptions {
|
|
@@ -52,9 +54,9 @@ interface SpecVersionAnalysis {
|
|
|
52
54
|
/** Suggested new version based on changes */
|
|
53
55
|
suggestedVersion: string;
|
|
54
56
|
/** Suggested bump type */
|
|
55
|
-
bumpType: VersionBumpType;
|
|
57
|
+
bumpType: VersionBumpType$1;
|
|
56
58
|
/** Detected changes requiring version bump */
|
|
57
|
-
changes: ChangeEntry[];
|
|
59
|
+
changes: ChangeEntry$1[];
|
|
58
60
|
/** Whether breaking changes were detected */
|
|
59
61
|
hasBreaking: boolean;
|
|
60
62
|
/** Whether the spec needs a version bump */
|
|
@@ -88,7 +90,7 @@ interface VersionBumpResult {
|
|
|
88
90
|
/** New version */
|
|
89
91
|
newVersion: string;
|
|
90
92
|
/** Bump type applied */
|
|
91
|
-
bumpType: VersionBumpType;
|
|
93
|
+
bumpType: VersionBumpType$1;
|
|
92
94
|
/** Changelog entry created */
|
|
93
95
|
changelogEntry: ChangelogEntry$1;
|
|
94
96
|
/** Error message if failed */
|
|
@@ -129,5 +131,5 @@ interface LibraryChangelogJson {
|
|
|
129
131
|
entries: ChangelogEntry$1[];
|
|
130
132
|
}
|
|
131
133
|
//#endregion
|
|
132
|
-
export { type ChangeEntry, type ChangelogDocBlock, type ChangelogEntry$1 as ChangelogEntry, ChangelogGenerateOptions, ChangelogGenerateResult, ChangelogJsonExport, type ChangelogTier, LibraryChangelogJson, SpecChangelogJson, SpecVersionAnalysis, VersionAnalyzeOptions, VersionAnalyzeResult, VersionBumpOptions, VersionBumpResult, type VersionBumpType };
|
|
134
|
+
export { type ChangeEntry$1 as ChangeEntry, type ChangelogDocBlock, type ChangelogEntry$1 as ChangelogEntry, ChangelogGenerateOptions, ChangelogGenerateResult, ChangelogJsonExport, type ChangelogTier, LibraryChangelogJson, SpecChangelogJson, SpecVersionAnalysis, VersionAnalyzeOptions, VersionAnalyzeResult, VersionBumpOptions, VersionBumpResult, type VersionBumpType$1 as VersionBumpType, type VersioningConfig };
|
|
133
135
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/services/versioning/types.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/services/versioning/types.ts"],"sourcesContent":[],"mappings":";;;;;AA4DiB,UA9BA,qBAAA,CA8BwB;EAkBxB;EAoBA,QAAA,CAAA,EAAA,MAAA;EAgBA;EAoBA,OAAA,CAAA,EAAA,MAAA;EAEC;EAEC,aAAA,CAAA,EAAA,MAAA;EAIX;EAAmB,OAAA,CAAA,EAAA,MAAA,EAAA;EAMV;EAQA,OAAA,CAAA,EAAA,MAAA,EAAA;AAQjB;;UAxHiB,kBAAA;;;;aAIJ;;;;YAID;;;;WAID;;;UAIM,wBAAA;;;;;;UAMP;;;;;;;UAYO,mBAAA;;;;;;;;;;YAUL;;WAED;;;;;;;UAQM,oBAAA;;YAEL;;;;;;;;;;;;;UAcK,iBAAA;;;;;;;;;;;;YAYL;;kBAEM;;;;;UAMD,uBAAA;;kBAEC;;mBAEC;;;;QAIX;;;;;UAMS,mBAAA;;;SAGR;aACI;;;UAII,iBAAA;;;;WAIN;;;UAIM,oBAAA;;;;WAIN"}
|