UNPKG

@shockpkg/ria-packager

Version:

Package for creating Adobe AIR packages

567 lines (439 loc) 11.7 kB
import _defineProperty from "@babel/runtime/helpers/defineProperty"; import { join as pathJoin, basename } from 'path'; import fse from 'fs-extra'; import { ArchiveDir, ArchiveHdi, createArchiveByFileExtension, fsWalk, pathNormalize } from '@shockpkg/archive-files'; import { Signature } from "./signature.mjs"; import { HasherSha256 } from "./hasher/sha256.mjs"; /** * Options for adding resources. */ /** * Packager constructor. * * @param path Output path. */ export class Packager extends Object { /** * Make a debug build. */ /** * Keystore object to use for signing. */ /** * Timestamp URL. */ /** * File and directory names to exclude when added a directory. */ /** * Output path. */ /** * Open flag. */ /** * Adding a resource flag. */ /** * Hasher object. */ /** * Signature object. */ constructor(path) { super(); _defineProperty(this, "debug", false); _defineProperty(this, "keystore", null); _defineProperty(this, "timestampUrl", null); _defineProperty(this, "excludes", [/^\./, /^ehthumbs\.db$/, /^Thumbs\.db$/]); _defineProperty(this, "path", void 0); _defineProperty(this, "_isOpen", false); _defineProperty(this, "_isAddingResource", false); _defineProperty(this, "_hasher", void 0); _defineProperty(this, "_signature", null); this._hasher = this._createHasher(); this.path = path; } /** * Check if output open. * * @returns Returns true if open, else false. */ get isOpen() { return this._isOpen; } /** * Open with application descriptor XML data. * * @param applicationData XML data. */ async open(applicationData) { if (this._isOpen) { throw new Error('Already open'); } this._applicationInfoInit(applicationData); await this._open(applicationData); this._isOpen = true; this._hasher.reset(); this._signature = null; if (this.signed) { this._signature = this._createSignature(); this._signature.timestampUrl = this.timestampUrl; const keystore = this._getKeystore(); this._signature.certificate = keystore.getCertificate(); this._signature.keyPrivate = keystore.getKeyPrivate(); } await this._addMetaResourcesStart(applicationData); } /** * Open with application descriptor file. * * @param descriptorFile Application descriptor file. */ async openFile(descriptorFile) { const applicationData = await fse.readFile(descriptorFile); await this.open(applicationData); } /** * Close output. */ async close() { if (!this._isOpen) { throw new Error('Not open'); } await this._addMetaResourcesEnd(); await this._close(); this._isOpen = false; this._applicationInfoClear(); this._hasher.reset(); this._signature = null; } /** * Run asyncronous function with automatic open and close. * * @param applicationData XML data. * @param func Async function. * @returns Return value of the async function. */ async with(applicationData, func) { await this.open(applicationData); let r; try { r = await func.call(this, this); } finally { await this.close(); } return r; } /** * Run asyncronous function with automatic open and close. * * @param descriptorFile Application descriptor file. * @param func Async function. * @returns Return value of the async function. */ async withFile(descriptorFile, func) { await this.openFile(descriptorFile); let r; try { r = await func.call(this, this); } finally { await this.close(); } return r; } /** * Check if name is excluded file. * * @param name File name. * @returns Returns true if excluded, else false. */ isExcludedFile(name) { for (const exclude of this.excludes) { if (exclude.test(name)) { return true; } } return false; } /** * Add resource with file. * * @param source File path. * @param destination Packaged file relative destination. * @param options Resource options. */ async addResourceFile(source, destination = null, options = null) { const opts = options || {}; const dest = destination === null ? source : destination; const stat = await fse.lstat(source); // Symlinks would only be allowed in a macOS native extension. // Throw an error like the official packager does. if (stat.isSymbolicLink()) { throw new Error(`Cannot add symlink: ${source}`); } // Throw if not a regular file. if (!stat.isFile()) { throw new Error(`Unsupported file type: ${source}`); } let { executable } = opts; if (executable !== true && executable !== false) { // eslint-disable-next-line no-bitwise executable = !!(stat.mode & 0b001000000); } const data = await fse.readFile(source); await this.addResource(dest, data, { executable, mtime: opts.mtime || stat.mtime }); } /** * Add resource with directory. * Walks the directory looking for files to add, skips excluded file names. * * @param source Directory path. * @param destination Packaged directory relative destination. * @param options Resource options. */ async addResourceDirectory(source, destination = null, options = null) { const dest = destination === null ? source : destination; await fsWalk(source, async (path, stat) => { // If this name is excluded, skip without descending. if (this.isExcludedFile(basename(path))) { return false; } // Ignore directories, but descend into them. // Only files are listed in the ZIP packages. // Created automatically for files in any other package. if (stat.isDirectory()) { return true; } // Anything else assume file. await this.addResourceFile(pathJoin(source, path), pathJoin(dest, path), options); return true; }); } /** * Add resource with data. * * @param destination Packaged file relative destination. * @param data Resource data. * @param options Resource options. */ async addResource(destination, data, options = null) { if (!this._isOpen) { throw new Error('Not open'); } if (this._isAddingResource) { throw new Error('Resources must be added sequentially'); } this._isAddingResource = true; await this._addResource(pathNormalize(destination), data, options || {}, true, true); this._isAddingResource = false; } /** * Create Hasher object. * * @returns Hasher object. */ _createHasher() { return new HasherSha256(); } /** * Create Signature object. * * @returns Hasher object. */ _createSignature() { return new Signature(); } /** * Path of the mimetype meta resource. * * @returns Resource path. */ get _metaResourceMimetypePath() { return 'mimetype'; } /** * Path of the application meta resource. * * @returns Resource path. */ get _metaResourceApplicationPath() { return 'META-INF/AIR/application.xml'; } /** * Path of the hash meta resource. * * @returns Resource path. */ get _metaResourceHashPath() { return 'META-INF/AIR/hash'; } /** * Path of the debug meta resource. * * @returns Resource path. */ get _metaResourceDebugPath() { return 'META-INF/AIR/debug'; } /** * Path of the signatures meta resource. * * @returns Resource path. */ get _metaResourceSignaturesPath() { return 'META-INF/signatures.xml'; } /** * Get encoded mimetype data. * * @returns Mimetype buffer. */ _getMimetypeData() { // The mimetype if UTF-8. return Buffer.from(this.mimetype, 'utf8'); } /** * Get the keystore object. * * @returns Keystore object. */ _getKeystore() { const r = this.keystore; if (!r) { throw new Error('A keystore not set'); } return r; } /** * Add resource with data, with options controlling hashing and signing. * * @param destination Packaged file relative destination. * @param data Resource data. * @param options Resource options. * @param hashed This file is hashed. * @param signed This file is signed. */ async _addResource(destination, data, options, hashed, signed) { if (hashed) { this._hasher.update(data); } if (signed) { const signature = this._signature; if (signature) { signature.addFile(destination, data); } } await this._writeResource(destination, data, options); } /** * Add meta resources start. * * @param applicationData XML data. */ async _addMetaResourcesStart(applicationData) { await this._addMetaResourceMimetype(); await this._addMetaResourceApplication(applicationData); await this._addMetaResourceHash(); if (this.debug) { await this._addMetaResourceDebug(); } } /** * Add meta resources end. */ async _addMetaResourcesEnd() { if (this.signed) { await this._addMetaResourceSignatures(); } } /** * Add meta resource for the mimetype. */ async _addMetaResourceMimetype() { const path = this._metaResourceMimetypePath; const data = this._getMimetypeData(); await this._addResource(path, data, {}, true, true); } /** * Add meta resource for the application descriptor. * * @param applicationData The application descriptor data. */ async _addMetaResourceApplication(applicationData) { const path = this._metaResourceApplicationPath; await this._addResource(path, applicationData, {}, true, true); } /** * Add meta resource for the hash (needs updating on close). */ async _addMetaResourceHash() { const path = this._metaResourceHashPath; const data = Buffer.alloc(this._hasher.bytes); await this._addResource(path, data, {}, false, false); } /** * Add meta resource for debug. */ async _addMetaResourceDebug() { const path = this._metaResourceDebugPath; const data = Buffer.alloc(0); await this._addResource(path, data, {}, true, true); } /** * Add resource for signatures. */ async _addMetaResourceSignatures() { const path = this._metaResourceSignaturesPath; const signature = this._signature; if (!signature) { throw new Error('Internal error'); } signature.digest(); signature.sign(); if (signature.timestampUrl) { await signature.timestamp(); } const data = signature.encode(); await this._addResource(path, data, {}, false, false); } /** * Init application info from descriptor data. * * @param applicationData The application descriptor data. */ _applicationInfoInit(applicationData) {// Do nothing. } /** * Clear application info from descriptor data. */ _applicationInfoClear() {// Do nothing. } /** * Open path as archive. * * @param path Archive path. * @returns Archive instance. */ async _openArchive(path) { const stat = await fse.stat(path); if (stat.isDirectory()) { return new ArchiveDir(path); } const archive = createArchiveByFileExtension(path); if (!archive) { throw new Error(`Unrecognized archive format: ${path}`); } if (archive instanceof ArchiveHdi) { archive.nobrowse = true; } return archive; } /** * Package mimetype. * * @returns Mimetype string. */ } //# sourceMappingURL=packager.mjs.map