feat(core): Add Cargo and Composer registries with storage, auth and helpers
This commit is contained in:
139
ts/composer/helpers.composer.ts
Normal file
139
ts/composer/helpers.composer.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Composer Registry Helper Functions
|
||||
*/
|
||||
|
||||
import type { IComposerPackage } from './interfaces.composer.js';
|
||||
|
||||
/**
|
||||
* Normalize version string to Composer format
|
||||
* Example: "1.0.0" -> "1.0.0.0", "v2.3.1" -> "2.3.1.0"
|
||||
*/
|
||||
export function normalizeVersion(version: string): string {
|
||||
// Remove 'v' prefix if present
|
||||
let normalized = version.replace(/^v/i, '');
|
||||
|
||||
// Handle special versions (dev, alpha, beta, rc)
|
||||
if (normalized.includes('dev') || normalized.includes('alpha') || normalized.includes('beta') || normalized.includes('RC')) {
|
||||
// For dev versions, just return as-is with .0 appended if needed
|
||||
const parts = normalized.split(/[-+]/)[0].split('.');
|
||||
while (parts.length < 4) {
|
||||
parts.push('0');
|
||||
}
|
||||
return parts.slice(0, 4).join('.');
|
||||
}
|
||||
|
||||
// Split by dots
|
||||
const parts = normalized.split('.');
|
||||
|
||||
// Ensure 4 parts (major.minor.patch.build)
|
||||
while (parts.length < 4) {
|
||||
parts.push('0');
|
||||
}
|
||||
|
||||
return parts.slice(0, 4).join('.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate composer.json structure
|
||||
*/
|
||||
export function validateComposerJson(composerJson: any): boolean {
|
||||
return !!(
|
||||
composerJson &&
|
||||
typeof composerJson.name === 'string' &&
|
||||
composerJson.name.includes('/') &&
|
||||
(composerJson.version || composerJson.require)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract composer.json from ZIP buffer
|
||||
*/
|
||||
export async function extractComposerJsonFromZip(zipBuffer: Buffer): Promise<any | null> {
|
||||
try {
|
||||
const AdmZip = (await import('adm-zip')).default;
|
||||
const zip = new AdmZip(zipBuffer);
|
||||
const entries = zip.getEntries();
|
||||
|
||||
// Look for composer.json in root or first-level directory
|
||||
for (const entry of entries) {
|
||||
if (entry.entryName.endsWith('composer.json')) {
|
||||
const parts = entry.entryName.split('/');
|
||||
if (parts.length <= 2) { // Root or first-level dir
|
||||
const content = entry.getData().toString('utf-8');
|
||||
return JSON.parse(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate SHA-1 hash for ZIP file
|
||||
*/
|
||||
export async function calculateSha1(data: Buffer): Promise<string> {
|
||||
const crypto = await import('crypto');
|
||||
return crypto.createHash('sha1').update(data).digest('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse vendor/package format
|
||||
*/
|
||||
export function parseVendorPackage(name: string): { vendor: string; package: string } | null {
|
||||
const parts = name.split('/');
|
||||
if (parts.length !== 2) {
|
||||
return null;
|
||||
}
|
||||
return { vendor: parts[0], package: parts[1] };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate packages.json root repository file
|
||||
*/
|
||||
export function generatePackagesJson(
|
||||
registryUrl: string,
|
||||
availablePackages: string[]
|
||||
): any {
|
||||
return {
|
||||
'metadata-url': `${registryUrl}/p2/%package%.json`,
|
||||
'available-packages': availablePackages,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort versions in semantic version order
|
||||
*/
|
||||
export function sortVersions(versions: string[]): string[] {
|
||||
return versions.sort((a, b) => {
|
||||
const aParts = a.replace(/^v/i, '').split(/[.-]/).map(part => {
|
||||
const num = parseInt(part, 10);
|
||||
return isNaN(num) ? part : num;
|
||||
});
|
||||
const bParts = b.replace(/^v/i, '').split(/[.-]/).map(part => {
|
||||
const num = parseInt(part, 10);
|
||||
return isNaN(num) ? part : num;
|
||||
});
|
||||
|
||||
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
|
||||
const aPart = aParts[i] ?? 0;
|
||||
const bPart = bParts[i] ?? 0;
|
||||
|
||||
// Compare numbers numerically, strings lexicographically
|
||||
if (typeof aPart === 'number' && typeof bPart === 'number') {
|
||||
if (aPart !== bPart) {
|
||||
return aPart - bPart;
|
||||
}
|
||||
} else {
|
||||
const aStr = String(aPart);
|
||||
const bStr = String(bPart);
|
||||
if (aStr !== bStr) {
|
||||
return aStr.localeCompare(bStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user