local-modules.ts (1835B)
1 import { BUNDLE_LIMIT, decodeBundle, sha256, type BundleHash } from './bundle.js'; 2 import { moduleHash, moduleIdentity, type ModuleHash } from './module-identity.js'; 3 export const LOCAL_MODULE_COUNT = 32, LOCAL_MODULE_BYTES = 64 * 1024 * 1024; 4 export interface LocalModuleInput { manifest: string; bytes: Uint8Array; } 5 export interface LocalModule extends LocalModuleInput { manifest: ModuleHash; hash: BundleHash; names: string[]; } 6 export async function validateLocalModule(input: Uint8Array): Promise<LocalModule> { 7 if (!(input instanceof Uint8Array) || !input.byteLength || input.byteLength > BUNDLE_LIMIT) throw new Error('Local module must be at most 16 MiB'); 8 const bytes = new Uint8Array(input), bundle = decodeBundle(bytes); 9 return { bytes, manifest: await moduleIdentity(bundle), hash: await sha256(bytes), names: bundle.exports.map(e => e.name) }; 10 } 11 // Copy at the async boundary; verify supplied identities against these bytes. 12 export function localModuleInputs(input: unknown = []): LocalModuleInput[] { 13 if (!Array.isArray(input) || input.length > LOCAL_MODULE_COUNT) throw new Error('Local module count limit exceeded'); 14 let size = 0; const seen = new Set<string>(); 15 return input.map((entry: unknown) => { 16 if (!entry || typeof entry !== 'object') throw new Error('Invalid local module'); 17 const { manifest, bytes } = entry as LocalModuleInput; 18 moduleHash(manifest); 19 if (seen.has(manifest)) throw new Error('Duplicate local module manifest'); seen.add(manifest); 20 if (!(bytes instanceof Uint8Array) || !bytes.byteLength || bytes.byteLength > BUNDLE_LIMIT) throw new Error('Local module byte limit exceeded'); 21 size += bytes.byteLength; if (size > LOCAL_MODULE_BYTES) throw new Error('Local module collection exceeds 64 MiB'); 22 return { manifest, bytes: new Uint8Array(bytes) }; 23 }); 24 }