/** Build identities and release requirements shared by packaging and tests. */ export const APP_ID = 'com.thetempusproject.capsule' /** Validate a public feed without embedding credentials in installed copies. */ export function validateFeedUrl(value) { let url try { url = new URL(value) } catch { throw new Error('CAPSULE_UPDATE_URL must be an absolute HTTPS directory URL.') } if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash || !url.pathname.endsWith('/') || /^(localhost|127\.|\[?::1\]?)/i.test(url.hostname) || /(^|\.)example\.(com|org|net)$|\.invalid$/i.test(url.hostname)) { throw new Error('Use a permanent public HTTPS feed ending in /, without credentials, query, or fragment.') } return url.href } /** Return a Windows build config; public releases fail closed without signing. */ export function createBuildConfig({ release = false, env = process.env } = {}) { const url = release ? validateFeedUrl(env.CAPSULE_UPDATE_URL) : null const publisher = env.CAPSULE_PUBLISHER_NAME?.trim() if (release && !publisher) { throw new Error('CAPSULE_PUBLISHER_NAME must match the signing certificate subject CN.') } if (release && !env.CSC_LINK && !env.CSC_NAME) { throw new Error('Set CSC_LINK (certificate) or CSC_NAME (Windows certificate store identity).') } return { appId: release ? APP_ID : `${APP_ID}.local`, productName: release ? 'Capsule' : 'Capsule Local', executableName: release ? 'Capsule' : 'Capsule Local', directories: { output: release ? 'dist/release' : 'dist/local' }, files: ['out/**/*', 'package.json'], asar: true, npmRebuild: false, forceCodeSigning: release, extraMetadata: { // Keep the release userData path compatible with the original dev app. name: release ? 'capsule' : 'capsule-local', capsuleUpdates: { enabled: release } }, artifactName: release ? 'Capsule-${version}-${arch}-Setup.${ext}' : 'Capsule-Local-${version}-${arch}-Setup.${ext}', win: { target: [{ target: 'nsis', arch: ['x64'] }], verifyUpdateCodeSignature: true, signExecutable: release, ...(release ? { signtoolOptions: { publisherName: publisher, signingHashAlgorithms: ['sha256'], ...(env.CSC_NAME ? { certificateSubjectName: env.CSC_NAME } : {}) } } : {}) }, nsis: { oneClick: true, perMachine: false, allowElevation: false, deleteAppDataOnUninstall: false, runAfterFinish: false, shortcutName: release ? 'Capsule' : 'Capsule Local' }, publish: release ? [{ provider: 'generic', url, channel: 'latest' }] : null } }