@3fn/core 14.0.0 → 14.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/.kiro/steering/Civitas-System-Overview.md +3 -12
  2. package/.kiro/steering/Spec-Feedback-Protocol.md +2 -11
  3. package/.kiro/steering/Task-Completion-Protocol.md +7 -7
  4. package/.kiro/steering/start-up-tasks.md +4 -4
  5. package/dist/ComponentTokens.android.kt +12 -12
  6. package/dist/ComponentTokens.ios.swift +12 -12
  7. package/dist/ComponentTokens.web.css +3 -3
  8. package/dist/DesignTokens.android.kt +1 -1
  9. package/dist/DesignTokens.dtcg.json +2 -2
  10. package/dist/DesignTokens.ios.swift +1 -1
  11. package/dist/DesignTokens.web.css +1 -1
  12. package/dist/android/DesignTokens.android.kt +1 -1
  13. package/dist/browser/designerpunk.esm.js +14 -9
  14. package/dist/browser/designerpunk.esm.min.js +7 -7
  15. package/dist/browser/designerpunk.umd.js +14 -9
  16. package/dist/browser/designerpunk.umd.min.js +12 -12
  17. package/dist/browser/tokens.css +3 -3
  18. package/dist/build/tokens/defineComponentTokens.d.ts +10 -0
  19. package/dist/build/tokens/defineComponentTokens.js +26 -0
  20. package/dist/components/core/Avatar-Base/avatar.tokens.d.ts +21 -26
  21. package/dist/components/core/Avatar-Base/avatar.tokens.js +31 -34
  22. package/dist/components/core/Avatar-Base/index.d.ts +1 -1
  23. package/dist/components/core/Avatar-Base/index.js +2 -2
  24. package/dist/components/core/Button-Icon/buttonIcon.tokens.d.ts +28 -14
  25. package/dist/components/core/Button-Icon/buttonIcon.tokens.js +35 -20
  26. package/dist/generators/TokenFileGenerator.js +7 -2
  27. package/dist/ios/DesignTokens.ios.swift +1 -1
  28. package/dist/tokens/component/progress.d.ts +65 -5
  29. package/dist/tokens/component/progress.js +79 -18
  30. package/dist/types/generated/TokenTypes.d.ts +1 -1
  31. package/dist/types/generated/TokenTypes.js +1 -1
  32. package/dist/web/DesignTokens.web.css +1 -1
  33. package/governance/BUILD-SYSTEM-SETUP.md +1 -2
  34. package/governance/Component-Development-Guide.md +1 -1
  35. package/governance/Component-Development-Standards.md +19 -18
  36. package/governance/Component-Family-Avatar.md +6 -6
  37. package/governance/Component-Family-Badge.md +19 -19
  38. package/governance/Component-Family-Button.md +26 -26
  39. package/governance/Component-Family-Chip.md +14 -14
  40. package/governance/Component-Family-Container.md +12 -12
  41. package/governance/Component-Family-Form-Inputs.md +61 -61
  42. package/governance/Component-Family-Icon.md +8 -8
  43. package/governance/Component-Family-Navigation.md +1 -1
  44. package/governance/Component-Inheritance-Structures.md +29 -28
  45. package/governance/Component-Readiness-Status.md +6 -7
  46. package/governance/Component-Templates.md +26 -26
  47. package/governance/Contract-System-Reference.md +1 -1
  48. package/governance/Process-Development-Workflow.md +4 -4
  49. package/governance/Process-File-Organization.md +1 -4
  50. package/governance/Process-Hook-Operations.md +6 -7
  51. package/governance/Process-Spec-Planning.md +8 -14
  52. package/governance/Rosetta-System-Architecture.md +7 -5
  53. package/governance/Token-Quick-Reference.md +35 -22
  54. package/governance/Web-Authoring-Standards.md +1 -1
  55. package/governance/classification-map.md +109 -3
  56. package/governance/completion-documentation-guide.md +14 -31
  57. package/governance/platform-implementation-guidelines.md +1 -2
  58. package/governance/release-management-system.md +28 -63
  59. package/package.json +2 -6
  60. package/src/build/tokens/__tests__/defineComponentTokens.test.ts +113 -0
  61. package/src/build/tokens/defineComponentTokens.ts +43 -1
  62. package/src/components/core/Avatar-Base/avatar.tokens.ts +31 -34
  63. package/src/components/core/Avatar-Base/index.ts +1 -1
  64. package/src/components/core/Button-Icon/buttonIcon.tokens.ts +43 -27
  65. package/src/generators/TokenFileGenerator.ts +7 -2
  66. package/src/tokens/__tests__/ProgressTokenCompliance.test.ts +5 -3
  67. package/src/tokens/__tests__/ProgressTokenFormulas.test.ts +11 -11
  68. package/src/tokens/__tests__/ProgressTokenTranslation.test.ts +22 -20
  69. package/src/tokens/component/progress.ts +83 -21
  70. package/src/types/generated/TokenTypes.ts +1 -1
  71. package/token-index/components.yaml +8 -8
  72. package/src/tools/release/__tests__/ChangeClassifier.test.ts +0 -133
  73. package/src/tools/release/__tests__/ChangeExtractor.test.ts +0 -222
  74. package/src/tools/release/__tests__/GitHubPublisher.test.ts +0 -240
  75. package/src/tools/release/__tests__/NotesRenderer.test.ts +0 -142
  76. package/src/tools/release/__tests__/NpmPublisher.test.ts +0 -289
  77. package/src/tools/release/__tests__/PipelineIntegration.test.ts +0 -188
  78. package/src/tools/release/__tests__/ReleasePipeline.test.ts +0 -192
  79. package/src/tools/release/__tests__/SemanticVersionValidator.test.ts +0 -49
  80. package/src/tools/release/__tests__/SummaryScanner.test.ts +0 -141
  81. package/src/tools/release/__tests__/TagResolver.test.ts +0 -91
  82. package/src/tools/release/__tests__/VersionCalculator.test.ts +0 -270
  83. package/src/tools/release/__tests__/helpers/NpmMockHelper.ts +0 -80
  84. package/src/tools/release/cli/ReleasePipeline.ts +0 -165
  85. package/src/tools/release/cli/release-tool.ts +0 -107
  86. package/src/tools/release/pipeline/ChangeClassifier.ts +0 -61
  87. package/src/tools/release/pipeline/ChangeExtractor.ts +0 -87
  88. package/src/tools/release/pipeline/NotesRenderer.ts +0 -66
  89. package/src/tools/release/pipeline/SummaryScanner.ts +0 -70
  90. package/src/tools/release/pipeline/TagResolver.ts +0 -40
  91. package/src/tools/release/pipeline/VersionCalculator.ts +0 -375
  92. package/src/tools/release/publishers/GitHubPublisher.ts +0 -228
  93. package/src/tools/release/publishers/NpmPublisher.ts +0 -196
  94. package/src/tools/release/release-config.json +0 -5
  95. package/src/tools/release/types/index.ts +0 -282
  96. package/src/tools/release/validators/SemanticVersionValidator.ts +0 -67
@@ -1,375 +0,0 @@
1
- /**
2
- * Version Calculator for Release Tool
3
- *
4
- * Semantic version calculation based on extracted changes.
5
- * Extracted from src/release-analysis/versioning/VersionCalculator.ts
6
- * and adapted to use local types.
7
- */
8
-
9
- import {
10
- ExtractedChanges,
11
- BreakingChange,
12
- Feature,
13
- BugFix,
14
- Improvement,
15
- VersionRecommendation,
16
- ChangeEvidence,
17
- PreReleaseInfo,
18
- ValidationResult,
19
- ParsedVersion,
20
- } from '../types';
21
-
22
- export class VersionCalculator {
23
- calculateVersionBump(changes: ExtractedChanges, currentVersion: string): VersionRecommendation {
24
- const parsedVersion = this.parseVersion(currentVersion);
25
- const evidence = this.generateEvidence(changes);
26
- const bumpType = this.determineBumpType(changes);
27
- const recommendedVersion = this.calculateNewVersion(parsedVersion, bumpType);
28
- const rationale = this.generateVersionRationale(changes);
29
- const confidence = this.calculateConfidence(changes, bumpType);
30
-
31
- return {
32
- currentVersion,
33
- recommendedVersion,
34
- bumpType,
35
- rationale,
36
- confidence,
37
- evidence,
38
- preReleaseInfo: this.analyzePreReleaseInfo(parsedVersion, bumpType),
39
- };
40
- }
41
-
42
- validateSemanticVersioning(recommendation: VersionRecommendation): ValidationResult {
43
- const errors: string[] = [];
44
- const warnings: string[] = [];
45
-
46
- if (!this.isValidSemanticVersion(recommendation.currentVersion)) {
47
- errors.push(`Current version "${recommendation.currentVersion}" is not a valid semantic version`);
48
- }
49
- if (!this.isValidSemanticVersion(recommendation.recommendedVersion)) {
50
- errors.push(`Recommended version "${recommendation.recommendedVersion}" is not a valid semantic version`);
51
- }
52
- if (errors.length > 0) {
53
- return { valid: false, errors, warnings };
54
- }
55
-
56
- const currentParsed = this.parseVersion(recommendation.currentVersion);
57
- const recommendedParsed = this.parseVersion(recommendation.recommendedVersion);
58
-
59
- if (!this.isValidVersionProgression(currentParsed, recommendedParsed, recommendation.bumpType)) {
60
- errors.push(
61
- `Version progression from ${recommendation.currentVersion} to ${recommendation.recommendedVersion} is invalid for ${recommendation.bumpType} bump`,
62
- );
63
- }
64
-
65
- const hasBreaking = recommendation.evidence.some((e) => e.type === 'breaking');
66
- const hasFeatures = recommendation.evidence.some((e) => e.type === 'feature');
67
- const hasFixes = recommendation.evidence.some((e) => e.type === 'fix' || e.type === 'improvement');
68
-
69
- let expectedBumpType: 'major' | 'minor' | 'patch' | 'none' = 'none';
70
- if (hasBreaking) expectedBumpType = 'major';
71
- else if (hasFeatures) expectedBumpType = 'minor';
72
- else if (hasFixes) expectedBumpType = 'patch';
73
-
74
- if (recommendation.bumpType !== expectedBumpType && expectedBumpType !== 'none') {
75
- warnings.push(
76
- `Recommended bump type "${recommendation.bumpType}" may not match evidence (expected "${expectedBumpType}")`,
77
- );
78
- }
79
-
80
- if (recommendation.confidence < 0.7) {
81
- warnings.push(`Low confidence score (${recommendation.confidence.toFixed(2)}) - manual review recommended`);
82
- }
83
-
84
- return { valid: errors.length === 0, errors, warnings };
85
- }
86
-
87
- generateVersionRationale(changes: ExtractedChanges): string {
88
- const rationale: string[] = [];
89
-
90
- if (changes.breakingChanges.length > 0) {
91
- rationale.push(`Major version bump required due to ${changes.breakingChanges.length} breaking change(s):`);
92
- changes.breakingChanges.slice(0, 3).forEach((c) => rationale.push(` • ${c.title} (${c.severity} severity)`));
93
- if (changes.breakingChanges.length > 3)
94
- rationale.push(` • ... and ${changes.breakingChanges.length - 3} more breaking changes`);
95
- } else if (changes.newFeatures.length > 0) {
96
- rationale.push(`Minor version bump recommended due to ${changes.newFeatures.length} new feature(s):`);
97
- changes.newFeatures.slice(0, 3).forEach((f) => rationale.push(` • ${f.title}`));
98
- if (changes.newFeatures.length > 3)
99
- rationale.push(` • ... and ${changes.newFeatures.length - 3} more features`);
100
- } else if (changes.bugFixes.length > 0 || changes.improvements.length > 0) {
101
- const total = changes.bugFixes.length + changes.improvements.length;
102
- rationale.push(`Patch version bump recommended due to ${total} fix(es) and improvement(s):`);
103
- changes.bugFixes.slice(0, 2).forEach((f) => rationale.push(` • ${f.title} (bug fix)`));
104
- changes.improvements.slice(0, 2).forEach((i) => rationale.push(` • ${i.title} (${i.type})`));
105
- if (total > 4) rationale.push(` • ... and ${total - 4} more fixes/improvements`);
106
- } else {
107
- rationale.push('No version bump required - only documentation or non-functional changes detected');
108
- }
109
-
110
- return rationale.join('\n');
111
- }
112
-
113
- handlePreReleaseVersions(currentVersion: string, bumpType: 'major' | 'minor' | 'patch' | 'none'): string {
114
- const parsed = this.parseVersion(currentVersion);
115
-
116
- if (parsed.preRelease) {
117
- const match = parsed.preRelease.match(/^(alpha|beta|rc)\.?(\d+)?$/);
118
- if (match) {
119
- const [, type, num] = match;
120
- const nextNum = num ? parseInt(num) + 1 : 1;
121
- return `${parsed.major}.${parsed.minor}.${parsed.patch}-${type}.${nextNum}`;
122
- }
123
- }
124
-
125
- return this.calculateNewVersion(parsed, bumpType);
126
- }
127
-
128
- generatePreReleaseVersion(
129
- baseVersion: string,
130
- preReleaseType: 'alpha' | 'beta' | 'rc',
131
- preReleaseNumber: number = 1,
132
- ): string {
133
- const parsed = this.parseVersion(baseVersion);
134
- return `${parsed.major}.${parsed.minor}.${parsed.patch}-${preReleaseType}.${preReleaseNumber}`;
135
- }
136
-
137
- progressPreReleaseStage(currentVersion: string): string {
138
- const parsed = this.parseVersion(currentVersion);
139
- if (!parsed.preRelease) throw new Error(`Version ${currentVersion} is not a pre-release version`);
140
-
141
- const match = parsed.preRelease.match(/^(alpha|beta|rc)\.?(\d+)?$/);
142
- if (!match) throw new Error(`Invalid pre-release format: ${parsed.preRelease}`);
143
-
144
- const base = `${parsed.major}.${parsed.minor}.${parsed.patch}`;
145
- switch (match[1]) {
146
- case 'alpha': return `${base}-beta.1`;
147
- case 'beta': return `${base}-rc.1`;
148
- case 'rc': return base;
149
- default: throw new Error(`Unknown pre-release type: ${match[1]}`);
150
- }
151
- }
152
-
153
- incrementPreReleaseNumber(currentVersion: string): string {
154
- const parsed = this.parseVersion(currentVersion);
155
- if (!parsed.preRelease) throw new Error(`Version ${currentVersion} is not a pre-release version`);
156
-
157
- const match = parsed.preRelease.match(/^(alpha|beta|rc)\.?(\d+)?$/);
158
- if (!match) throw new Error(`Invalid pre-release format: ${parsed.preRelease}`);
159
-
160
- const [, type, num] = match;
161
- return `${parsed.major}.${parsed.minor}.${parsed.patch}-${type}.${num ? parseInt(num) + 1 : 1}`;
162
- }
163
-
164
- promoteToStable(currentVersion: string): string {
165
- const parsed = this.parseVersion(currentVersion);
166
- if (!parsed.preRelease) throw new Error(`Version ${currentVersion} is already a stable version`);
167
- return `${parsed.major}.${parsed.minor}.${parsed.patch}`;
168
- }
169
-
170
- isPreRelease(version: string): boolean {
171
- try {
172
- return this.parseVersion(version).preRelease !== null;
173
- } catch {
174
- return false;
175
- }
176
- }
177
-
178
- getPreReleaseType(version: string): 'alpha' | 'beta' | 'rc' | null {
179
- try {
180
- const parsed = this.parseVersion(version);
181
- if (!parsed.preRelease) return null;
182
- const match = parsed.preRelease.match(/^(alpha|beta|rc)\.?(\d+)?$/);
183
- return match ? (match[1] as 'alpha' | 'beta' | 'rc') : null;
184
- } catch {
185
- return null;
186
- }
187
- }
188
-
189
- getPreReleaseNumber(version: string): number | null {
190
- try {
191
- const parsed = this.parseVersion(version);
192
- if (!parsed.preRelease) return null;
193
- const match = parsed.preRelease.match(/^(alpha|beta|rc)\.?(\d+)?$/);
194
- if (!match) return null;
195
- return match[2] ? parseInt(match[2]) : 0;
196
- } catch {
197
- return null;
198
- }
199
- }
200
-
201
- parseVersion(version: string): ParsedVersion {
202
- const match = version.match(
203
- /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/,
204
- );
205
- if (!match) throw new Error(`Invalid semantic version: ${version}`);
206
-
207
- return {
208
- major: parseInt(match[1]),
209
- minor: parseInt(match[2]),
210
- patch: parseInt(match[3]),
211
- preRelease: match[4] || null,
212
- build: match[5] || null,
213
- raw: version,
214
- };
215
- }
216
-
217
- private determineBumpType(changes: ExtractedChanges): 'major' | 'minor' | 'patch' | 'none' {
218
- if (changes.breakingChanges.length > 0) return 'major';
219
- if (changes.newFeatures.length > 0) return 'minor';
220
- if (changes.bugFixes.length > 0 || changes.improvements.length > 0) return 'patch';
221
- return 'none';
222
- }
223
-
224
- private calculateNewVersion(parsed: ParsedVersion, bumpType: 'major' | 'minor' | 'patch' | 'none'): string {
225
- switch (bumpType) {
226
- case 'major': return `${parsed.major + 1}.0.0`;
227
- case 'minor': return `${parsed.major}.${parsed.minor + 1}.0`;
228
- case 'patch': return `${parsed.major}.${parsed.minor}.${parsed.patch + 1}`;
229
- case 'none': return parsed.raw;
230
- }
231
- }
232
-
233
- private generateEvidence(changes: ExtractedChanges): ChangeEvidence[] {
234
- const evidence: ChangeEvidence[] = [];
235
-
236
- if (changes.breakingChanges.length > 0) {
237
- evidence.push({
238
- type: 'breaking',
239
- description: `${changes.breakingChanges.length} breaking change(s) detected`,
240
- source: 'completion documents',
241
- impact: this.determineBreakingChangeImpact(changes.breakingChanges),
242
- count: changes.breakingChanges.length,
243
- });
244
- }
245
- if (changes.newFeatures.length > 0) {
246
- evidence.push({
247
- type: 'feature',
248
- description: `${changes.newFeatures.length} new feature(s) added`,
249
- source: 'completion documents',
250
- impact: this.determineFeatureImpact(changes.newFeatures),
251
- count: changes.newFeatures.length,
252
- });
253
- }
254
- if (changes.bugFixes.length > 0) {
255
- evidence.push({
256
- type: 'fix',
257
- description: `${changes.bugFixes.length} bug fix(es) implemented`,
258
- source: 'completion documents',
259
- impact: this.determineBugFixImpact(changes.bugFixes),
260
- count: changes.bugFixes.length,
261
- });
262
- }
263
- if (changes.improvements.length > 0) {
264
- evidence.push({
265
- type: 'improvement',
266
- description: `${changes.improvements.length} improvement(s) made`,
267
- source: 'completion documents',
268
- impact: this.determineImprovementImpact(changes.improvements),
269
- count: changes.improvements.length,
270
- });
271
- }
272
-
273
- return evidence;
274
- }
275
-
276
- private calculateConfidence(changes: ExtractedChanges, bumpType: 'major' | 'minor' | 'patch' | 'none'): number {
277
- let confidence = changes.metadata.extractionConfidence;
278
-
279
- if (changes.metadata.ambiguousItems.length > 0) {
280
- confidence -= Math.min(0.2, changes.metadata.ambiguousItems.length * 0.05);
281
- }
282
-
283
- if (bumpType === 'major' && changes.breakingChanges.length > 0) confidence += 0.1;
284
- else if (bumpType === 'minor' && changes.newFeatures.length > 0 && changes.breakingChanges.length === 0)
285
- confidence += 0.1;
286
- else if (
287
- bumpType === 'patch' &&
288
- (changes.bugFixes.length > 0 || changes.improvements.length > 0) &&
289
- changes.breakingChanges.length === 0 &&
290
- changes.newFeatures.length === 0
291
- )
292
- confidence += 0.1;
293
-
294
- return Math.max(0, Math.min(1, confidence));
295
- }
296
-
297
- private analyzePreReleaseInfo(
298
- parsed: ParsedVersion,
299
- bumpType: 'major' | 'minor' | 'patch' | 'none',
300
- ): PreReleaseInfo | undefined {
301
- if (!parsed.preRelease) return undefined;
302
-
303
- const match = parsed.preRelease.match(/^(alpha|beta|rc)\.?(\d+)?$/);
304
- if (!match) return { isPreRelease: true, canPromote: false };
305
-
306
- const [, type, num] = match;
307
- const preReleaseNumber = num ? parseInt(num) : 0;
308
-
309
- return {
310
- isPreRelease: true,
311
- preReleaseType: type as 'alpha' | 'beta' | 'rc',
312
- preReleaseNumber,
313
- canPromote: type === 'rc' && bumpType !== 'none',
314
- nextPreRelease: `${type}.${preReleaseNumber + 1}`,
315
- };
316
- }
317
-
318
- private isValidSemanticVersion(version: string): boolean {
319
- return /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/.test(
320
- version,
321
- );
322
- }
323
-
324
- private isValidVersionProgression(
325
- current: ParsedVersion,
326
- recommended: ParsedVersion,
327
- bumpType: 'major' | 'minor' | 'patch' | 'none',
328
- ): boolean {
329
- switch (bumpType) {
330
- case 'major':
331
- return recommended.major === current.major + 1 && recommended.minor === 0 && recommended.patch === 0;
332
- case 'minor':
333
- return (
334
- recommended.major === current.major && recommended.minor === current.minor + 1 && recommended.patch === 0
335
- );
336
- case 'patch':
337
- return (
338
- recommended.major === current.major &&
339
- recommended.minor === current.minor &&
340
- recommended.patch === current.patch + 1
341
- );
342
- case 'none':
343
- return (
344
- recommended.major === current.major &&
345
- recommended.minor === current.minor &&
346
- recommended.patch === current.patch
347
- );
348
- }
349
- }
350
-
351
- private determineBreakingChangeImpact(breakingChanges: BreakingChange[]): 'high' | 'medium' | 'low' {
352
- if (breakingChanges.some((c) => c.severity === 'critical')) return 'high';
353
- if (breakingChanges.some((c) => c.severity === 'high') || breakingChanges.length > 3) return 'high';
354
- if (breakingChanges.length > 1) return 'medium';
355
- return 'low';
356
- }
357
-
358
- private determineFeatureImpact(features: Feature[]): 'high' | 'medium' | 'low' {
359
- if (features.length > 5) return 'high';
360
- if (features.length > 2) return 'medium';
361
- return 'low';
362
- }
363
-
364
- private determineBugFixImpact(bugFixes: BugFix[]): 'high' | 'medium' | 'low' {
365
- if (bugFixes.some((f) => f.severity === 'critical')) return 'high';
366
- if (bugFixes.some((f) => f.severity === 'high') || bugFixes.length > 5) return 'medium';
367
- return 'low';
368
- }
369
-
370
- private determineImprovementImpact(improvements: Improvement[]): 'high' | 'medium' | 'low' {
371
- if (improvements.some((i) => i.impact === 'high')) return 'high';
372
- if (improvements.length > 3) return 'medium';
373
- return 'low';
374
- }
375
- }
@@ -1,228 +0,0 @@
1
- /**
2
- * GitHub Publisher for Release Tool
3
- *
4
- * Handles GitHub API interactions for creating releases, tags, and uploading artifacts.
5
- * Extracted from src/release/publishing/GitHubPublisher.ts.
6
- */
7
-
8
- import { Octokit } from '@octokit/rest';
9
- import * as fs from 'fs';
10
- import {
11
- GitHubRelease,
12
- GitTag,
13
- Artifact,
14
- ReleaseResult,
15
- TagResult,
16
- UploadResult,
17
- ReleaseError,
18
- RepositoryInfo,
19
- } from '../types';
20
-
21
- export interface GitHubConfig {
22
- token: string;
23
- owner: string;
24
- repo: string;
25
- baseUrl?: string;
26
- timeout?: number;
27
- maxRetries?: number;
28
- retryDelay?: number;
29
- }
30
-
31
- export class GitHubPublisher {
32
- private octokit: Octokit;
33
- private config: GitHubConfig;
34
- private authenticated: boolean = false;
35
-
36
- constructor(config: GitHubConfig) {
37
- this.config = { timeout: 30000, maxRetries: 3, retryDelay: 1000, ...config };
38
- this.octokit = new Octokit({
39
- auth: this.config.token,
40
- baseUrl: this.config.baseUrl,
41
- request: { timeout: this.config.timeout },
42
- });
43
- }
44
-
45
- async validateAuthentication(): Promise<boolean> {
46
- try {
47
- await this.octokit.rest.users.getAuthenticated();
48
- this.authenticated = true;
49
- return true;
50
- } catch (error) {
51
- this.authenticated = false;
52
- throw new Error(`GitHub authentication failed: ${this.getErrorMessage(error)}`);
53
- }
54
- }
55
-
56
- async getRepositoryInfo(): Promise<RepositoryInfo> {
57
- try {
58
- const { data } = await this.octokit.rest.repos.get({
59
- owner: this.config.owner,
60
- repo: this.config.repo,
61
- });
62
- return {
63
- owner: data.owner.login,
64
- name: data.name,
65
- fullName: data.full_name,
66
- defaultBranch: data.default_branch,
67
- private: data.private,
68
- url: data.html_url,
69
- };
70
- } catch (error) {
71
- throw new Error(`Failed to get repository info: ${this.getErrorMessage(error)}`);
72
- }
73
- }
74
-
75
- async releaseExists(tagName: string): Promise<boolean> {
76
- try {
77
- await this.octokit.rest.repos.getReleaseByTag({
78
- owner: this.config.owner,
79
- repo: this.config.repo,
80
- tag: tagName,
81
- });
82
- return true;
83
- } catch (error: any) {
84
- if (error.status === 404) return false;
85
- throw new Error(`Failed to check if release exists: ${this.getErrorMessage(error)}`);
86
- }
87
- }
88
-
89
- async createTags(tags: GitTag[]): Promise<TagResult[]> {
90
- const results: TagResult[] = [];
91
- for (const tag of tags) {
92
- try {
93
- const { data: commit } = await this.octokit.rest.git.getCommit({
94
- owner: this.config.owner, repo: this.config.repo, commit_sha: tag.commit,
95
- });
96
- const { data: tagObject } = await this.octokit.rest.git.createTag({
97
- owner: this.config.owner, repo: this.config.repo,
98
- tag: tag.name, message: tag.message, object: commit.sha, type: 'commit',
99
- });
100
- await this.octokit.rest.git.createRef({
101
- owner: this.config.owner, repo: this.config.repo,
102
- ref: `refs/tags/${tag.name}`, sha: tagObject.sha,
103
- });
104
- results.push({ success: true, tagName: tag.name });
105
- } catch (error) {
106
- results.push({ success: false, tagName: tag.name, error: this.getErrorMessage(error) });
107
- }
108
- }
109
- return results;
110
- }
111
-
112
- async createRelease(release: GitHubRelease): Promise<ReleaseResult> {
113
- const startTime = Date.now();
114
- const errors: ReleaseError[] = [];
115
-
116
- try {
117
- if (!this.authenticated) await this.validateAuthentication();
118
-
119
- const exists = await this.releaseExists(release.tagName);
120
- if (exists) throw new Error(`Release ${release.tagName} already exists`);
121
-
122
- const { data: githubRelease } = await this.octokit.rest.repos.createRelease({
123
- owner: this.config.owner, repo: this.config.repo,
124
- tag_name: release.tagName, name: release.name, body: release.body,
125
- draft: release.draft, prerelease: release.prerelease,
126
- });
127
-
128
- if (release.artifacts?.length > 0) {
129
- const artifactResults = await this.uploadArtifacts(githubRelease.id.toString(), release.artifacts);
130
- artifactResults.forEach((result) => {
131
- if (!result.success && result.error) {
132
- errors.push({
133
- code: 'ARTIFACT_UPLOAD_FAILED',
134
- message: `Failed to upload artifact ${result.artifactName}: ${result.error}`,
135
- severity: 'warning', step: 'artifact-upload',
136
- });
137
- }
138
- });
139
- }
140
-
141
- return {
142
- success: errors.filter((e) => e.severity === 'error').length === 0,
143
- version: release.tagName, releasedPackages: [this.config.repo],
144
- githubReleaseUrl: githubRelease.html_url, npmPackageUrls: [],
145
- duration: Date.now() - startTime, errors, releasedAt: new Date(),
146
- };
147
- } catch (error) {
148
- errors.push({
149
- code: 'RELEASE_CREATION_FAILED', message: this.getErrorMessage(error),
150
- severity: 'error', step: 'release-creation',
151
- stackTrace: error instanceof Error ? error.stack : undefined,
152
- });
153
- return {
154
- success: false, version: release.tagName, releasedPackages: [],
155
- npmPackageUrls: [], duration: Date.now() - startTime, errors, releasedAt: new Date(),
156
- };
157
- }
158
- }
159
-
160
- async uploadArtifacts(releaseId: string, artifacts: Artifact[]): Promise<UploadResult[]> {
161
- const results: UploadResult[] = [];
162
- for (const artifact of artifacts) {
163
- try {
164
- if (!fs.existsSync(artifact.path)) {
165
- results.push({ success: false, artifactName: artifact.name, error: `Artifact file not found: ${artifact.path}` });
166
- continue;
167
- }
168
- const data = fs.readFileSync(artifact.path);
169
- const uploadResult = await this.uploadArtifactWithRetry(parseInt(releaseId), artifact.name, data, artifact.contentType);
170
- results.push({ success: true, artifactName: artifact.name, url: uploadResult.browser_download_url });
171
- } catch (error) {
172
- results.push({ success: false, artifactName: artifact.name, error: this.getErrorMessage(error) });
173
- }
174
- }
175
- return results;
176
- }
177
-
178
- async deleteRelease(tagName: string): Promise<boolean> {
179
- try {
180
- const { data: release } = await this.octokit.rest.repos.getReleaseByTag({
181
- owner: this.config.owner, repo: this.config.repo, tag: tagName,
182
- });
183
- await this.octokit.rest.repos.deleteRelease({
184
- owner: this.config.owner, repo: this.config.repo, release_id: release.id,
185
- });
186
- return true;
187
- } catch (error) {
188
- throw new Error(`Failed to delete release: ${this.getErrorMessage(error)}`);
189
- }
190
- }
191
-
192
- async deleteTag(tagName: string): Promise<boolean> {
193
- try {
194
- await this.octokit.rest.git.deleteRef({
195
- owner: this.config.owner, repo: this.config.repo, ref: `tags/${tagName}`,
196
- });
197
- return true;
198
- } catch (error) {
199
- throw new Error(`Failed to delete tag: ${this.getErrorMessage(error)}`);
200
- }
201
- }
202
-
203
- private async uploadArtifactWithRetry(
204
- releaseId: number, name: string, data: Buffer, contentType: string, attempt: number = 1,
205
- ): Promise<any> {
206
- try {
207
- const { data: asset } = await this.octokit.rest.repos.uploadReleaseAsset({
208
- owner: this.config.owner, repo: this.config.repo, release_id: releaseId,
209
- name, data: data as any,
210
- headers: { 'content-type': contentType, 'content-length': data.length },
211
- });
212
- return asset;
213
- } catch (error) {
214
- if (attempt < (this.config.maxRetries || 3)) {
215
- await new Promise((r) => setTimeout(r, this.config.retryDelay || 1000));
216
- return this.uploadArtifactWithRetry(releaseId, name, data, contentType, attempt + 1);
217
- }
218
- throw error;
219
- }
220
- }
221
-
222
- private getErrorMessage(error: unknown): string {
223
- if (error instanceof Error) return error.message;
224
- if (typeof error === 'string') return error;
225
- if (error && typeof error === 'object' && 'message' in error) return String((error as any).message);
226
- return 'Unknown error occurred';
227
- }
228
- }