mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-10-12 04:29:51 +08:00
68 lines
2.6 KiB
JavaScript
68 lines
2.6 KiB
JavaScript
/**
|
|||
|
|
* 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 }
|
||
|
|
}
|
||
|
|
}
|