import * as plugins from './mod.plugins.js'; export type TAccessLevel = 'public' | 'private'; export interface IReleaseConfig { registries: string[]; accessLevel: TAccessLevel; } /** * Manages release configuration stored in npmextra.json * under @git.zone/cli.release namespace */ export class ReleaseConfig { private cwd: string; private config: IReleaseConfig; constructor(cwd: string = process.cwd()) { this.cwd = cwd; this.config = { registries: [], accessLevel: 'public' }; } /** * Create a ReleaseConfig instance from current working directory */ public static async fromCwd(cwd: string = process.cwd()): Promise { const instance = new ReleaseConfig(cwd); await instance.load(); return instance; } /** * Load configuration from npmextra.json */ public async load(): Promise { const npmextraInstance = new plugins.npmextra.Npmextra(this.cwd); const gitzoneConfig = npmextraInstance.dataFor('@git.zone/cli', {}); // Also check szci for backward compatibility const szciConfig = npmextraInstance.dataFor('@ship.zone/szci', {}); this.config = { registries: gitzoneConfig?.release?.registries || [], accessLevel: gitzoneConfig?.release?.accessLevel || szciConfig?.npmAccessLevel || 'public', }; } /** * Save configuration to npmextra.json */ public async save(): Promise { const npmextraPath = plugins.path.join(this.cwd, 'npmextra.json'); let npmextraData: any = {}; // Read existing npmextra.json if (await plugins.smartfs.file(npmextraPath).exists()) { const content = await plugins.smartfs.file(npmextraPath).encoding('utf8').read(); npmextraData = JSON.parse(content as string); } // Ensure @git.zone/cli namespace exists if (!npmextraData['@git.zone/cli']) { npmextraData['@git.zone/cli'] = {}; } // Ensure release object exists if (!npmextraData['@git.zone/cli'].release) { npmextraData['@git.zone/cli'].release = {}; } // Update registries and accessLevel npmextraData['@git.zone/cli'].release.registries = this.config.registries; npmextraData['@git.zone/cli'].release.accessLevel = this.config.accessLevel; // Write back to file await plugins.smartfs .file(npmextraPath) .encoding('utf8') .write(JSON.stringify(npmextraData, null, 2)); } /** * Get all configured registries */ public getRegistries(): string[] { return [...this.config.registries]; } /** * Check if any registries are configured */ public hasRegistries(): boolean { return this.config.registries.length > 0; } /** * Add a registry URL * @returns true if added, false if already exists */ public addRegistry(url: string): boolean { const normalizedUrl = this.normalizeUrl(url); if (this.config.registries.includes(normalizedUrl)) { return false; } this.config.registries.push(normalizedUrl); return true; } /** * Remove a registry URL * @returns true if removed, false if not found */ public removeRegistry(url: string): boolean { const normalizedUrl = this.normalizeUrl(url); const index = this.config.registries.indexOf(normalizedUrl); if (index === -1) { return false; } this.config.registries.splice(index, 1); return true; } /** * Clear all registries */ public clearRegistries(): void { this.config.registries = []; } /** * Get the npm access level */ public getAccessLevel(): TAccessLevel { return this.config.accessLevel; } /** * Set the npm access level */ public setAccessLevel(level: TAccessLevel): void { this.config.accessLevel = level; } /** * Normalize a registry URL (ensure it has https:// prefix) */ private normalizeUrl(url: string): string { let normalized = url.trim(); // Add https:// if no protocol specified if (!normalized.startsWith('http://') && !normalized.startsWith('https://')) { normalized = `https://${normalized}`; } // Remove trailing slash if (normalized.endsWith('/')) { normalized = normalized.slice(0, -1); } return normalized; } }