mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-10-13 04:59:51 +08:00
refactor(mcp-server): make the diagram modules usable from the web app
The web app will reuse the MCP server's XML engine instead of its own
copy in lib/utils.ts, so these modules now run in the browser too.
- Relative imports end in .ts, rewritten to .js by tsc
(rewriteRelativeImportExtensions); Next.js resolves them directly
- Every module uses the global DOMParser/XMLSerializer: native in the
browser, linkedom in Node via installDomPolyfill. pages.ts parsed with
linkedom but serialized with the global serializer, which throws in
the browser
- The saxes syntax check moves to xml-syntax.ts, so the browser does not
pull in linkedom; it now also rejects undeclared prefixes such as
xlink:, as the browser does
- Page decompression uses pako and atob instead of node:zlib and Buffer
- hasCells moves to pages.ts, away from the file system code
- The duplicate cell id check counts UserObject/object ids
- wrapCellsInModel drops comments and text before the first cell, which
the web app accepts today
- validateAndFixXml takes { strict: false } for diagrams with user content
- Web tests run these modules with a browser DOM (jsdom)
- saxes becomes a direct dependency of the web app
This commit is contained in:
@@ -17,8 +17,7 @@
|
||||
* - how to add/rename/delete pages without re-parsing ad-hoc.
|
||||
*/
|
||||
|
||||
import { DOMParser } from "linkedom"
|
||||
import { getXmlSyntaxError } from "./dom.js"
|
||||
import { getXmlSyntaxError } from "./xml-syntax.ts"
|
||||
|
||||
export interface PageInfo {
|
||||
id: string
|
||||
@@ -53,6 +52,10 @@ export function generatePageId(): string {
|
||||
return `${a}-${b}`
|
||||
}
|
||||
|
||||
/** Any cell besides the root cells "0" and "1" */
|
||||
export const hasCells = (xml: string) =>
|
||||
/<(mxCell\b[^>]*\bid="(?![01]")|UserObject\b|object\b)/.test(xml)
|
||||
|
||||
/** Cheap regex check — does the XML start with an <mxfile> root? */
|
||||
export function isMxFile(xml: string): boolean {
|
||||
return /^\s*(<\?xml[^>]*\?>\s*)?<mxfile[\s>]/i.test(xml)
|
||||
@@ -87,15 +90,24 @@ const ROOT_CELLS = '<mxCell id="0"/><mxCell id="1" parent="0"/>'
|
||||
* Turn a list of bare cells (optionally inside <root>) into a one-page
|
||||
* <mxGraphModel>, adding the "0" and "1" root cells. The model then only
|
||||
* writes its own cells, as in the web app (wrapWithMxFile in lib/utils.ts).
|
||||
* Root cells the model wrote anyway are replaced, and trailing closing tags
|
||||
* some providers append are dropped. <mxfile>, <mxGraphModel> and anything
|
||||
* else are returned unchanged.
|
||||
* Root cells the model wrote anyway are replaced, and comments or text
|
||||
* before the first cell and trailing closing tags some providers append
|
||||
* are dropped. <mxfile>, <mxGraphModel> and anything else are returned
|
||||
* unchanged.
|
||||
*/
|
||||
export function wrapCellsInModel(xml: string): string {
|
||||
let content = stripXmlDeclaration(xml.trim())
|
||||
if (!/^<(mxCell|UserObject|object|root)[\s/>]/.test(content)) return xml
|
||||
const start = content.search(/<(mxCell|UserObject|object|root)[\s/>]/)
|
||||
if (start === -1) return xml
|
||||
// Only comments and plain text may come before the first cell
|
||||
if (!/^(?:<!--[\s\S]*?-->|[^<])*$/.test(content.slice(0, start))) {
|
||||
return xml
|
||||
}
|
||||
|
||||
content = content.replace(/<\/?root>/g, "").trim()
|
||||
content = content
|
||||
.slice(start)
|
||||
.replace(/<\/?root>/g, "")
|
||||
.trim()
|
||||
// End of the last cell, counting wrapped cells (</UserObject>, </object>)
|
||||
let end = -1
|
||||
for (const close of ["/>", "</mxCell>", "</UserObject>", "</object>"]) {
|
||||
|
||||
Reference in New Issue
Block a user