refactor(scripts): pin the draw.io release in one json file and share the zip reader

This commit is contained in:
dayuan.jiang
2026-10-11 20:56:07 +09:00
parent 1aa8abcd6e
commit f89d23880e
3 changed files with 81 additions and 50 deletions
+67
View File
@@ -0,0 +1,67 @@
/**
* Shared by the two draw.io fetch scripts (the web app's scripts/fetch-drawio.mjs
* and packages/mcp-server/scripts/fetch-drawio.mjs): the pinned release and a
* minimal reader for its draw.war zip file, so no extra dependency is needed.
*/
import crypto from "node:crypto"
import fs from "node:fs"
import path from "node:path"
import { fileURLToPath } from "node:url"
import zlib from "node:zlib"
// Pinned because the UI relies on draw.io internals; bump deliberately. The
// web app and the MCP server embed the same release.
const VERSION_FILE = path.join(
path.dirname(fileURLToPath(import.meta.url)),
"../packages/mcp-server/src/drawio-version.json",
)
/** { version, sha256, downloadUrl } of the pinned draw.war */
export function readDrawioVersion() {
return JSON.parse(fs.readFileSync(VERSION_FILE, "utf8"))
}
export function sha256(buf) {
return crypto.createHash("sha256").update(buf).digest("hex")
}
// Minimal zip reader: finds the central directory and inflates each entry
export function* readZipEntries(buf) {
const EOCD_SIG = 0x06054b50
let eocd = -1
for (let i = buf.length - 22; i >= Math.max(0, buf.length - 65557); i--) {
if (buf.readUInt32LE(i) === EOCD_SIG) {
eocd = i
break
}
}
if (eocd < 0) throw new Error("Not a zip file (end record not found)")
const count = buf.readUInt16LE(eocd + 10)
let offset = buf.readUInt32LE(eocd + 16)
for (let n = 0; n < count; n++) {
if (buf.readUInt32LE(offset) !== 0x02014b50) {
throw new Error("Corrupt zip central directory")
}
const method = buf.readUInt16LE(offset + 10)
const compressedSize = buf.readUInt32LE(offset + 20)
const nameLength = buf.readUInt16LE(offset + 28)
const extraLength = buf.readUInt16LE(offset + 30)
const commentLength = buf.readUInt16LE(offset + 32)
const localOffset = buf.readUInt32LE(offset + 42)
const name = buf.toString("utf8", offset + 46, offset + 46 + nameLength)
offset += 46 + nameLength + extraLength + commentLength
const localNameLength = buf.readUInt16LE(localOffset + 26)
const localExtraLength = buf.readUInt16LE(localOffset + 28)
const start = localOffset + 30 + localNameLength + localExtraLength
const raw = buf.subarray(start, start + compressedSize)
if (name.endsWith("/")) continue
if (method !== 0 && method !== 8) {
throw new Error(`Unsupported zip compression ${method} (${name})`)
}
yield { name, data: method === 8 ? zlib.inflateRawSync(raw) : raw }
}
}