import type { ExtensionConfig, SafariPlatform } from './types'; /** * Safari container-app tooling: scaffold the Xcode project, sync the built * safari bundle into the appex Resources, and xcodebuild the app. * * Safari Web Extensions ship inside a macOS app, so the safari target has two * halves: the web bundle (`extension:build --target safari`, `dist-safari/`) * and the container app scaffolded here (a checked-in, converter-free Xcode * project: SwiftUI app + Safari Web Extension appex). * * The template tree lives in `safari-template/` at the package root and is * tokenized (`__APP_NAME__`, `__BUNDLE_ID__`, …); scaffolding copies it and * substitutes per project. It mirrors what `xcrun * safari-web-extension-converter` generates, so day-to-day work never needs * the converter. */ /** App target name from the extension display name (`Very Good AdBlock` → `VeryGoodAdBlock`). */ export declare function safariAppName(config: ExtensionConfig): string; /** Directory the container app is scaffolded into. */ export declare function safariProjectDir(cwd?: unknown, dir?: string): string; /** Safari platforms selected in config, de-duplicated in stable order. */ export declare function resolveSafariPlatforms(config: ExtensionConfig, platforms?: SafariPlatform[]): SafariPlatform[]; export declare function safariPackagerArgs(input: string, projectLocation: string, appName: string, bundleId: string, platforms: SafariPlatform[]): string[]; /** Upgrade a Stacks-generated project that copied Resources as a nested folder. */ export declare function migrateSafariResourceBuildPhase(project: string, appName: string): string; /** * Scaffold the macOS container app (Xcode project + SwiftUI app + appex) from * the tokenized template. Idempotent: existing files are kept unless `force`. * Also renders the AppIcon/ExtensionIcon PNGs from the project's own icons * via `sips` (macOS-only step; skipped with a warning elsewhere). */ export declare function scaffoldSafariApp(config: ExtensionConfig, options?: SafariScaffoldOptions): Promise<{ dir: string, written: string[], skipped: string[] }>; /** * Mirror the built safari bundle into the appex `Resources/` folder. The * project references Resources as a folder, so everything synced here ships * in the appex verbatim. Files listed in `config.safariExclude` (e.g. * marketing-site pages built into dist) are kept out. */ export declare function syncSafariResources(config: ExtensionConfig, options?: SafariSyncOptions): Promise<{ resources: string, files: number }>; /** Generate a clean Apple-supported Safari project for macOS, iOS, or both. */ export declare function createSafariUniversalProject(config: ExtensionConfig, options?: SafariUniversalProjectOptions): Promise; /** Build Apple-supported macOS and iOS Safari containers from one web bundle. */ export declare function buildSafariUniversalApp(config: ExtensionConfig, options?: SafariUniversalBuildOptions): Promise; /** * Full pipeline: build the safari bundle, sync it into the appex Resources, * then xcodebuild the container app when full Xcode is available. Returns the * built `.app` path (undefined when xcodebuild was skipped/unavailable). */ export declare function buildSafariApp(config: ExtensionConfig, options?: SafariAppBuildOptions): Promise<{ appPath?: string, resources: string }>; /** Resolve and validate App Store Connect credentials from options or environment variables. */ export declare function resolveAppStoreConnectAuth(options: AppStoreConnectAuth): Required; /** * Signing settings for the *archive* step. The export below is what signs for * real, through App Store Connect with the API key, so neither platform needs a * developer identity here — but "needs no identity" is not the same as "skip * signing", and the difference cost us every macOS upload for two days. * * iOS archives with signing off outright: a development-signed device archive * needs a registered UDID, and iOS has no entitlement that the export cannot * supply for itself. * * macOS ad-hoc signs instead. Turning signing off entirely (which is what this * used to do) also skips entitlement processing, so the archived app and appex * carried no `com.apple.security.app-sandbox`; `-exportArchive` re-signs from * whatever the archive holds, so it had none to preserve, and App Store Connect * rejected every macOS build with "App sandbox not enabled". The `-` identity * is local and costs nothing — entitlements get embedded, no certificate is * requested from Apple, and the export re-signs over the top. * * Not signing at all was itself a fix, for a real problem: `xcodeAuthArgs` * passes `-allowProvisioningUpdates` with an API key, so on a CI runner whose * keychain starts empty every run, Xcode asked Apple for a *new* Mac * Development certificate, used it once, and threw the machine away. Enough * releases and the team hits Apple's certificate cap. Ad-hoc signing keeps that * fixed: `Manual` style with an explicit empty profile is what stops Xcode * reaching for a certificate, so all three settings have to travel together. */ export declare function archiveSigningArgs(platform: SafariPlatform): string[]; /** * Create a signed Release archive and either validate it or upload it to App * Store Connect. Xcode owns certificate/profile creation and the upload so the * same command works locally and in macOS CI with an API key. */ export declare function publishSafariApp(config: ExtensionConfig, options: SafariPublishOptions): Promise; export declare interface SafariScaffoldOptions { bundleId?: string dir?: string force?: boolean version?: string iconsDir?: string teamId?: string cwd?: string } export declare interface SafariSyncOptions { outdir?: string dir?: string cwd?: string } export declare interface SafariUniversalProjectOptions extends SafariSyncOptions { platforms?: SafariPlatform[] build?: boolean version?: string } export declare interface SafariUniversalProject { dir: string project: string resources: string platforms: SafariPlatform[] } export declare interface SafariUniversalBuildOptions extends SafariUniversalProjectOptions { release?: boolean signed?: boolean skipXcodebuild?: boolean } export declare interface SafariUniversalBuildResult extends SafariUniversalProject { appPaths: Partial> } export declare interface SafariAppBuildOptions extends SafariSyncOptions { release?: boolean signed?: boolean skipXcodebuild?: boolean version?: string build?: boolean } export declare interface AppStoreConnectAuth { keyId?: string issuerId?: string keyPath?: string } export declare interface SafariPublishOptions extends SafariSyncOptions, AppStoreConnectAuth { version: string buildNumber?: string teamId?: string validateOnly?: boolean build?: boolean platforms?: SafariPlatform[] processingTimeoutMs?: number processingPollIntervalMs?: number } export declare interface SafariPublishedArtifact { platform: SafariPlatform archivePath: string exportPath: string } export declare interface SafariPublishResult { archivePath: string exportPath: string buildNumber: string artifacts: SafariPublishedArtifact[] deferred: Array<{ platform: SafariPlatform, version: string, state?: string, reason: string }> alreadyPublished: Array<{ platform: SafariPlatform, version: string, state?: string }> attachments: Array<{ platform: SafariPlatform, versionId: string, buildId: string, buildNumber: string }> appStoreSubmission?: { versions: Array<{ platform: SafariPlatform, versionId: string, localizationId: string }> reviewSubmissionIds: string[] } }