UNPKG

@devicecloud.dev/dcd

Version:

Better cloud maestro testing

139 lines (138 loc) 4.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MetadataExtractorService = exports.IosZipMetadataExtractor = exports.IosAppMetadataExtractor = exports.AndroidMetadataExtractor = void 0; const AppInfoParser = require("app-info-parser"); const bplist_parser_1 = require("bplist-parser"); const promises_1 = require("node:fs/promises"); const path = require("node:path"); const StreamZip = require("node-stream-zip"); const plist_1 = require("plist"); /** * Extracts metadata from Android APK files */ class AndroidMetadataExtractor { canHandle(filePath) { return filePath.endsWith('.apk'); } async extract(filePath) { const parser = new AppInfoParser(filePath); const result = await parser.parse(); return { appId: result.package, platform: 'android' }; } } exports.AndroidMetadataExtractor = AndroidMetadataExtractor; /** * Extracts metadata from iOS .app directories */ class IosAppMetadataExtractor { canHandle(filePath) { return filePath.endsWith('.app'); } async extract(filePath) { const infoPlistPath = path.normalize(path.join(filePath, 'Info.plist')); const buffer = await (0, promises_1.readFile)(infoPlistPath); const data = await this.parseInfoPlist(buffer); const appId = data.CFBundleIdentifier; return { appId, platform: 'ios' }; } async parseInfoPlist(buffer) { let data; const bufferType = buffer[0]; if (bufferType === 60 || bufferType === '<' || bufferType === 239) { data = (0, plist_1.parse)(buffer.toString()); } else if (bufferType === 98) { data = (0, bplist_parser_1.parseBuffer)(buffer)[0]; } else { throw new Error('Unknown plist buffer type.'); } return data; } } exports.IosAppMetadataExtractor = IosAppMetadataExtractor; /** * Extracts metadata from iOS .zip files containing .app bundles */ class IosZipMetadataExtractor { canHandle(filePath) { return filePath.endsWith('.zip'); } async extract(filePath) { return new Promise((resolve, reject) => { const zip = new StreamZip({ file: filePath }); zip.on('ready', () => { // Get all entries and sort them by path depth const entries = Object.values(zip.entries()); const sortedEntries = entries.sort((a, b) => { const aDepth = a.name.split('/').length; const bDepth = b.name.split('/').length; return aDepth - bDepth; }); // Find the first Info.plist in the shallowest directory const infoPlist = sortedEntries.find((e) => e.name.endsWith('.app/Info.plist')); if (!infoPlist) { reject(new Error('Failed to find info plist')); return; } const buffer = zip.entryDataSync(infoPlist.name); this.parseInfoPlist(buffer) .then((data) => { const appId = data.CFBundleIdentifier; zip.close(); resolve({ appId, platform: 'ios' }); }) .catch(reject); }); zip.on('error', reject); }); } async parseInfoPlist(buffer) { let data; const bufferType = buffer[0]; if (bufferType === 60 || bufferType === '<' || bufferType === 239) { data = (0, plist_1.parse)(buffer.toString()); } else if (bufferType === 98) { data = (0, bplist_parser_1.parseBuffer)(buffer)[0]; } else { throw new Error('Unknown plist buffer type.'); } return data; } } exports.IosZipMetadataExtractor = IosZipMetadataExtractor; /** * Service for extracting app metadata from various file formats */ class MetadataExtractorService { extractors = [ new AndroidMetadataExtractor(), new IosZipMetadataExtractor(), new IosAppMetadataExtractor(), ]; /** * Extract app metadata from a file * @param filePath - Path to the app file (.apk, .app, or .zip) * @returns App metadata or undefined if extraction fails */ async extract(filePath) { const extractor = this.extractors.find((e) => e.canHandle(filePath)); if (!extractor) { return undefined; } try { return await extractor.extract(filePath); } catch { console.warn('Failed to extract app metadata, please share with support@devicecloud.dev so we can improve our parsing.'); return undefined; } } } exports.MetadataExtractorService = MetadataExtractorService;