import fs from 'node:fs'; const configPath = new URL('../src-tauri/tauri.conf.json', import.meta.url); const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); const capabilityPath = new URL( '../src-tauri/capabilities/main.json', import.meta.url, ); const capability = JSON.parse(fs.readFileSync(capabilityPath, 'utf8')); const buildScriptPath = new URL('../src-tauri/build.rs', import.meta.url); const buildScript = fs.readFileSync(buildScriptPath, 'utf8'); const iconPath = new URL('../src-tauri/icons/icon.png', import.meta.url); const sharedContractPath = new URL( '../../../packages/shared/src/contracts/hostBridge.ts', import.meta.url, ); const sharedContractSource = fs.readFileSync(sharedContractPath, 'utf8'); const mainPath = new URL('../src-tauri/src/main.rs', import.meta.url); const main = fs.readFileSync(mainPath, 'utf8'); function extractStringArrayExport(source, exportName) { const match = source.match( new RegExp(`export const ${exportName}[^=]*= \\[([\\s\\S]*?)\\](?: as const)?;`), ); if (!match) { throw new Error(`unable to read ${exportName}`); } const entries = [...match[1].matchAll(/'([^']+)'/g)].map( (entry) => entry[1], ); if (match[1].includes('...HOST_BRIDGE_METHODS')) { return [ ...extractStringArrayExport(source, 'HOST_BRIDGE_METHODS'), ...entries, ]; } return entries; } function extractDesktopCapabilities(source) { const match = source.match(/fn capabilities\(\)[^{]*\{[\s\S]*?vec!\[([\s\S]*?)\]\s*\}/); if (!match) { throw new Error('unable to read desktop shell capabilities'); } return [...match[1].matchAll(/"([^"]+)"/g)].map((entry) => entry[1]); } function resolveHostCapabilitiesFromUrl(rawUrl) { const url = new URL(rawUrl, 'https://app.genarrative.world/'); return (url.searchParams.get('hostCapabilities') ?? '') .split(',') .map((capability) => capability.trim()) .filter(Boolean); } function assertSameList(actual, expected, label) { if ( actual.length !== expected.length || actual.some((value, index) => value !== expected[index]) ) { throw new Error( `${label} drifted: expected ${expected.join(', ')} but got ${actual.join(', ')}`, ); } } const sharedCapabilities = extractStringArrayExport( sharedContractSource, 'HOST_BRIDGE_CAPABILITIES', ); const desktopCapabilities = extractDesktopCapabilities(main); const unknownDesktopCapabilities = desktopCapabilities.filter( (capability) => !sharedCapabilities.includes(capability), ); if (unknownDesktopCapabilities.length > 0) { throw new Error( `desktop shell declares unknown HostBridge capabilities: ${unknownDesktopCapabilities.join(', ')}`, ); } if (config.build?.frontendDist !== '../../../dist') { throw new Error('desktop shell must package the root H5 dist'); } if (config.build?.beforeBuildCommand !== 'npm --prefix ../.. run build:raw && npm run typecheck') { throw new Error('desktop shell build command must run from apps/desktop-shell'); } const [mainWindow] = config.app?.windows ?? []; if (!mainWindow || mainWindow.create !== false) { throw new Error('desktop shell must create the main window from Rust setup'); } if (!config.bundle?.icon?.includes('icons/icon.png')) { throw new Error('desktop shell must use the real brand icon asset'); } const requiredUrlParts = [ 'clientRuntime=native_app', 'clientType=native_app', 'hostShell=tauri_desktop', 'hostPlatform=unknown', 'bridgeVersion=1', ]; for (const part of requiredUrlParts) { if (!String(mainWindow.url ?? '').includes(part)) { throw new Error(`desktop shell main window URL missing ${part}`); } if (!String(config.build?.devUrl ?? '').includes(part)) { throw new Error(`desktop shell dev URL missing ${part}`); } } assertSameList( resolveHostCapabilitiesFromUrl(mainWindow.url), desktopCapabilities, 'desktop shell main window hostCapabilities', ); assertSameList( resolveHostCapabilitiesFromUrl(config.build?.devUrl ?? ''), desktopCapabilities, 'desktop shell dev hostCapabilities', ); const requiredPermissions = [ 'core:default', 'allow-host-bridge-request', ]; const requiredBuildCommands = ['host_bridge_request']; const requiredMainSnippets = [ 'tauri_plugin_clipboard_manager::init()', '"share.open"', '"share.setTarget"', '"navigation.openNativePage"', '"app.setTitle"', '"app.setBadgeCount"', '"clipboard.writeText"', '"file.exportText"', '"file.exportImage"', 'tauri_plugin_dialog::init()', '"copied_to_clipboard"', '"file export cancelled"', 'BASE64_STANDARD.decode', 'set_title', 'set_badge_count', ]; for (const permission of requiredPermissions) { if (!capability.permissions?.includes(permission)) { throw new Error(`desktop shell capability missing ${permission}`); } } for (const command of requiredBuildCommands) { if (!buildScript.includes(command)) { throw new Error(`desktop shell build manifest missing ${command}`); } } if (buildScript.includes('resolve_desktop_shell_runtime')) { throw new Error('desktop shell build manifest exposes an unused runtime command'); } const icon = fs.readFileSync(iconPath); if (icon.length < 10000) { throw new Error('desktop shell icon must use a real brand asset'); } for (const snippet of requiredMainSnippets) { if (!main.includes(snippet)) { throw new Error(`desktop shell Rust host bridge missing ${snippet}`); } }