- commit
- a9c93ddde6fc5416d1f31f79bb14460ac8dd6806
- parent
- d3993ef265a3feab55aebb8fc83abf37dcc808e1
- author
- Dami <dami.etoile@gmail.com>
@@ -8,7 +8,7 @@ const CAPABILITIES = Object.freeze({
88 pipeline: ['diagramsnet'],99 frame: '/index.html?v=1',1010 knownLosses: [11- 'SVG is the client preview format; server-side static rendering remains on the compatibility origin.',11+ 'Rendering requires the sandboxed browser exporter; there is no server-side rendering fallback.',1212 'External resources are blocked; embedded data images remain supported.',1313 ],1414 })@@ -37,7 +37,7 @@ build, and pipeline with `X-Diagram-Unit`, `X-Renderer-Build`, and
3737 3838 Browser-rendered units expose the same health and capability routes plus one3939 sandboxed frame. The frame accepts only its own engine over the versioned40-postMessage protocol. Mermaid, BPMN, and Excalidraw have separate packages,40+postMessage protocol. Mermaid, BPMN, Excalidraw, and diagrams.net have separate packages,4141 bundles, CSPs, deployments, and subdomains.4242 4343 A unit may expose multiple catalog hostnames when those engines share the same@@ -61,10 +61,10 @@ catalog engine.
6161 | Worker JavaScript | Bytefield, Nomnoml, Vega family (Vega and Vega-Lite → Vega), WaveDrom |6262 | Worker Python | BlockDiag family (BlockDiag, SeqDiag, ActDiag, NwDiag, PacketDiag, RackDiag); WireViz → DOT → GraphViz |6363 | Worker WebAssembly | PlantUML family (PlantUML and bounded C4 → PlantUML); GraphViz family (GraphViz, DBML → DOT → GraphViz, and ERD → DOT → GraphViz); GoAT; Pikchr; Svgbob |64-| Sandboxed browser unit | Mermaid, BPMN, Excalidraw |65-| Compatibility unit | 7 dependency units covering the remaining 7 engines |64+| Sandboxed browser unit | Mermaid, BPMN, Excalidraw, diagrams.net |65+| Compatibility unit | 6 dependency units covering the remaining 6 engines |6666 67-The current split is 23/30 engines off Fly and 7/30 still dependent on it.67+The current split is 24/30 engines off Fly and 6/30 still dependent on it.6868 Compatibility units are extraction seams, not final runtimes or fallbacks for6969 an engine after cutover. DBML retains the upstream parser/checker/DOT model in7070 an explicitly rebuilt Worker bundle, then shares the GraphViz-Wasm runtime.@@ -12,7 +12,6 @@ const configs = [
1212 'wrangler.units/goat.jsonc',1313 'wrangler.plantuml-family.jsonc',1414 'wrangler.compatibility/d2.jsonc',15- 'wrangler.compatibility/diagramsnet.jsonc',1615 'wrangler.compatibility/ditaa.jsonc',1716 'wrangler.compatibility/structurizr.jsonc',1817 'wrangler.compatibility/symbolator.jsonc',@@ -57,7 +57,23 @@ const goatEngines = new Set(['goat'])
5757 const wirevizEngines = new Set(['wireviz'])5858 const plantumlEngines = new Set(['plantuml', 'c4plantuml'])5959 60+async function smokeClientUnit(engine) {61+ const base = `https://${engine}.render.diagram.zip`62+ const [capabilitiesResponse, frameResponse] = await Promise.all([63+ fetch(`${base}/v1/capabilities`),64+ fetch(`${base}/index.html`),65+ ])66+ if (!capabilitiesResponse.ok) throw new Error(`${engine}: capabilities HTTP ${capabilitiesResponse.status}`)67+ if (!frameResponse.ok) throw new Error(`${engine}: frame HTTP ${frameResponse.status}`)68+ const capabilities = await capabilitiesResponse.json()69+ if (capabilities.id !== engine || capabilities.runtime !== 'client' || capabilities.format !== 'svg') {70+ throw new Error(`${engine}: unexpected client capabilities ${JSON.stringify(capabilities)}`)71+ }72+ return `${engine.padEnd(10)} client ${capabilities.build}`73+}74+6075 async function smoke([engine, filename]) {76+ if (engine === 'diagramsnet') return smokeClientUnit(engine)6177 const source = await readFile(resolve(fixtureDirectory, filename), 'utf8')6278 const endpoint = `https://${engine}.render.diagram.zip/v1/svg`6379 const response = await fetch(endpoint, {@@ -154,4 +170,4 @@ for (let index = 0; index < entries.length; index += 4) {
154170 results.push(...await Promise.all(entries.slice(index, index + 4).map(smoke)))155171 }156172 for (const result of results) console.log(result)157-console.log(`${results.length}/${results.length} catalog engines returned SVG through dependency-backed units`)173+console.log(`${results.length}/${results.length} catalog engines passed structural unit smoke`)@@ -48,6 +48,7 @@ const activeRuntime: Partial<Record<EngineId, EngineRuntime>> = {
4848 mermaid: 'client',4949 bpmn: 'client',5050 bytefield: 'edge-js',51+ diagramsnet: 'client',5152 excalidraw: 'client',5253 nomnoml: 'edge-js',5354 vega: 'edge-js',@@ -73,6 +74,7 @@ const versions: Partial<Record<EngineId, string>> = {
7374 c4plantuml: 'c4plantuml@2.7.0-lowered+plantuml@1.2026.6/edge-wasm-1',7475 mermaid: 'mermaid@11.17.0',7576 bpmn: 'bpmn-js@18.25.1',77+ diagramsnet: 'diagrams.net@29.6.1/client-unit-1',7678 bytefield: 'bytefield-svg@1.11.0',7779 excalidraw: '@excalidraw/excalidraw@0.18.1',7880 nomnoml: 'nomnoml@1.7.0',@@ -135,6 +137,10 @@ const losses: Partial<Record<EngineId, readonly string[]>> = {
135137 'DBML URLs are lowered to GraphViz HREFs, then external links are removed by SVG sanitization.',136138 'The translated graph uses GraphViz 15.1.1 versus the compatibility DBML image Viz.js/GraphViz 2.47.0; output differences are possible.',137139 ],140+ diagramsnet: [141+ 'Rendering runs in the sandboxed official diagrams.net browser exporter; there is no server-side or Fly fallback.',142+ 'External resources are blocked; embedded data images remain supported.',143+ ],138144 excalidraw: [139145 'External resources are blocked; image data must be embedded in the scene.',140146 'Font subsetting is disabled by the sandbox policy, so exports embed the complete self-hosted font.',@@ -62,6 +62,13 @@ describe('engine catalog', () => {
6262 }6363 })6464 65+ it('marks diagrams.net as an official browser renderer without origin fallback', () => {66+ const diagramsnet = ENGINE_CATALOG.find(engine => engine.id === 'diagramsnet')!67+ expect(diagramsnet.activeRuntime).toBe('client')68+ expect(diagramsnet.version).toContain('diagrams.net@29.6.1')69+ expect(diagramsnet.fallback).toBeNull()70+ })71+6572 it('does not retain a silent origin fallback for any engine', () => {6673 for (const engine of ENGINE_CATALOG) {6774 if (engine.activeRuntime === 'origin') expect(engine.version).toBe('compatibility-origin')@@ -11,7 +11,6 @@ export const HTTP_RENDERER_UNITS = Object.freeze({
1111 rackdiag: 'https://rackdiag.render.diagram.zip/v1/svg',1212 bytefield: 'https://bytefield.render.diagram.zip/v1/svg',1313 dbml: 'https://dbml.render.diagram.zip/v1/svg',14- diagramsnet: 'https://diagramsnet.render.diagram.zip/v1/svg',1514 ditaa: 'https://ditaa.render.diagram.zip/v1/svg',1615 erd: 'https://erd.render.diagram.zip/v1/svg',1716 goat: 'https://goat.render.diagram.zip/v1/svg',@@ -29,7 +28,7 @@ export const HTTP_RENDERER_UNITS = Object.freeze({
2928 })3029 3130 export const NO_GATEWAY_FALLBACK_ENGINES = Object.freeze([32- 'mermaid', 'bpmn', 'excalidraw',31+ 'mermaid', 'bpmn', 'excalidraw', 'diagramsnet',3332 'plantuml', 'c4plantuml',3433 'bytefield', 'nomnoml', 'vega', 'vegalite', 'wavedrom',3534 'blockdiag', 'seqdiag', 'actdiag', 'nwdiag', 'packetdiag', 'rackdiag',@@ -102,3 +102,27 @@ test('does not send a failed client renderer to the gateway', async () => {
102102 assert.equal(gatewayCalls, 0)103103 assert.equal(statuses.at(-1).state, 'error')104104 })105+106+test('does not send a failed diagrams.net renderer to the gateway', async () => {107+ let gatewayCalls = 0108+ const statuses = []109+ const controller = {110+ abortController: null,111+ stage: { style: { setProperty() {} } },112+ setStatus(message, state) { statuses.push({ message, state }) },113+ async renderThroughGateway() { gatewayCalls += 1 },114+ }115+116+ await PreviewController.prototype.performRender.call(controller, {117+ type: 'diagramsnet',118+ source: '<mxGraphModel/>',119+ options: {},120+ meta: {},121+ presentation: {},122+ renderKey: 'diagramsnet-client-failure',123+ requestNumber: 1,124+ })125+126+ assert.equal(gatewayCalls, 0)127+ assert.equal(statuses.at(-1).state, 'error')128+})@@ -3,7 +3,7 @@ import test from 'node:test'
33 import { HTTP_RENDERER_UNITS, httpRendererUnitFor, requiresDedicatedRenderer } from '../src/renderer-units.js'44 55 test('gives each HTTP renderer its own origin', () => {6- assert.equal(Object.keys(HTTP_RENDERER_UNITS).length, 27)6+ assert.equal(Object.keys(HTTP_RENDERER_UNITS).length, 26)77 for (const [engine, endpoint] of Object.entries(HTTP_RENDERER_UNITS)) {88 assert.equal(new URL(endpoint).hostname, `${engine}.render.diagram.zip`)99 assert.equal(new URL(endpoint).pathname, '/v1/svg')@@ -16,11 +16,12 @@ test('does not route client renderers through an HTTP unit', () => {
1616 assert.equal(httpRendererUnitFor('mermaid'), null)1717 assert.equal(httpRendererUnitFor('bpmn'), null)1818 assert.equal(httpRendererUnitFor('excalidraw'), null)19+ assert.equal(httpRendererUnitFor('diagramsnet'), null)1920 })2021 2122 test('requires every migrated engine to stay on its dedicated runtime', () => {2223 for (const engine of [23- 'mermaid', 'bpmn', 'excalidraw',24+ 'mermaid', 'bpmn', 'excalidraw', 'diagramsnet',2425 'plantuml', 'c4plantuml',2526 'bytefield', 'nomnoml', 'vega', 'vegalite', 'wavedrom',2627 'blockdiag', 'seqdiag', 'actdiag', 'nwdiag', 'packetdiag', 'rackdiag',