UNPKG

@pipobscure/sea

Version:
454 lines 16.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); //@ts-ignore const SEA = __importStar(require("node:sea")); const ZLib = __importStar(require("node:zlib")); const Crypto = __importStar(require("node:crypto")); const Url = __importStar(require("node:url")); const Path = __importStar(require("node:path")); const VM = __importStar(require("node:vm")); const OS = __importStar(require("node:os")); const FS = __importStar(require("node:fs")); const node_module_1 = require("node:module"); const node_assert_1 = __importDefault(require("node:assert")); const VERSION = 1; const COMPRESSION = { NONE: 0x00, DEFLATE: 0x01, GZIP: 0x02, BROTLI: 0x03, }; const HASH = { NONE: 0x00, SHA1: 0x10, SHA2: 0x20, }; const PROTO = 'sea:'; const resolutions = JSON.parse(SEA.getAsset('resolv', 'utf8')); const bundle = SEA.getRawAsset('bundle'); (0, node_assert_1.default)(typeof bundle === 'object', 'bundle is not an ArrayBuffer'); const blobs = extractBlobs(bundle); function extractBlobs(data) { const index = {}; let item = blobData(data, 0); while (item) { index[item.name] = item; item = blobData(data, item.next); } return index; function blobData(data, offset = 0) { if (offset >= data.byteLength) return null; const header = new DataView(data, offset, 8); const version = header.getUint8(0); const flags = header.getUint8(1); const nlen = header.getUint16(2); const dlen = header.getUint32(4); if (version !== VERSION) throw new Error(`invalid blob-version: ${version}`); const hashFlag = flags & 0xf0; const compression = flags & 0x0f; offset += 8; const name = Buffer.from(data, offset, nlen).toString('utf-8'); offset += nlen; let hash = null; switch (hashFlag) { case HASH.SHA1: hash = data.slice(offset, offset + 20); offset += 20; break; case HASH.SHA2: hash = data.slice(offset, offset + 32); offset += 32; break; } const blob = data.slice(offset, offset + dlen); offset += dlen; return { name, blob, hash, compression, next: offset, }; } } function asset(urlStr, encoding) { const url = new URL(urlStr); if (url.protocol !== PROTO) throw new Error(`not an asset-url ${urlStr}`); const name = url.pathname; const info = blobs[name]; if (!info) throw new Error(`not an asset: ${name}`); let data = Buffer.from(info.blob); switch (info.compression) { case COMPRESSION.BROTLI: data = ZLib.brotliDecompressSync(data); break; case COMPRESSION.DEFLATE: data = ZLib.inflateSync(data); break; case COMPRESSION.GZIP: data = ZLib.gunzipSync(data); break; } switch (info.hash?.byteLength) { case 20: { const actual = Crypto.createHash('sha1').update(data).digest('hex'); const wanted = Buffer.from(info.hash).toString('hex'); if (actual !== wanted) throw new Error('invalid content'); break; } case 32: { const actual = Crypto.createHash('sha256').update(data).digest('hex'); const wanted = Buffer.from(info.hash).toString('hex'); if (actual !== wanted) throw new Error('invalid content'); break; } } if (encoding) return data.toString(encoding); return data; } const modules = Object.create(null); function resolve(parent, specifier) { if (specifier && node_module_1.Module.isBuiltin(specifier)) { return new URL(specifier.startsWith('node:') ? specifier : `node:${specifier}`); } else if (!specifier || !specifier.startsWith(PROTO)) { specifier = resolutions[parent?.slice(PROTO.length) ?? '<main>'][specifier ?? '<main>'] ?? specifier; return new URL(specifier, parent ?? `${PROTO}/`); } else { const url = new URL(specifier); return url; } } function patch(id, exports) { try { // @ts-expect-error exports.__esModule = true; } catch { } try { switch (id) { case 'node:fs': return patchFS(exports); case 'node:fs/promises': return patchFSP(exports); case 'node:path': return patchPath(exports); default: return exports; } } catch { return exports; } function resolve(one, two, ...rest) { const url = two ? new URL(two, one) : new URL(one); if (rest.length) return resolve(url.toString(), ...rest); return url.toString().split(/\/\/+/).join('/'); } function readFileSync(path, ...args) { if (!path.startsWith(PROTO)) return FS.readFileSync(path, ...args); path = resolve(path); const data = asset(path); if (!data) { throw Object.assign(new Error(`ENOENT: no such file or directory, open '${path}'`), { errno: -4058, code: 'ENOENT' }); } else { const opts = args[0]; const encoding = 'object' === typeof opts ? opts?.encoding : opts; if (encoding) return data.toString(encoding); const copy = Buffer.allocUnsafe(data.byteLength); data.copy(copy); return copy; } } function readDir(path, opts) { if ('string' === typeof opts) opts = { encoding: opts }; if (opts?.withFileTypes) throw new Error('unsupported option: withFileTypes'); path = resolve(path).slice(PROTO.length); path = path[path.length - 1] === '/' ? path : `${path}/`; const matches = Object.values(blobs).filter((b) => b?.name.startsWith(path) ?? false); let contents = matches.map((b) => b?.name.slice(path.length)); contents = opts?.recursive ? contents : contents.filter((f) => !f.includes('/')); if ('string' === typeof (opts?.encoding ?? opts)) { switch (opts?.encoding) { case undefined: case null: case 'utf8': case 'utf-8': return contents; case 'buffer': default: return contents.map((f) => Buffer.from(f).toString(opts.encoding)); } } return contents; } function patchFS(exports) { return Object.create(exports, { readFileSync: { value: readFileSync, enumerable: true, writable: true, configurable: true, }, existsSync: { value: function existsSync(path) { if (!path.startsWith(PROTO)) return exports.existsSync.call(this, path); path = resolve(path).slice(PROTO.length); return !!blobs[path]; }, enumerable: true, writable: true, configurable: true, }, readdirSync: { value: function readdirSync(path, opts) { if (!path.startsWith(PROTO)) { //@ts-ignore return exports.readdirSync.call(this, path, opts); } return readDir(path, opts); }, enumerable: true, writable: true, configurable: true, }, readFile: { value: function (path, opts, cb) { if (!path.startsWith(PROTO)) { //@ts-ignore return exports.readFile.call(this, path, opts, cb); } if (!cb) { cb = opts; opts = undefined; } let data = null; try { data = readFileSync(path, opts); } catch (err) { cb(err); return; } cb(null, data); }, enumerable: true, writable: true, configurable: true, }, exists: { value: function exists(path, cb) { if (!path.startsWith(PROTO)) return exports.exists.call(this, path, cb); path = resolve(path).slice(PROTO.length); let exists = false; try { exists = !!blobs[path]; } catch (err) { cb(err, undefined); return; } cb(null, exists); }, enumerable: true, writable: true, configurable: true, }, readdir: { value: function readdir(path, opts, cb) { if (!path.startsWith(PROTO)) return exports.readdir.call(this, path, opts, cb); if (!cb) { cb = opts; opts = undefined; } let files = []; try { files = readDir(path, opts); } catch (err) { cb(err, undefined); return; } cb(null, files); }, enumerable: true, writable: true, configurable: true, }, }); } function patchFSP(exports) { return Object.create(exports, { readFile: { value: function readFile(path, opts) { if (!path.startsWith(PROTO)) return exports.readFile.call(this, path, opts); try { return Promise.resolve(readFileSync(path, opts)); } catch (err) { return Promise.reject(err); } }, enumerable: true, writable: true, configurable: true, }, readdir: { value: function readFile(path, opts) { if (!path.startsWith(PROTO)) return exports.readdir.call(this, path, opts); try { return Promise.resolve(readDir(path, opts)); } catch (err) { return Promise.reject(err); } }, enumerable: true, writable: true, configurable: true, }, }); } function patchPath(exports) { return Object.create(exports, { resolve: { value: function (path, ...rest) { if (!path.startsWith(PROTO)) return exports.resolve.call(this, path, ...rest); return resolve(path, ...rest); }, enumerable: true, writable: true, configurable: true, }, join: { value: function join(path, ...rest) { if (!path.startsWith(PROTO)) return exports.join.call(this, path, ...rest); path = [path, ...rest].join('/'); return resolve(path); }, enumerable: true, writable: true, configurable: true, }, }); } } function load(parent, specifier) { const url = resolve(parent, specifier); const id = url.toString(); if (modules[id]) return modules[id].exports; switch (url.protocol) { case 'node:': { const exports = patch(id, require(id)); modules[id] = { id, exports }; return exports; } case 'file:': { const exports = require(Url.fileURLToPath(url)); modules[id] = { id, exports }; return exports; } case PROTO: const module = (modules[id] = { id, exports: {} }); const name = url.pathname; switch (Path.extname(name).toLowerCase()) { case '.json': return (module.exports = JSON.parse(asset(id, 'utf-8'))); case '.js': case '.cjs': { const dirname = new URL('./', url).toString(); const exec = new VM.Script(`(function module(module,exports,require,__dirname,__filename) {\n${asset(id, 'utf-8')}\n})`, { filename: id, lineOffset: -1 }).runInThisContext(); const main = modules[resolve().toString()]; exec.call(null, module, module.exports, //@ts-expect-error Object.assign((specifier) => load(id, specifier), { main, addon: process.addon }), dirname, id); return module.exports; } case '.node': { const code = asset(id); const tmpnam = Path.join(OS.tmpdir(), [crypto.randomUUID(), 'node'].join('.')); FS.writeFileSync(tmpnam, code); //@ts-ignore process.dlopen(module, tmpnam); return module.exports; } default: throw new Error(`invalid code-type: ${id}`); } default: { throw new Error(`invalid code resource: ${id}`); } } } //@ts-expect-error process.addon = function addon(pkgbase) { if (!pkgbase.startsWith('sea:')) throw new Error('not inside sea'); const basename = pkgbase.slice(PROTO.length); const addons = Array.from(Object.keys(blobs)) .filter((name) => name.startsWith(basename) && name.endsWith('.node')) .sort((a, b) => a.length - b.length); const url = new URL(addons[0], pkgbase); return load(pkgbase, url.toString()); }; if (module === require.main) load(); //# sourceMappingURL=runtime.js.map