/** * Minimal ZIP (STORE / no compression) for browser — one dependency-free helper * so PUBLISH_QDN_RESOURCE can ship WEBSITE/APP archives from this Q-App. */ (function (global) { 'use strict'; const crcTable = (function makeTable() { const t = new Uint32Array(256); for (let i = 0; i < 256; i++) { let c = i; for (let k = 0; k < 8; k++) c = (c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1); t[i] = c >>> 0; } return t; })(); function crc32(buf) { let c = 0xffffffff; for (let i = 0; i < buf.length; i++) c = crcTable[(c ^ buf[i]) & 0xff] ^ (c >>> 8); return (c ^ 0xffffffff) >>> 0; } function strToUtf8(s) { return new TextEncoder().encode(String(s)); } /** @param {Record} files map path -> bytes or utf8 string */ function buildZipStore(files) { const entries = Object.keys(files).map((name) => { const raw = files[name]; const data = raw instanceof Uint8Array ? raw : strToUtf8(raw); return { name, data }; }); let offset = 0; const parts = []; const central = []; for (const e of entries) { const nameBytes = strToUtf8(e.name); const nameLen = nameBytes.length; const data = e.data; const size = data.length; const crc = crc32(data); const sig = 0x04034b50; const ver = 20; const flags = 0; const method = 0; const time = 0; const date = 0; const csize = size; const hlen = 30 + nameLen; const local = new DataView(new ArrayBuffer(hlen + size)); let p = 0; local.setUint32(p, sig, true); p += 4; local.setUint16(p, ver, true); p += 2; local.setUint16(p, flags, true); p += 2; local.setUint16(p, method, true); p += 2; local.setUint16(p, time, true); p += 2; local.setUint16(p, date, true); p += 2; local.setUint32(p, crc, true); p += 4; local.setUint32(p, csize, true); p += 4; local.setUint32(p, size, true); p += 4; local.setUint16(p, nameLen, true); p += 2; local.setUint16(p, 0, true); p += 2; new Uint8Array(local.buffer).set(nameBytes, p); p += nameLen; new Uint8Array(local.buffer).set(data, p); parts.push(new Uint8Array(local.buffer)); central.push({ nameBytes, crc, size, csize, localOffset: offset, }); offset += local.buffer.byteLength; } const cdParts = []; let cdSize = 0; let cdOffset = offset; for (let i = 0; i < central.length; i++) { const c = central[i]; const nameLen = c.nameBytes.length; const hlen = 46 + nameLen; const buf = new DataView(new ArrayBuffer(46)); let q = 0; buf.setUint32(q, 0x02014b50, true); q += 4; buf.setUint16(q, 20, true); q += 2; buf.setUint16(q, 20, true); q += 2; buf.setUint16(q, 0, true); q += 2; buf.setUint16(q, 0, true); q += 2; buf.setUint16(q, 0, true); q += 2; buf.setUint16(q, 0, true); q += 2; buf.setUint32(q, c.crc, true); q += 4; buf.setUint32(q, c.csize, true); q += 4; buf.setUint32(q, c.size, true); q += 4; buf.setUint16(q, nameLen, true); q += 2; buf.setUint16(q, 0, true); q += 2; buf.setUint16(q, 0, true); q += 2; buf.setUint16(q, 0, true); q += 2; buf.setUint16(q, 0, true); q += 2; buf.setUint32(q, 0, true); q += 4; buf.setUint32(q, c.localOffset, true); q += 4; const header = new Uint8Array(buf.buffer); const merged = new Uint8Array(hlen); merged.set(header, 0); merged.set(c.nameBytes, 46); cdParts.push(merged); cdSize += merged.length; } const eoc = new DataView(new ArrayBuffer(22)); eoc.setUint32(0, 0x06054b50, true); eoc.setUint16(4, 0, true); eoc.setUint16(6, 0, true); eoc.setUint16(8, entries.length, true); eoc.setUint16(10, entries.length, true); eoc.setUint32(12, cdSize, true); eoc.setUint32(16, cdOffset, true); eoc.setUint16(20, 0, true); const totalLen = offset + cdSize + 22; const out = new Uint8Array(totalLen); let o = 0; for (const part of parts) { out.set(part, o); o += part.length; } for (const part of cdParts) { out.set(part, o); o += part.length; } out.set(new Uint8Array(eoc.buffer), o); return out; } global.buildZipStore = buildZipStore; })(typeof window !== 'undefined' ? window : globalThis);