import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { Zip } from 'zip-lib' const machoObjectBase64 = 'z/rt/gwAAAEAAAAAAQAAAAQAAABoAQAAACAAAAAAAAAZAAAA6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAAAAAiAEAAAAAAAA4AAAAAAAAAAcAAAAHAAAAAgAAAAAAAABfX3RleHQAAAAAAAAAAAAAX19URVhUAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAACIAQAAAgAAAAAAAAAAAAAAAAQAgAAAAAAAAAAAAAAAAF9fY29tcGFjdF91bndpbmRfX0xEAAAAAAAAAAAAAAAAGAAAAAAAAAAgAAAAAAAAAKABAAADAAAAwAEAAAEAAAAAAAACAAAAAAAAAAAAAAAAMgAAABgAAAABAAAAAAALAAACGgAAAAAAAgAAABgAAADIAQAAAwAAAPgBAAAYAAAACwAAAFAAAAAAAAAAAgAAAAIAAAABAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/QwDRAACAUv8PALn/QwCRwANf1gAAAAAAAAAAAAAAABQAAAAAEAACAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAGDQAAAA4BAAAAAAAAAAAAAAcAAAAOAgAAGAAAAAAAAAABAAAADwEAAAAAAAAAAAAAAF9tYWluAGx0bXAxAGx0bXAwAAAAAAAA' export interface PlaywrightUploadFile { arrayBuffer: ArrayBuffer buffer: Buffer mimeType: string name: string type: string } function makeInfoPlist ( appName: string ) { return [ '', '', '', '', ' CFBundleDisplayName', ` ${ appName }`, ' CFBundleExecutable', ` ${ appName }`, ' CFBundleIdentifier', ` com.doesitarm.${ appName.toLowerCase().replaceAll( ' ', '-' ) }`, ' CFBundleName', ` ${ appName }`, ' CFBundleShortVersionString', ' 1.0.0', '', '', '' ].join( '\n' ) } export async function createNativeAppArchive ( appName = 'Playwright Native App' ): Promise { const tempRoot = await mkdtemp( join( tmpdir(), 'doesitarm-playwright-' ) ) const appBundlePath = join( tempRoot, `${ appName }.app` ) const contentsPath = join( appBundlePath, 'Contents' ) const executablePath = join( contentsPath, 'MacOS', appName ) const archivePath = join( tempRoot, `${ appName }.app.zip` ) try { const executableBytes = new Uint8Array( Buffer.from( machoObjectBase64, 'base64' ) ) await mkdir( join( contentsPath, 'MacOS' ), { recursive: true } ) await writeFile( join( contentsPath, 'Info.plist' ), makeInfoPlist( appName ) ) await writeFile( executablePath, executableBytes, { mode: 0o755 } ) const zip = new Zip() zip.addFolder( appBundlePath, `${ appName }.app` ) await zip.archive( archivePath ) const archiveBuffer = await readFile( archivePath ) return { arrayBuffer: archiveBuffer.buffer.slice( archiveBuffer.byteOffset, archiveBuffer.byteOffset + archiveBuffer.byteLength ), buffer: archiveBuffer, mimeType: 'application/zip', name: `${ appName }.app.zip`, type: 'application/zip' } } finally { await rm( tempRoot, { force: true, recursive: true } ) } }