@contractspec/bundle.workspace 1.45.1 → 1.45.3
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/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/services/doctor/checks/deps.js +7 -0
- package/dist/services/doctor/checks/deps.js.map +1 -1
- package/dist/services/index.d.ts +2 -1
- package/dist/services/index.js +1 -0
- package/dist/services/sync.d.ts.map +1 -1
- package/dist/services/validate/blueprint-validator.js +1 -1
- package/dist/services/validate/blueprint-validator.js.map +1 -1
- package/dist/services/validate/tenant-validator.js +1 -1
- package/dist/services/validate/tenant-validator.js.map +1 -1
- package/dist/services/versioning/changelog-formatter.d.ts +24 -0
- package/dist/services/versioning/changelog-formatter.d.ts.map +1 -0
- package/dist/services/versioning/changelog-formatter.js +155 -0
- package/dist/services/versioning/changelog-formatter.js.map +1 -0
- package/dist/services/versioning/index.d.ts +11 -0
- package/dist/services/versioning/index.d.ts.map +1 -0
- package/dist/services/versioning/index.js +17 -0
- package/dist/services/versioning/index.js.map +1 -0
- package/dist/services/versioning/types.d.ts +133 -0
- package/dist/services/versioning/types.d.ts.map +1 -0
- package/dist/services/versioning/versioning-service.d.ts +32 -0
- package/dist/services/versioning/versioning-service.d.ts.map +1 -0
- package/dist/services/versioning/versioning-service.js +359 -0
- package/dist/services/versioning/versioning-service.js.map +1 -0
- package/dist/templates/app-config.template.js +7 -7
- package/dist/templates/app-config.template.js.map +1 -1
- package/dist/templates/experiment.template.js +4 -4
- package/dist/templates/experiment.template.js.map +1 -1
- package/dist/templates/integration.template.js +3 -3
- package/dist/templates/integration.template.js.map +1 -1
- package/dist/types.d.ts +20 -20
- package/package.json +7 -11
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { ChangeEntry, ChangelogDocBlock, ChangelogEntry as ChangelogEntry$1, ChangelogTier, VersionBumpType } from "@contractspec/lib.contracts";
|
|
2
|
+
|
|
3
|
+
//#region src/services/versioning/types.d.ts
|
|
4
|
+
|
|
5
|
+
/** Options for version analysis */
|
|
6
|
+
interface VersionAnalyzeOptions {
|
|
7
|
+
/** Git ref to compare against (branch, tag, commit) */
|
|
8
|
+
baseline?: string;
|
|
9
|
+
/** Glob pattern for spec discovery */
|
|
10
|
+
pattern?: string;
|
|
11
|
+
/** Workspace root directory */
|
|
12
|
+
workspaceRoot?: string;
|
|
13
|
+
/** Include paths (glob patterns) */
|
|
14
|
+
include?: string[];
|
|
15
|
+
/** Exclude paths (glob patterns) */
|
|
16
|
+
exclude?: string[];
|
|
17
|
+
}
|
|
18
|
+
/** Options for version bump */
|
|
19
|
+
interface VersionBumpOptions {
|
|
20
|
+
/** Spec file path to bump */
|
|
21
|
+
specPath: string;
|
|
22
|
+
/** Bump type (auto-detected if not specified) */
|
|
23
|
+
bumpType?: VersionBumpType;
|
|
24
|
+
/** Change description for changelog entry */
|
|
25
|
+
changeDescription?: string;
|
|
26
|
+
/** Additional change entries */
|
|
27
|
+
changes?: ChangeEntry[];
|
|
28
|
+
/** Dry run (don't write changes) */
|
|
29
|
+
dryRun?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** Options for changelog generation */
|
|
32
|
+
interface ChangelogGenerateOptions {
|
|
33
|
+
/** Git ref to compare against */
|
|
34
|
+
baseline?: string;
|
|
35
|
+
/** Workspace root directory */
|
|
36
|
+
workspaceRoot?: string;
|
|
37
|
+
/** Changelog tiers to generate */
|
|
38
|
+
tiers?: ChangelogTier[];
|
|
39
|
+
/** Output format */
|
|
40
|
+
format?: 'keep-a-changelog' | 'conventional' | 'custom';
|
|
41
|
+
/** Custom template (for 'custom' format) */
|
|
42
|
+
template?: string;
|
|
43
|
+
}
|
|
44
|
+
/** Result of version analysis for a single spec */
|
|
45
|
+
interface SpecVersionAnalysis {
|
|
46
|
+
/** Path to the spec file */
|
|
47
|
+
specPath: string;
|
|
48
|
+
/** Spec key (e.g., "auth.login") */
|
|
49
|
+
specKey: string;
|
|
50
|
+
/** Current version in the spec */
|
|
51
|
+
currentVersion: string;
|
|
52
|
+
/** Suggested new version based on changes */
|
|
53
|
+
suggestedVersion: string;
|
|
54
|
+
/** Suggested bump type */
|
|
55
|
+
bumpType: VersionBumpType;
|
|
56
|
+
/** Detected changes requiring version bump */
|
|
57
|
+
changes: ChangeEntry[];
|
|
58
|
+
/** Whether breaking changes were detected */
|
|
59
|
+
hasBreaking: boolean;
|
|
60
|
+
/** Whether the spec needs a version bump */
|
|
61
|
+
needsBump: boolean;
|
|
62
|
+
}
|
|
63
|
+
/** Aggregated version analysis result */
|
|
64
|
+
interface VersionAnalyzeResult {
|
|
65
|
+
/** Individual spec analyses */
|
|
66
|
+
analyses: SpecVersionAnalysis[];
|
|
67
|
+
/** Total specs analyzed */
|
|
68
|
+
totalSpecs: number;
|
|
69
|
+
/** Specs needing version bump */
|
|
70
|
+
specsNeedingBump: number;
|
|
71
|
+
/** Total breaking changes across all specs */
|
|
72
|
+
totalBreaking: number;
|
|
73
|
+
/** Total non-breaking changes */
|
|
74
|
+
totalNonBreaking: number;
|
|
75
|
+
/** Git baseline used for comparison */
|
|
76
|
+
baseline?: string;
|
|
77
|
+
}
|
|
78
|
+
/** Result of version bump operation */
|
|
79
|
+
interface VersionBumpResult {
|
|
80
|
+
/** Whether the bump was successful */
|
|
81
|
+
success: boolean;
|
|
82
|
+
/** Path to the updated spec file */
|
|
83
|
+
specPath: string;
|
|
84
|
+
/** Spec key */
|
|
85
|
+
specKey: string;
|
|
86
|
+
/** Previous version */
|
|
87
|
+
previousVersion: string;
|
|
88
|
+
/** New version */
|
|
89
|
+
newVersion: string;
|
|
90
|
+
/** Bump type applied */
|
|
91
|
+
bumpType: VersionBumpType;
|
|
92
|
+
/** Changelog entry created */
|
|
93
|
+
changelogEntry: ChangelogEntry$1;
|
|
94
|
+
/** Error message if failed */
|
|
95
|
+
error?: string;
|
|
96
|
+
}
|
|
97
|
+
/** Result of changelog generation */
|
|
98
|
+
interface ChangelogGenerateResult {
|
|
99
|
+
/** Per-spec DocBlock entries */
|
|
100
|
+
specChangelogs: ChangelogDocBlock[];
|
|
101
|
+
/** Library-level markdown by package path */
|
|
102
|
+
libraryMarkdown: Map<string, string>;
|
|
103
|
+
/** Monorepo-level markdown */
|
|
104
|
+
monorepoMarkdown: string;
|
|
105
|
+
/** JSON format for programmatic use */
|
|
106
|
+
json: ChangelogJsonExport;
|
|
107
|
+
/** Total entries generated */
|
|
108
|
+
totalEntries: number;
|
|
109
|
+
}
|
|
110
|
+
/** JSON export format for changelogs */
|
|
111
|
+
interface ChangelogJsonExport {
|
|
112
|
+
generatedAt: string;
|
|
113
|
+
baseline?: string;
|
|
114
|
+
specs: SpecChangelogJson[];
|
|
115
|
+
libraries: LibraryChangelogJson[];
|
|
116
|
+
}
|
|
117
|
+
/** Spec-level changelog in JSON format */
|
|
118
|
+
interface SpecChangelogJson {
|
|
119
|
+
key: string;
|
|
120
|
+
version: string;
|
|
121
|
+
path: string;
|
|
122
|
+
entries: ChangelogEntry$1[];
|
|
123
|
+
}
|
|
124
|
+
/** Library-level changelog in JSON format */
|
|
125
|
+
interface LibraryChangelogJson {
|
|
126
|
+
name: string;
|
|
127
|
+
path: string;
|
|
128
|
+
version: string;
|
|
129
|
+
entries: ChangelogEntry$1[];
|
|
130
|
+
}
|
|
131
|
+
//#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 };
|
|
133
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/services/versioning/types.ts"],"sourcesContent":[],"mappings":";;;;AA8GA;AAoBiB,UAtGA,qBAAA,CAsGuB;EAEtB;EAEC,QAAA,CAAA,EAAA,MAAA;EAIX;EAAmB,OAAA,CAAA,EAAA,MAAA;EAMV;EAQA,aAAA,CAAA,EAAA,MAAiB;EAQjB;;;;;;UAtHA,kBAAA;;;;aAIJ;;;;YAID;;;;;UAMK,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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { FsAdapter } from "../../ports/fs.js";
|
|
2
|
+
import { GitAdapter } from "../../ports/git.js";
|
|
3
|
+
import { LoggerAdapter } from "../../ports/logger.js";
|
|
4
|
+
import { ChangelogGenerateOptions, ChangelogGenerateResult, VersionAnalyzeOptions, VersionAnalyzeResult, VersionBumpOptions, VersionBumpResult } from "./types.js";
|
|
5
|
+
|
|
6
|
+
//#region src/services/versioning/versioning-service.d.ts
|
|
7
|
+
|
|
8
|
+
interface ServiceAdapters {
|
|
9
|
+
fs: FsAdapter;
|
|
10
|
+
git: GitAdapter;
|
|
11
|
+
logger: LoggerAdapter;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Analyze specs and suggest version bumps based on changes.
|
|
15
|
+
*
|
|
16
|
+
* Compares the current state against a baseline (git ref) and determines
|
|
17
|
+
* which specs need version bumps based on detected changes.
|
|
18
|
+
*/
|
|
19
|
+
declare function analyzeVersions(adapters: ServiceAdapters, options?: VersionAnalyzeOptions): Promise<VersionAnalyzeResult>;
|
|
20
|
+
/**
|
|
21
|
+
* Apply a version bump to a spec file.
|
|
22
|
+
*
|
|
23
|
+
* Updates the version in the spec file and creates a changelog entry.
|
|
24
|
+
*/
|
|
25
|
+
declare function applyVersionBump(adapters: ServiceAdapters, options: VersionBumpOptions): Promise<VersionBumpResult>;
|
|
26
|
+
/**
|
|
27
|
+
* Generate changelogs at all configured tiers.
|
|
28
|
+
*/
|
|
29
|
+
declare function generateChangelogs(adapters: ServiceAdapters, options?: ChangelogGenerateOptions): Promise<ChangelogGenerateResult>;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { analyzeVersions, applyVersionBump, generateChangelogs };
|
|
32
|
+
//# sourceMappingURL=versioning-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"versioning-service.d.ts","names":[],"sources":["../../../src/services/versioning/versioning-service.ts"],"sourcesContent":[],"mappings":";;;;;;;UAmCU,eAAA,CAGa;EAaD,EAAA,EAfhB,SAegB;EACV,GAAA,EAfL,UAeK;EACD,MAAA,EAfD,aAeC;;;;AAwGX;;;;AAGG,iBA7GmB,eAAA,CA6GnB,QAAA,EA5GS,eA4GT,EAAA,OAAA,CAAA,EA3GQ,qBA2GR,CAAA,EA1GA,OA0GA,CA1GQ,oBA0GR,CAAA;;AAiGH;;;;AAGG,iBAvGmB,gBAAA,CAuGnB,QAAA,EAtGS,eAsGT,EAAA,OAAA,EArGQ,kBAqGR,CAAA,EApGA,OAoGA,CApGQ,iBAoGR,CAAA;;;;iBAHmB,kBAAA,WACV,2BACD,2BACR,QAAQ"}
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { formatChangelogJson, formatKeepAChangelog } from "./changelog-formatter.js";
|
|
2
|
+
import { bumpVersion, determineBumpType } from "@contractspec/lib.contracts";
|
|
3
|
+
|
|
4
|
+
//#region src/services/versioning/versioning-service.ts
|
|
5
|
+
/**
|
|
6
|
+
* Versioning service.
|
|
7
|
+
*
|
|
8
|
+
* Provides version analysis, version bumping, and changelog generation
|
|
9
|
+
* for ContractSpec specs.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Analyze specs and suggest version bumps based on changes.
|
|
13
|
+
*
|
|
14
|
+
* Compares the current state against a baseline (git ref) and determines
|
|
15
|
+
* which specs need version bumps based on detected changes.
|
|
16
|
+
*/
|
|
17
|
+
async function analyzeVersions(adapters, options = {}) {
|
|
18
|
+
const { fs, git, logger } = adapters;
|
|
19
|
+
const workspaceRoot = options.workspaceRoot ?? process.cwd();
|
|
20
|
+
logger.info("Starting version analysis...", { baseline: options.baseline });
|
|
21
|
+
const pattern = options.pattern ?? "**/*.{operation,event,presentation}.ts";
|
|
22
|
+
const specFiles = (await fs.glob({
|
|
23
|
+
pattern,
|
|
24
|
+
cwd: workspaceRoot
|
|
25
|
+
})).filter((f) => !f.includes(".test.") && !f.includes(".spec.") && !f.includes("node_modules"));
|
|
26
|
+
logger.debug(`Found ${specFiles.length} spec files`);
|
|
27
|
+
const analyses = [];
|
|
28
|
+
let totalBreaking = 0;
|
|
29
|
+
let totalNonBreaking = 0;
|
|
30
|
+
for (const specPath of specFiles) try {
|
|
31
|
+
const currentContent = await fs.readFile(specPath);
|
|
32
|
+
const currentMeta = extractSpecMeta(currentContent);
|
|
33
|
+
if (!currentMeta) continue;
|
|
34
|
+
let baselineContent = null;
|
|
35
|
+
if (options.baseline) try {
|
|
36
|
+
baselineContent = await git.showFile(options.baseline, specPath);
|
|
37
|
+
} catch {
|
|
38
|
+
baselineContent = null;
|
|
39
|
+
}
|
|
40
|
+
const changes = analyzeSpecChanges(currentContent, baselineContent);
|
|
41
|
+
const hasBreaking = changes.some((c) => c.type === "breaking");
|
|
42
|
+
const hasNonBreaking = changes.length > 0 && !hasBreaking;
|
|
43
|
+
const needsBump = changes.length > 0;
|
|
44
|
+
if (hasBreaking) totalBreaking++;
|
|
45
|
+
if (hasNonBreaking) totalNonBreaking++;
|
|
46
|
+
const bumpType = determineBumpType(hasBreaking, hasNonBreaking);
|
|
47
|
+
const suggestedVersion = needsBump ? bumpVersion(currentMeta.version, bumpType) : currentMeta.version;
|
|
48
|
+
analyses.push({
|
|
49
|
+
specPath,
|
|
50
|
+
specKey: currentMeta.key,
|
|
51
|
+
currentVersion: currentMeta.version,
|
|
52
|
+
suggestedVersion,
|
|
53
|
+
bumpType,
|
|
54
|
+
changes,
|
|
55
|
+
hasBreaking,
|
|
56
|
+
needsBump
|
|
57
|
+
});
|
|
58
|
+
} catch (error) {
|
|
59
|
+
logger.warn(`Failed to analyze ${specPath}:`, { error: error instanceof Error ? error.message : String(error) });
|
|
60
|
+
}
|
|
61
|
+
const specsNeedingBump = analyses.filter((a) => a.needsBump).length;
|
|
62
|
+
logger.info("Version analysis complete", {
|
|
63
|
+
totalSpecs: analyses.length,
|
|
64
|
+
specsNeedingBump,
|
|
65
|
+
totalBreaking,
|
|
66
|
+
totalNonBreaking
|
|
67
|
+
});
|
|
68
|
+
return {
|
|
69
|
+
analyses,
|
|
70
|
+
totalSpecs: analyses.length,
|
|
71
|
+
specsNeedingBump,
|
|
72
|
+
totalBreaking,
|
|
73
|
+
totalNonBreaking,
|
|
74
|
+
baseline: options.baseline
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Apply a version bump to a spec file.
|
|
79
|
+
*
|
|
80
|
+
* Updates the version in the spec file and creates a changelog entry.
|
|
81
|
+
*/
|
|
82
|
+
async function applyVersionBump(adapters, options) {
|
|
83
|
+
const { fs, logger } = adapters;
|
|
84
|
+
const { specPath, dryRun = false } = options;
|
|
85
|
+
logger.info("Applying version bump...", {
|
|
86
|
+
specPath,
|
|
87
|
+
bumpType: options.bumpType
|
|
88
|
+
});
|
|
89
|
+
try {
|
|
90
|
+
const content = await fs.readFile(specPath);
|
|
91
|
+
const meta = extractSpecMeta(content);
|
|
92
|
+
if (!meta) return {
|
|
93
|
+
success: false,
|
|
94
|
+
specPath,
|
|
95
|
+
specKey: "unknown",
|
|
96
|
+
previousVersion: "unknown",
|
|
97
|
+
newVersion: "unknown",
|
|
98
|
+
bumpType: "patch",
|
|
99
|
+
changelogEntry: createEmptyChangelogEntry(),
|
|
100
|
+
error: "Could not extract spec metadata"
|
|
101
|
+
};
|
|
102
|
+
const bumpType = options.bumpType ?? "patch";
|
|
103
|
+
const newVersion = bumpVersion(meta.version, bumpType);
|
|
104
|
+
const changes = options.changes ?? [];
|
|
105
|
+
if (options.changeDescription) changes.push({
|
|
106
|
+
type: bumpType === "major" ? "breaking" : "changed",
|
|
107
|
+
description: options.changeDescription
|
|
108
|
+
});
|
|
109
|
+
const changelogEntry = {
|
|
110
|
+
version: newVersion,
|
|
111
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0] ?? "",
|
|
112
|
+
bumpType,
|
|
113
|
+
changes,
|
|
114
|
+
breakingChanges: changes.filter((c) => c.type === "breaking")
|
|
115
|
+
};
|
|
116
|
+
if (!dryRun) {
|
|
117
|
+
const updatedContent = updateSpecVersion(content, meta.version, newVersion);
|
|
118
|
+
await fs.writeFile(specPath, updatedContent);
|
|
119
|
+
}
|
|
120
|
+
logger.info("Version bump applied", {
|
|
121
|
+
specPath,
|
|
122
|
+
previousVersion: meta.version,
|
|
123
|
+
newVersion
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
success: true,
|
|
127
|
+
specPath,
|
|
128
|
+
specKey: meta.key,
|
|
129
|
+
previousVersion: meta.version,
|
|
130
|
+
newVersion,
|
|
131
|
+
bumpType,
|
|
132
|
+
changelogEntry
|
|
133
|
+
};
|
|
134
|
+
} catch (error) {
|
|
135
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
136
|
+
logger.error("Version bump failed", {
|
|
137
|
+
specPath,
|
|
138
|
+
error: errorMessage
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
success: false,
|
|
142
|
+
specPath,
|
|
143
|
+
specKey: "unknown",
|
|
144
|
+
previousVersion: "unknown",
|
|
145
|
+
newVersion: "unknown",
|
|
146
|
+
bumpType: options.bumpType ?? "patch",
|
|
147
|
+
changelogEntry: createEmptyChangelogEntry(),
|
|
148
|
+
error: errorMessage
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Generate changelogs at all configured tiers.
|
|
154
|
+
*/
|
|
155
|
+
async function generateChangelogs(adapters, options = {}) {
|
|
156
|
+
const { logger } = adapters;
|
|
157
|
+
const tiers = options.tiers ?? [
|
|
158
|
+
"spec",
|
|
159
|
+
"library",
|
|
160
|
+
"monorepo"
|
|
161
|
+
];
|
|
162
|
+
const format = options.format ?? "keep-a-changelog";
|
|
163
|
+
logger.info("Generating changelogs...", {
|
|
164
|
+
tiers,
|
|
165
|
+
format
|
|
166
|
+
});
|
|
167
|
+
const analysis = await analyzeVersions(adapters, {
|
|
168
|
+
baseline: options.baseline,
|
|
169
|
+
workspaceRoot: options.workspaceRoot
|
|
170
|
+
});
|
|
171
|
+
const specChangelogs = [];
|
|
172
|
+
const libraryMarkdown = /* @__PURE__ */ new Map();
|
|
173
|
+
let monorepoMarkdown = "";
|
|
174
|
+
let totalEntries = 0;
|
|
175
|
+
if (tiers.includes("spec")) for (const spec of analysis.analyses.filter((a) => a.needsBump)) {
|
|
176
|
+
const docBlock = {
|
|
177
|
+
id: `changelog.${spec.specKey}`,
|
|
178
|
+
title: `Changelog for ${spec.specKey}`,
|
|
179
|
+
body: formatSpecChangelog(spec),
|
|
180
|
+
kind: "changelog",
|
|
181
|
+
specKey: spec.specKey,
|
|
182
|
+
specVersion: spec.suggestedVersion,
|
|
183
|
+
entries: [{
|
|
184
|
+
version: spec.suggestedVersion,
|
|
185
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0] ?? "",
|
|
186
|
+
bumpType: spec.bumpType,
|
|
187
|
+
changes: spec.changes,
|
|
188
|
+
breakingChanges: spec.changes.filter((c) => c.type === "breaking")
|
|
189
|
+
}]
|
|
190
|
+
};
|
|
191
|
+
specChangelogs.push(docBlock);
|
|
192
|
+
totalEntries++;
|
|
193
|
+
}
|
|
194
|
+
if (tiers.includes("library")) {
|
|
195
|
+
const libraryGroups = groupByLibrary(analysis.analyses);
|
|
196
|
+
for (const [libPath, specs] of libraryGroups) {
|
|
197
|
+
const entries = specs.filter((s) => s.needsBump);
|
|
198
|
+
if (entries.length > 0) {
|
|
199
|
+
libraryMarkdown.set(libPath, formatKeepAChangelog(entries.map(specToChangelogEntry)));
|
|
200
|
+
totalEntries += entries.length;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (tiers.includes("monorepo")) monorepoMarkdown = formatKeepAChangelog(analysis.analyses.filter((a) => a.needsBump).map(specToChangelogEntry));
|
|
205
|
+
const json = formatChangelogJson(analysis, options.baseline);
|
|
206
|
+
logger.info("Changelog generation complete", { totalEntries });
|
|
207
|
+
return {
|
|
208
|
+
specChangelogs,
|
|
209
|
+
libraryMarkdown,
|
|
210
|
+
monorepoMarkdown,
|
|
211
|
+
json,
|
|
212
|
+
totalEntries
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Extract spec metadata from file content.
|
|
217
|
+
*/
|
|
218
|
+
function extractSpecMeta(content) {
|
|
219
|
+
const keyMatch = content.match(/key:\s*['"`]([^'"`]+)['"`]/);
|
|
220
|
+
const versionMatch = content.match(/version:\s*['"`]([^'"`]+)['"`]/);
|
|
221
|
+
if (!keyMatch || !versionMatch) return null;
|
|
222
|
+
const key = keyMatch[1];
|
|
223
|
+
const version = versionMatch[1];
|
|
224
|
+
if (!key || !version) return null;
|
|
225
|
+
return {
|
|
226
|
+
key,
|
|
227
|
+
version
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Analyze changes between current and baseline spec content.
|
|
232
|
+
*/
|
|
233
|
+
function analyzeSpecChanges(current, baseline) {
|
|
234
|
+
const changes = [];
|
|
235
|
+
if (!baseline) {
|
|
236
|
+
changes.push({
|
|
237
|
+
type: "added",
|
|
238
|
+
description: "New spec added"
|
|
239
|
+
});
|
|
240
|
+
return changes;
|
|
241
|
+
}
|
|
242
|
+
const currentMeta = extractSpecMeta(current);
|
|
243
|
+
const baselineMeta = extractSpecMeta(baseline);
|
|
244
|
+
if (!currentMeta || !baselineMeta) return changes;
|
|
245
|
+
if (currentMeta.version !== baselineMeta.version) changes.push({
|
|
246
|
+
type: "changed",
|
|
247
|
+
description: `Version updated from ${baselineMeta.version} to ${currentMeta.version}`,
|
|
248
|
+
path: "meta.version"
|
|
249
|
+
});
|
|
250
|
+
const hasIoChanges = detectIoChanges(current, baseline);
|
|
251
|
+
if (hasIoChanges.breaking) changes.push({
|
|
252
|
+
type: "breaking",
|
|
253
|
+
description: "Breaking changes to input/output schema",
|
|
254
|
+
path: "io"
|
|
255
|
+
});
|
|
256
|
+
else if (hasIoChanges.nonBreaking) changes.push({
|
|
257
|
+
type: "changed",
|
|
258
|
+
description: "Non-breaking changes to input/output schema",
|
|
259
|
+
path: "io"
|
|
260
|
+
});
|
|
261
|
+
return changes;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Detect I/O changes between specs (simplified).
|
|
265
|
+
*/
|
|
266
|
+
function detectIoChanges(current, baseline) {
|
|
267
|
+
const ioPattern = /io:\s*\{[\s\S]*?\}/;
|
|
268
|
+
const currentIo = current.match(ioPattern)?.[0] ?? "";
|
|
269
|
+
const baselineIo = baseline.match(ioPattern)?.[0] ?? "";
|
|
270
|
+
if (currentIo === baselineIo) return {
|
|
271
|
+
breaking: false,
|
|
272
|
+
nonBreaking: false
|
|
273
|
+
};
|
|
274
|
+
const currentFields = extractFields(currentIo);
|
|
275
|
+
const baselineFields = extractFields(baselineIo);
|
|
276
|
+
const removedFields = baselineFields.filter((f) => !currentFields.includes(f));
|
|
277
|
+
const addedFields = currentFields.filter((f) => !baselineFields.includes(f));
|
|
278
|
+
return {
|
|
279
|
+
breaking: removedFields.length > 0,
|
|
280
|
+
nonBreaking: addedFields.length > 0 && removedFields.length === 0
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Extract field names from a code block (simplified).
|
|
285
|
+
*/
|
|
286
|
+
function extractFields(code) {
|
|
287
|
+
return [...code.matchAll(/(\w+):/g)].map((m) => m[1]).filter((field) => !!field);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Update version in spec content.
|
|
291
|
+
*/
|
|
292
|
+
function updateSpecVersion(content, oldVersion, newVersion) {
|
|
293
|
+
return content.replace(/* @__PURE__ */ new RegExp(`version:\\s*['"\`]${oldVersion}['"\`]`), `version: '${newVersion}'`);
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Create an empty changelog entry.
|
|
297
|
+
*/
|
|
298
|
+
function createEmptyChangelogEntry() {
|
|
299
|
+
return {
|
|
300
|
+
version: "0.0.0",
|
|
301
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0] ?? "",
|
|
302
|
+
bumpType: "patch",
|
|
303
|
+
changes: []
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Format a single spec's changelog.
|
|
308
|
+
*/
|
|
309
|
+
function formatSpecChangelog(spec) {
|
|
310
|
+
const lines = [`## [${spec.suggestedVersion}] - ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`, ""];
|
|
311
|
+
const breaking = spec.changes.filter((c) => c.type === "breaking");
|
|
312
|
+
const changed = spec.changes.filter((c) => c.type === "changed");
|
|
313
|
+
const added = spec.changes.filter((c) => c.type === "added");
|
|
314
|
+
if (breaking.length > 0) {
|
|
315
|
+
lines.push("### Breaking Changes");
|
|
316
|
+
breaking.forEach((c) => lines.push(`- ${c.description}`));
|
|
317
|
+
lines.push("");
|
|
318
|
+
}
|
|
319
|
+
if (added.length > 0) {
|
|
320
|
+
lines.push("### Added");
|
|
321
|
+
added.forEach((c) => lines.push(`- ${c.description}`));
|
|
322
|
+
lines.push("");
|
|
323
|
+
}
|
|
324
|
+
if (changed.length > 0) {
|
|
325
|
+
lines.push("### Changed");
|
|
326
|
+
changed.forEach((c) => lines.push(`- ${c.description}`));
|
|
327
|
+
lines.push("");
|
|
328
|
+
}
|
|
329
|
+
return lines.join("\n");
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Group specs by library (package path).
|
|
333
|
+
*/
|
|
334
|
+
function groupByLibrary(analyses) {
|
|
335
|
+
const groups = /* @__PURE__ */ new Map();
|
|
336
|
+
for (const analysis of analyses) {
|
|
337
|
+
const libPath = analysis.specPath.match(/(.+?\/src\/)/)?.[1] ?? analysis.specPath;
|
|
338
|
+
const existing = groups.get(libPath) ?? [];
|
|
339
|
+
existing.push(analysis);
|
|
340
|
+
groups.set(libPath, existing);
|
|
341
|
+
}
|
|
342
|
+
return groups;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Convert SpecVersionAnalysis to ChangelogEntry.
|
|
346
|
+
*/
|
|
347
|
+
function specToChangelogEntry(spec) {
|
|
348
|
+
return {
|
|
349
|
+
version: spec.suggestedVersion,
|
|
350
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0] ?? "",
|
|
351
|
+
bumpType: spec.bumpType,
|
|
352
|
+
changes: spec.changes,
|
|
353
|
+
breakingChanges: spec.changes.filter((c) => c.type === "breaking")
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
//#endregion
|
|
358
|
+
export { analyzeVersions, applyVersionBump, generateChangelogs };
|
|
359
|
+
//# sourceMappingURL=versioning-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"versioning-service.js","names":["analyses: SpecVersionAnalysis[]","baselineContent: string | null","changes: ChangeEntry[]","changelogEntry: ChangelogEntry","specChangelogs: ChangelogDocBlock[]","docBlock: ChangelogDocBlock","lines: string[]"],"sources":["../../../src/services/versioning/versioning-service.ts"],"sourcesContent":["/**\n * Versioning service.\n *\n * Provides version analysis, version bumping, and changelog generation\n * for ContractSpec specs.\n */\n\nimport {\n bumpVersion,\n determineBumpType,\n type ChangeEntry,\n type ChangelogEntry,\n type ChangelogDocBlock,\n} from '@contractspec/lib.contracts';\nimport type { FsAdapter } from '../../ports/fs';\nimport type { GitAdapter } from '../../ports/git';\nimport type { LoggerAdapter } from '../../ports/logger';\nimport type {\n VersionAnalyzeOptions,\n VersionAnalyzeResult,\n VersionBumpOptions,\n VersionBumpResult,\n ChangelogGenerateOptions,\n ChangelogGenerateResult,\n SpecVersionAnalysis,\n} from './types';\nimport {\n formatKeepAChangelog,\n formatChangelogJson,\n} from './changelog-formatter';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Adapters Type\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface ServiceAdapters {\n fs: FsAdapter;\n git: GitAdapter;\n logger: LoggerAdapter;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Version Analysis\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Analyze specs and suggest version bumps based on changes.\n *\n * Compares the current state against a baseline (git ref) and determines\n * which specs need version bumps based on detected changes.\n */\nexport async function analyzeVersions(\n adapters: ServiceAdapters,\n options: VersionAnalyzeOptions = {}\n): Promise<VersionAnalyzeResult> {\n const { fs, git, logger } = adapters;\n const workspaceRoot = options.workspaceRoot ?? process.cwd();\n\n logger.info('Starting version analysis...', { baseline: options.baseline });\n\n // Discover spec files\n const pattern = options.pattern ?? '**/*.{operation,event,presentation}.ts';\n const files = await fs.glob({ pattern, cwd: workspaceRoot });\n\n const specFiles = files.filter(\n (f) =>\n !f.includes('.test.') &&\n !f.includes('.spec.') &&\n !f.includes('node_modules')\n );\n\n logger.debug(`Found ${specFiles.length} spec files`);\n\n const analyses: SpecVersionAnalysis[] = [];\n let totalBreaking = 0;\n let totalNonBreaking = 0;\n\n for (const specPath of specFiles) {\n try {\n // Read current spec\n const currentContent = await fs.readFile(specPath);\n const currentMeta = extractSpecMeta(currentContent);\n\n if (!currentMeta) {\n continue; // Skip files that don't have valid spec meta\n }\n\n // Get baseline content if baseline specified\n let baselineContent: string | null = null;\n if (options.baseline) {\n try {\n baselineContent = await git.showFile(options.baseline, specPath);\n } catch {\n // File doesn't exist in baseline (new file)\n baselineContent = null;\n }\n }\n\n // Analyze changes\n const changes = analyzeSpecChanges(currentContent, baselineContent);\n const hasBreaking = changes.some((c) => c.type === 'breaking');\n const hasNonBreaking = changes.length > 0 && !hasBreaking;\n const needsBump = changes.length > 0;\n\n if (hasBreaking) totalBreaking++;\n if (hasNonBreaking) totalNonBreaking++;\n\n const bumpType = determineBumpType(hasBreaking, hasNonBreaking);\n const suggestedVersion = needsBump\n ? bumpVersion(currentMeta.version, bumpType)\n : currentMeta.version;\n\n analyses.push({\n specPath,\n specKey: currentMeta.key,\n currentVersion: currentMeta.version,\n suggestedVersion,\n bumpType,\n changes,\n hasBreaking,\n needsBump,\n });\n } catch (error) {\n logger.warn(`Failed to analyze ${specPath}:`, {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n const specsNeedingBump = analyses.filter((a) => a.needsBump).length;\n\n logger.info('Version analysis complete', {\n totalSpecs: analyses.length,\n specsNeedingBump,\n totalBreaking,\n totalNonBreaking,\n });\n\n return {\n analyses,\n totalSpecs: analyses.length,\n specsNeedingBump,\n totalBreaking,\n totalNonBreaking,\n baseline: options.baseline,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Version Bump\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Apply a version bump to a spec file.\n *\n * Updates the version in the spec file and creates a changelog entry.\n */\nexport async function applyVersionBump(\n adapters: ServiceAdapters,\n options: VersionBumpOptions\n): Promise<VersionBumpResult> {\n const { fs, logger } = adapters;\n const { specPath, dryRun = false } = options;\n\n logger.info('Applying version bump...', {\n specPath,\n bumpType: options.bumpType,\n });\n\n try {\n // Read current spec\n const content = await fs.readFile(specPath);\n const meta = extractSpecMeta(content);\n\n if (!meta) {\n return {\n success: false,\n specPath,\n specKey: 'unknown',\n previousVersion: 'unknown',\n newVersion: 'unknown',\n bumpType: 'patch',\n changelogEntry: createEmptyChangelogEntry(),\n error: 'Could not extract spec metadata',\n };\n }\n\n // Determine bump type\n const bumpType = options.bumpType ?? 'patch';\n const newVersion = bumpVersion(meta.version, bumpType);\n\n // Create changelog entry\n const changes: ChangeEntry[] = options.changes ?? [];\n if (options.changeDescription) {\n changes.push({\n type: bumpType === 'major' ? 'breaking' : 'changed',\n description: options.changeDescription,\n });\n }\n\n const changelogEntry: ChangelogEntry = {\n version: newVersion,\n date: new Date().toISOString().split('T')[0] ?? '',\n bumpType,\n changes,\n breakingChanges: changes.filter((c) => c.type === 'breaking'),\n };\n\n // Update the spec file\n if (!dryRun) {\n const updatedContent = updateSpecVersion(\n content,\n meta.version,\n newVersion\n );\n await fs.writeFile(specPath, updatedContent);\n }\n\n logger.info('Version bump applied', {\n specPath,\n previousVersion: meta.version,\n newVersion,\n });\n\n return {\n success: true,\n specPath,\n specKey: meta.key,\n previousVersion: meta.version,\n newVersion,\n bumpType,\n changelogEntry,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error('Version bump failed', { specPath, error: errorMessage });\n\n return {\n success: false,\n specPath,\n specKey: 'unknown',\n previousVersion: 'unknown',\n newVersion: 'unknown',\n bumpType: options.bumpType ?? 'patch',\n changelogEntry: createEmptyChangelogEntry(),\n error: errorMessage,\n };\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Changelog Generation\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Generate changelogs at all configured tiers.\n */\nexport async function generateChangelogs(\n adapters: ServiceAdapters,\n options: ChangelogGenerateOptions = {}\n): Promise<ChangelogGenerateResult> {\n const { logger } = adapters;\n const tiers = options.tiers ?? ['spec', 'library', 'monorepo'];\n const format = options.format ?? 'keep-a-changelog';\n\n logger.info('Generating changelogs...', { tiers, format });\n\n // First, analyze versions to get changes\n const analysis = await analyzeVersions(adapters, {\n baseline: options.baseline,\n workspaceRoot: options.workspaceRoot,\n });\n\n const specChangelogs: ChangelogDocBlock[] = [];\n const libraryMarkdown = new Map<string, string>();\n let monorepoMarkdown = '';\n let totalEntries = 0;\n\n // Generate per-spec changelogs\n if (tiers.includes('spec')) {\n for (const spec of analysis.analyses.filter((a) => a.needsBump)) {\n const docBlock: ChangelogDocBlock = {\n id: `changelog.${spec.specKey}`,\n title: `Changelog for ${spec.specKey}`,\n body: formatSpecChangelog(spec),\n kind: 'changelog',\n specKey: spec.specKey,\n specVersion: spec.suggestedVersion,\n entries: [\n {\n version: spec.suggestedVersion,\n date: new Date().toISOString().split('T')[0] ?? '',\n bumpType: spec.bumpType,\n changes: spec.changes,\n breakingChanges: spec.changes.filter((c) => c.type === 'breaking'),\n },\n ],\n };\n specChangelogs.push(docBlock);\n totalEntries++;\n }\n }\n\n // Generate library-level changelogs\n if (tiers.includes('library')) {\n const libraryGroups = groupByLibrary(analysis.analyses);\n for (const [libPath, specs] of libraryGroups) {\n const entries = specs.filter((s) => s.needsBump);\n if (entries.length > 0) {\n libraryMarkdown.set(\n libPath,\n formatKeepAChangelog(entries.map(specToChangelogEntry))\n );\n totalEntries += entries.length;\n }\n }\n }\n\n // Generate monorepo-level changelog\n if (tiers.includes('monorepo')) {\n const allEntries = analysis.analyses\n .filter((a) => a.needsBump)\n .map(specToChangelogEntry);\n monorepoMarkdown = formatKeepAChangelog(allEntries);\n }\n\n // Generate JSON export\n const json = formatChangelogJson(analysis, options.baseline);\n\n logger.info('Changelog generation complete', { totalEntries });\n\n return {\n specChangelogs,\n libraryMarkdown,\n monorepoMarkdown,\n json,\n totalEntries,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface SpecMeta {\n key: string;\n version: string;\n}\n\n/**\n * Extract spec metadata from file content.\n */\nfunction extractSpecMeta(content: string): SpecMeta | null {\n // Match meta.key and meta.version patterns\n const keyMatch = content.match(/key:\\s*['\"`]([^'\"`]+)['\"`]/);\n const versionMatch = content.match(/version:\\s*['\"`]([^'\"`]+)['\"`]/);\n\n if (!keyMatch || !versionMatch) {\n return null;\n }\n\n const key = keyMatch[1];\n const version = versionMatch[1];\n\n // Type guard for regex capture groups\n if (!key || !version) {\n return null;\n }\n\n return {\n key,\n version,\n };\n}\n\n/**\n * Analyze changes between current and baseline spec content.\n */\nfunction analyzeSpecChanges(\n current: string,\n baseline: string | null\n): ChangeEntry[] {\n const changes: ChangeEntry[] = [];\n\n if (!baseline) {\n // New spec\n changes.push({\n type: 'added',\n description: 'New spec added',\n });\n return changes;\n }\n\n // Simple diff analysis - check for structural changes\n const currentMeta = extractSpecMeta(current);\n const baselineMeta = extractSpecMeta(baseline);\n\n if (!currentMeta || !baselineMeta) {\n return changes;\n }\n\n // Version change\n if (currentMeta.version !== baselineMeta.version) {\n changes.push({\n type: 'changed',\n description: `Version updated from ${baselineMeta.version} to ${currentMeta.version}`,\n path: 'meta.version',\n });\n }\n\n // Check for io changes (simplified)\n const hasIoChanges = detectIoChanges(current, baseline);\n if (hasIoChanges.breaking) {\n changes.push({\n type: 'breaking',\n description: 'Breaking changes to input/output schema',\n path: 'io',\n });\n } else if (hasIoChanges.nonBreaking) {\n changes.push({\n type: 'changed',\n description: 'Non-breaking changes to input/output schema',\n path: 'io',\n });\n }\n\n return changes;\n}\n\n/**\n * Detect I/O changes between specs (simplified).\n */\nfunction detectIoChanges(\n current: string,\n baseline: string\n): { breaking: boolean; nonBreaking: boolean } {\n // Simple heuristic: check if io section differs\n const ioPattern = /io:\\s*\\{[\\s\\S]*?\\}/;\n const currentIo = current.match(ioPattern)?.[0] ?? '';\n const baselineIo = baseline.match(ioPattern)?.[0] ?? '';\n\n if (currentIo === baselineIo) {\n return { breaking: false, nonBreaking: false };\n }\n\n // Check for removed fields (breaking) vs added fields (non-breaking)\n const currentFields = extractFields(currentIo);\n const baselineFields = extractFields(baselineIo);\n\n const removedFields = baselineFields.filter(\n (f) => !currentFields.includes(f)\n );\n const addedFields = currentFields.filter((f) => !baselineFields.includes(f));\n\n return {\n breaking: removedFields.length > 0,\n nonBreaking: addedFields.length > 0 && removedFields.length === 0,\n };\n}\n\n/**\n * Extract field names from a code block (simplified).\n */\nfunction extractFields(code: string): string[] {\n const fieldPattern = /(\\w+):/g;\n const matches = [...code.matchAll(fieldPattern)];\n return matches.map((m) => m[1]).filter((field): field is string => !!field);\n}\n\n/**\n * Update version in spec content.\n */\nfunction updateSpecVersion(\n content: string,\n oldVersion: string,\n newVersion: string\n): string {\n return content.replace(\n new RegExp(`version:\\\\s*['\"\\`]${oldVersion}['\"\\`]`),\n `version: '${newVersion}'`\n );\n}\n\n/**\n * Create an empty changelog entry.\n */\nfunction createEmptyChangelogEntry(): ChangelogEntry {\n return {\n version: '0.0.0',\n date: new Date().toISOString().split('T')[0] ?? '',\n bumpType: 'patch',\n changes: [],\n };\n}\n\n/**\n * Format a single spec's changelog.\n */\nfunction formatSpecChangelog(spec: SpecVersionAnalysis): string {\n const lines: string[] = [\n `## [${spec.suggestedVersion}] - ${new Date().toISOString().split('T')[0]}`,\n '',\n ];\n\n const breaking = spec.changes.filter((c) => c.type === 'breaking');\n const changed = spec.changes.filter((c) => c.type === 'changed');\n const added = spec.changes.filter((c) => c.type === 'added');\n\n if (breaking.length > 0) {\n lines.push('### Breaking Changes');\n breaking.forEach((c) => lines.push(`- ${c.description}`));\n lines.push('');\n }\n\n if (added.length > 0) {\n lines.push('### Added');\n added.forEach((c) => lines.push(`- ${c.description}`));\n lines.push('');\n }\n\n if (changed.length > 0) {\n lines.push('### Changed');\n changed.forEach((c) => lines.push(`- ${c.description}`));\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Group specs by library (package path).\n */\nfunction groupByLibrary(\n analyses: SpecVersionAnalysis[]\n): Map<string, SpecVersionAnalysis[]> {\n const groups = new Map<string, SpecVersionAnalysis[]>();\n\n for (const analysis of analyses) {\n // Extract library path (up to src/)\n const libMatch = analysis.specPath.match(/(.+?\\/src\\/)/);\n const matchedPath = libMatch?.[1];\n const libPath = matchedPath ?? analysis.specPath;\n\n const existing = groups.get(libPath) ?? [];\n existing.push(analysis);\n groups.set(libPath, existing);\n }\n\n return groups;\n}\n\n/**\n * Convert SpecVersionAnalysis to ChangelogEntry.\n */\nfunction specToChangelogEntry(spec: SpecVersionAnalysis): ChangelogEntry {\n return {\n version: spec.suggestedVersion,\n date: new Date().toISOString().split('T')[0] ?? '',\n bumpType: spec.bumpType,\n changes: spec.changes,\n breakingChanges: spec.changes.filter((c) => c.type === 'breaking'),\n };\n}\n"],"mappings":"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"}
|
|
@@ -40,24 +40,24 @@ function buildMappingSection(prop, mappings) {
|
|
|
40
40
|
if (mappings.length === 0) return "";
|
|
41
41
|
return ` ${prop}: {\n${mappings.map((mapping) => ` ${mapping.slot}: {
|
|
42
42
|
name: '${escapeString(mapping.name)}',
|
|
43
|
-
${
|
|
43
|
+
${mapping.version !== void 0 ? `version: '${mapping.version}',` : ""}
|
|
44
44
|
}`).join(",\n")}\n },\n`;
|
|
45
45
|
}
|
|
46
46
|
function buildPolicySection(data) {
|
|
47
47
|
if (data.policyRefs.length === 0) return "";
|
|
48
48
|
return ` policies: [\n${data.policyRefs.map((policy) => ` {
|
|
49
|
-
name: '${escapeString(policy.name)}'${
|
|
49
|
+
name: '${escapeString(policy.name)}'${policy.version !== void 0 ? `,\n version: '${policy.version}'` : ""}
|
|
50
50
|
}`).join(",\n")}\n ],\n`;
|
|
51
51
|
}
|
|
52
52
|
function buildThemeSection(data) {
|
|
53
53
|
if (!data.theme) return "";
|
|
54
|
-
return ` theme: {\n${` primary: { name: '${escapeString(data.theme.name)}', version: ${data.theme.version} },\n`}${data.themeFallbacks.length > 0 ? ` fallbacks: [${data.themeFallbacks.map((theme) => `{ name: '${escapeString(theme.name)}', version: ${theme.version} }`).join(", ")}],\n` : ""} },\n`;
|
|
54
|
+
return ` theme: {\n${` primary: { name: '${escapeString(data.theme.name)}', version: '${data.theme.version}' },\n`}${data.themeFallbacks.length > 0 ? ` fallbacks: [${data.themeFallbacks.map((theme) => `{ name: '${escapeString(theme.name)}', version: '${theme.version}' }`).join(", ")}],\n` : ""} },\n`;
|
|
55
55
|
}
|
|
56
56
|
function buildTelemetrySection(data) {
|
|
57
57
|
if (!data.telemetry) return "";
|
|
58
58
|
return ` telemetry: {
|
|
59
59
|
spec: {
|
|
60
|
-
name: '${escapeString(data.telemetry.name)}'${
|
|
60
|
+
name: '${escapeString(data.telemetry.name)}'${data.telemetry.version !== void 0 ? `\n version: '${data.telemetry.version}'` : ""}
|
|
61
61
|
},
|
|
62
62
|
},\n`;
|
|
63
63
|
}
|
|
@@ -82,9 +82,9 @@ function buildRoutesSection(data) {
|
|
|
82
82
|
route.label ? `label: '${escapeString(route.label)}'` : null,
|
|
83
83
|
route.dataView ? `dataView: '${escapeString(route.dataView)}'` : null,
|
|
84
84
|
route.workflow ? `workflow: '${escapeString(route.workflow)}'` : null,
|
|
85
|
-
route.guardName ? `guard: { name: '${escapeString(route.guardName)}'${
|
|
85
|
+
route.guardName ? `guard: { name: '${escapeString(route.guardName)}'${route.guardVersion !== void 0 ? `, version: '${route.guardVersion}'` : ""} }` : null,
|
|
86
86
|
route.featureFlag ? `featureFlag: '${escapeString(route.featureFlag)}'` : null,
|
|
87
|
-
route.experimentName ? `experiment: { name: '${escapeString(route.experimentName)}'${
|
|
87
|
+
route.experimentName ? `experiment: { name: '${escapeString(route.experimentName)}'${route.experimentVersion !== void 0 ? `, version: '${route.experimentVersion}'` : ""} }` : null
|
|
88
88
|
].filter(Boolean).join(", ")} }`;
|
|
89
89
|
}).join(",\n")}\n ],\n`;
|
|
90
90
|
}
|
|
@@ -92,7 +92,7 @@ function formatCapabilityRef(key) {
|
|
|
92
92
|
return `{ key: '${escapeString(key)}' }`;
|
|
93
93
|
}
|
|
94
94
|
function formatExperimentRef(exp) {
|
|
95
|
-
const version =
|
|
95
|
+
const version = exp.version !== void 0 ? `, version: '${exp.version}'` : "";
|
|
96
96
|
return `{ name: '${escapeString(exp.name)}'${version} }`;
|
|
97
97
|
}
|
|
98
98
|
function toPascalCase(value) {
|