diagrams-js 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/diagrams-js/diagram-diff/SKILL.md +11 -11
- package/skills/diagrams-js/json-serialization/SKILL.md +4 -4
- package/skills/diagrams-js/rendering-export/SKILL.md +3 -3
- package/skills/diagrams-js/svg-serialization/SKILL.md +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -796,9 +796,9 @@ interface DiagramNodeJSON {
|
|
|
796
796
|
/** Cloud provider identifier (e.g., 'aws', 'gcp', 'azure') */
|
|
797
797
|
provider?: string;
|
|
798
798
|
/** Service type within the provider (e.g., 'compute', 'storage') */
|
|
799
|
-
service?: string;
|
|
800
|
-
/** Specific resource type (e.g., 'EC2', 'S3', 'RDS') */
|
|
801
799
|
type?: string;
|
|
800
|
+
/** Specific resource type (e.g., 'EC2', 'S3', 'RDS') */
|
|
801
|
+
resource?: string;
|
|
802
802
|
/** Custom icon URL or data URI */
|
|
803
803
|
iconUrl?: string;
|
|
804
804
|
/** Additional Graphviz attributes */
|
|
@@ -921,8 +921,8 @@ interface DiagramJSON {
|
|
|
921
921
|
* name: "My Architecture",
|
|
922
922
|
* direction: "LR",
|
|
923
923
|
* nodes: [
|
|
924
|
-
* { id: "web", label: "Web Server", provider: "aws",
|
|
925
|
-
* { id: "db", label: "Database", provider: "aws",
|
|
924
|
+
* { id: "web", label: "Web Server", provider: "aws", type: "compute", resource: "EC2" },
|
|
925
|
+
* { id: "db", label: "Database", provider: "aws", type: "database", resource: "RDS" }
|
|
926
926
|
* ],
|
|
927
927
|
* edges: [
|
|
928
928
|
* { from: "web", to: "db", label: "SQL" }
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ var t=(()=>{var e=import.meta.url;return(function(t={}){var n,r=t,i,a,o=new Prom
|
|
|
12
12
|
<svg xmlns="http://www.w3.org/2000/svg" width="${t.width}" height="${t.height}"></svg>
|
|
13
13
|
`;return e.FS.createPath(`/`,e.PATH.dirname(n)),e.FS.writeFile(n,r),n}):[]}function u(e,t){for(let n of t)e.FS.analyzePath(n).exists&&e.FS.unlink(n)}function d(e,t,n){let r;try{let n=e.lengthBytesUTF8(t);return r=e.ccall(`malloc`,`number`,[`number`],[n+1]),e.stringToUTF8(t,r,n+1),e.ccall(`viz_read_one_graph`,`number`,[`number`],[r])}finally{r&&e.ccall(`free`,`number`,[`number`],[r])}}function f(e,t,n){let r=e.ccall(`viz_create_graph`,`number`,[`string`,`number`,`number`],[t.name,t.directed===void 0?!0:t.directed,t.strict===void 0?!1:t.strict]);return p(e,r,t),r}function p(e,t,n){m(e,t,n),n.nodes&&n.nodes.forEach(n=>{if(n.name===void 0)throw Error(`nodes must have a name`);let r=e.ccall(`viz_add_node`,`number`,[`number`,`string`],[t,String(n.name)]);n.attributes&&h(e,t,r,n.attributes)}),n.edges&&n.edges.forEach(n=>{if(n.tail===void 0)throw Error(`edges must have a tail`);if(n.head===void 0)throw Error(`edges must have a head`);let r=e.ccall(`viz_add_edge`,`number`,[`number`,`string`,`string`],[t,String(n.tail),String(n.head)]);n.attributes&&h(e,t,r,n.attributes)}),n.subgraphs&&n.subgraphs.forEach(n=>{p(e,e.ccall(`viz_add_subgraph`,`number`,[`number`,`string`],[t,n.name===void 0?0:String(n.name)]),n)})}function m(e,t,n){if(n.graphAttributes)for(let[r,i]of Object.entries(n.graphAttributes))g(e,t,i,n=>{e.ccall(`viz_set_default_graph_attribute`,`number`,[`number`,`string`,`number`],[t,r,n])});if(n.nodeAttributes)for(let[r,i]of Object.entries(n.nodeAttributes))g(e,t,i,n=>{e.ccall(`viz_set_default_node_attribute`,`number`,[`number`,`string`,`number`],[t,r,n])});if(n.edgeAttributes)for(let[r,i]of Object.entries(n.edgeAttributes))g(e,t,i,n=>{e.ccall(`viz_set_default_edge_attribute`,`number`,[`number`,`string`,`number`],[t,r,n])})}function h(e,t,n,r){for(let[i,a]of Object.entries(r))g(e,t,a,t=>{e.ccall(`viz_set_attribute`,`number`,[`number`,`string`,`number`],[n,i,t])})}function g(e,t,n,r){let i;if(i=typeof n==`object`&&`html`in n?e.ccall(`viz_string_dup_html`,`number`,[`number`,`string`],[t,String(n.html)]):e.ccall(`viz_string_dup`,`number`,[`number`,`string`],[t,String(n)]),i==0)throw Error(`couldn't dup string`);r(i),typeof n==`object`&&`html`in n?e.ccall(`viz_string_free_html`,`number`,[`number`,`number`],[t,i]):e.ccall(`viz_string_free`,`number`,[`number`,`number`],[t,i])}var _=class{constructor(e){this.module=e}get graphvizVersion(){return a(this.module)}get formats(){return o(this.module,`device`)}get engines(){return o(this.module,`layout`)}renderFormats(e,t,n={}){return s(this.module,e,t,{engine:`dot`,...n})}render(e,t={}){let n;n=t.format===void 0?`dot`:t.format;let r=s(this.module,e,[n],{engine:`dot`,...t});return r.status===`success`&&(r.output=r.output[n]),r}renderString(e,t={}){let n=this.render(e,t);if(n.status!==`success`)throw Error(n.errors.find(e=>e.level==`error`)?.message||`render failed`);return n.output}renderSVGElement(e,t={}){let n=this.renderString(e,{...t,format:`svg`}),r;return r=t.trustedTypePolicy===void 0?n:t.trustedTypePolicy.createHTML(n),new DOMParser().parseFromString(r,`image/svg+xml`).documentElement}renderJSON(e,t={}){let n=this.renderString(e,{...t,format:`json`});return JSON.parse(n)}};function v(){return t().then(e=>new _(e))}var y=class{stack=[];get(){return this.stack[this.stack.length-1]}push(e){this.stack.push(e)}pop(){return this.stack.pop()}};const b=new y;new y;function x(e){b.push(e)}const S={shape:`box`,style:`rounded`,labeljust:`l`,fontname:`Sans-Serif`,fontsize:`12`};function C(e=`cluster`,t=`LR`,n,r,i){let a=new Map,o=[],s=`cluster_${e.replace(/\s+/g,`_`)}`;if(!r)throw Error(`Cluster must be created through diagram.cluster() or cluster.cluster()`);let c=r.themeConfig,l={...S};l.label=e,l.pencolor=c.pencolor;let u=i?i.depth+1:0,d=c.bgcolor;l.bgcolor=d[u%d.length],n&&Object.assign(l,n);let f={label:e,name:s,depth:u,graphAttr:l,get clusterAttrs(){return l},set clusterAttrs(e){for(let e of Object.keys(l))delete l[e];Object.assign(l,e)},get diagram(){return r},"~node"(e,t,n){a.set(e,{label:t,attrs:n})},add(e){return e[`~register`](f),e},subgraph(e){o.push(e)},cluster(e){let t=C(e,`LR`,void 0,r,f);return o.push(t),t},getNodes(){return a},getSubgraphs(){return o},getParent(){return i},getDiagram(){return r}};return f}const w={pastel:{bgcolor:[`#E5F5FD`,`#EBF3E7`,`#ECE8F6`,`#FDF7E3`],pencolor:`#AEB6BE`,edgecolor:`#7B8894`},neutral:{bgcolor:[`#F8F9FA`,`#F1F3F5`,`#E9ECEF`,`#DEE2E6`],pencolor:`#ADB5BD`,edgecolor:`#495057`},blues:{bgcolor:[`#E7F5FF`,`#D0EBFF`,`#A5D8FF`,`#74C0FC`],pencolor:`#339AF0`,edgecolor:`#1971C2`},greens:{bgcolor:[`#EBFBEE`,`#D3F9D8`,`#B2F2BB`,`#8CE99A`],pencolor:`#40C057`,edgecolor:`#2F9E44`},orange:{bgcolor:[`#FFF4E6`,`#FFE8CC`,`#FFD8A8`,`#FFC078`],pencolor:`#FD7E14`,edgecolor:`#E8590C`}};function T(){return typeof window<`u`&&typeof document<`u`}function E(e){let t=e.match(/translate\(\s*([^,\s]+)[,\s]+([^\s)]+)\s*\)/);return t?{x:parseFloat(t[1]),y:parseFloat(t[2])}:null}function D(e){let t=e.match(/transform="([^"]+)"/);if(t){let e=E(t[1]);if(e)return e}let n=e.match(/<ellipse[^>]+cx="([^"]+)"[^>]+cy="([^"]+)"/);if(n)return{x:parseFloat(n[1]),y:parseFloat(n[2])};let r=e.match(/<polygon[^>]+points="([^"]+)"/);if(r){let e=r[1].match(/[-\d.]+/g);if(e&&e.length>=4){let t=0,n=0,r=0;for(let i=0;i<e.length;i+=2)t+=parseFloat(e[i]),n+=parseFloat(e[i+1]),r++;return{x:t/r,y:n/r}}}let i=e.match(/<path[^>]+d="([^"]+)"/);if(i){let e=i[1].match(/[-\d.]+/g);if(e&&e.length>=4){let t=[],n=[];for(let r=0;r<e.length;r+=2){let i=parseFloat(e[r]),a=parseFloat(e[r+1]);!isNaN(i)&&!isNaN(a)&&(t.push(i),n.push(a))}if(t.length>0&&n.length>0)return{x:(Math.min(...t)+Math.max(...t))/2,y:(Math.min(...n)+Math.max(...n))/2}}}let a=e.match(/<text[^>]*\sx="([^"]+)"/),o=e.match(/<text[^>]*\sy="([^"]+)"/);if(a&&o){let t=e.match(/font-size="([^"]+)"/),n=t?parseFloat(t[1]):13;return{x:parseFloat(a[1]),y:parseFloat(o[1])-n-32}}return null}function O(e){let t=e.match(/<title>([^<]*)<\/title>/);return t?t[1].replace(/&#(\d+);/g,(e,t)=>String.fromCharCode(parseInt(t,10))):null}function k(e){let t=0;for(let n=0;n<e.length;n++){let r=e.charCodeAt(n);t=(t<<5)-t+r,t&=t}return`icon-${Math.abs(t).toString(36)}`}function A(e,t,n){return!n||Object.keys(n).length===0||t.length===0?e:T()?ee(e,t,n):te(e,t,n)}function ee(e,t,n){let r=new DOMParser().parseFromString(e,`image/svg+xml`).querySelector(`svg`);if(!r)return console.warn(`No SVG element found`),e;let i=r.querySelector(`defs`);i||(i=document.createElementNS(`http://www.w3.org/2000/svg`,`defs`),r.insertBefore(i,r.firstChild));let a=new Map;return r.querySelectorAll(`g.node`).forEach(e=>{let r=e.querySelector(`title`);if(!r)return;let o=r.textContent,s=t.find(e=>e.node.nodeId===o);if(!s||!n[s.icon])return;let c=n[s.icon],l;if(a.has(c))l=a.get(c);else{l=k(c),a.set(c,l);let e=document.createElementNS(`http://www.w3.org/2000/svg`,`g`);e.setAttribute(`id`,l);let t=document.createElementNS(`http://www.w3.org/2000/svg`,`image`);t.setAttribute(`x`,`-24`),t.setAttribute(`y`,`-24`),t.setAttribute(`width`,`48`),t.setAttribute(`height`,`48`),t.setAttribute(`href`,c),t.setAttribute(`preserveAspectRatio`,`xMidYMid meet`),e.appendChild(t),i.appendChild(e)}let u,d,f=e.getAttribute(`transform`);if(f&&f!==`null`){let e=f.match(/translate\(\s*([^,\s]+)[,\s]+([^\s)]+)\s*\)/);if(!e)return;u=parseFloat(e[1]),d=parseFloat(e[2])}else{let t=e.querySelector(`ellipse`),n=e.querySelector(`polygon`),r=e.querySelector(`path`),i=e.querySelector(`text`);if(t){let e=t.getAttribute(`cx`),n=t.getAttribute(`cy`);if(!e||!n)return;u=parseFloat(e),d=parseFloat(n)}else if(n){let e=n.getAttribute(`points`);if(!e)return;let t=e.match(/[-\d.]+/g);if(!t||t.length<4)return;let r=0,i=0,a=0;for(let e=0;e<t.length;e+=2)r+=parseFloat(t[e]),i+=parseFloat(t[e+1]),a++;u=r/a,d=i/a}else if(r){let e=r.getAttribute(`d`);if(!e)return;let t=e.match(/[-\d.]+/g);if(!t||t.length<4)return;let n=[],i=[];for(let e=0;e<t.length;e+=2){let r=parseFloat(t[e]),a=parseFloat(t[e+1]);!isNaN(r)&&!isNaN(a)&&(n.push(r),i.push(a))}if(n.length===0||i.length===0)return;u=(Math.min(...n)+Math.max(...n))/2,d=(Math.min(...i)+Math.max(...i))/2}else if(i){let e=i.getAttribute(`x`),t=i.getAttribute(`y`),n=i.getAttribute(`font-size`);if(!e||!t)return;u=parseFloat(e),d=parseFloat(t)-(n?parseFloat(n):13)-32}else return}let p=document.createElementNS(`http://www.w3.org/2000/svg`,`use`);p.setAttribute(`href`,`#${l}`),p.setAttribute(`x`,String(u)),p.setAttribute(`y`,String(d+6)),e.appendChild(p)}),new XMLSerializer().serializeToString(r)}function te(e,t,n){let r=new Map,i=[],a=/<g[^>]*class="node"[^>]*>([\s\S]*?)<\/g>/g,o=e,s,c=[];for(;(s=a.exec(e))!==null;){let e=s[0],a=O(e);if(!a)continue;let o=t.find(e=>e.node.nodeId===a);if(!o||!n[o.icon])continue;let l=n[o.icon],u;if(r.has(l))u=r.get(l);else{u=k(l),r.set(l,u);let e=`<g id="${u}"><image x="-24" y="-24" width="48" height="48" href="${l}" preserveAspectRatio="xMidYMid meet"/></g>`;i.push(e)}let d=D(e);if(!d)continue;let f=`<use href="#${u}" x="${d.x}" y="${d.y+6}"/>`,p=e.replace(/<\/g>$/,`${f}</g>`);c.push({original:e,replacement:p})}for(let{original:e,replacement:t}of c)o=o.replace(e,t);if(i.length>0){let e=`<defs>\n${i.join(`
|
|
14
14
|
`)}\n</defs>`;o=o.replace(/(<svg[^>]*>)/,`$1\n${e}`)}return o}function j(e={}){let t=e.node,n=e.forward??!1,r=e.reverse??!1,i={};e.label&&(i.label=e.label),e.color&&(i.color=e.color),e.style&&(i.style=e.style);for(let[t,n]of Object.entries(e))t!==`node`&&t!==`forward`&&t!==`reverse`&&t!==`label`&&t!==`color`&&t!==`style`&&(i[t]=String(n));let a={get node(){return t},set node(e){t=e},get forward(){return n},set forward(e){n=e},get reverse(){return r},set reverse(e){r=e},get attrs(){let e;return e=n&&r?`both`:n?`forward`:r?`back`:`none`,{...i,dir:e}},to(e){return n=!0,ne(e)?(Object.assign(i,e.attrs),a):t?(t[`~connect`](e,a),e):(t=e,e)},get edgeAttrs(){return i},set edgeAttrs(e){for(let e of Object.keys(i))delete i[e];Object.assign(i,e)},from(e){return r=!0,ne(e)?(Object.assign(i,e.attrs),a):t?(e[`~connect`](t,a),e):(t=e,e)}};return a}function ne(e){return typeof e==`object`&&!!e&&`attrs`in e&&`forward`in e&&`reverse`in e}function re(e){return j({label:e})}function M(e){return j({color:e})}function ie(e){return j({style:e})}j.label=re,j.color=M,j.style=ie;function ae(e){return typeof e==`object`&&!!e&&`attrs`in e&&`forward`in e&&`reverse`in e}function oe(){return`${Date.now().toString(36)}_${Math.random().toString(36).substr(2,9)}`}function N(e=``,t={}){let n=t.nodeId??oe(),r=null,i,a={},o=null,s={},c=t[`~iconDataUrl`];c&&(o=c);let l={shape:t.shape!==void 0,height:t.height!==void 0,width:t.width!==void 0,fixedsize:t.fixedsize!==void 0,margin:t.margin!==void 0,labelloc:t.labelloc!==void 0,imagescale:t.imagescale!==void 0};l.shape?a.shape=String(t.shape):o&&(a.shape=`none`);for(let[e,n]of Object.entries(t))e.startsWith(`~`)||e===`nodeId`||e===`shape`||(a[e]=String(n));o&&Object.assign(a,{height:`0.9`,width:`0.8`,fixedsize:`true`,margin:`0,0`,labelloc:`b`,imagescale:`true`});let u={label:e,"~id":n,"~diagram":null,"~cluster":void 0,"~attrs":a,get"~iconDataUrl"(){return o},set"~iconDataUrl"(e){o=e},get nodeId(){return n},get nodeAttrs(){return a},set nodeAttrs(e){for(let e of Object.keys(a))delete a[e];Object.assign(a,e)},get provider(){return u[`~provider`]},set provider(e){u[`~provider`]=e},get type(){return u[`~type`]},set type(e){u[`~type`]=e},get resource(){return u[`~resource`]},set resource(e){u[`~resource`]=e},get metadata(){return s},set metadata(e){s=e},get cluster(){return i},"~register"(o){if(P(o)?(i=o,r=o.diagram):r=o,r[`~trackNode`](this),r&&r.autolabel){let n=t[`~type`]??`Node`;e?u.label=n+`
|
|
15
|
-
`+e:u.label=n}let s=u[`~iconDataUrl`];if(s&&(l.shape||(r[`~userNodeAttr`]?.shape?a.shape=r[`~userNodeAttr`].shape:a.shape=`none`),l.height||(r[`~userNodeAttr`]?.height?a.height=r[`~userNodeAttr`].height:a.height=`0.9`),l.width||(r[`~userNodeAttr`]?.width?a.width=r[`~userNodeAttr`].width:a.width=`0.8`),l.fixedsize||(r[`~userNodeAttr`]?.fixedsize?a.fixedsize=r[`~userNodeAttr`].fixedsize:a.fixedsize=`true`),l.margin||(r[`~userNodeAttr`]?.margin?a.margin=r[`~userNodeAttr`].margin:a.margin=`0,0`),l.labelloc||(r[`~userNodeAttr`]?.labelloc?a.labelloc=r[`~userNodeAttr`].labelloc:a.labelloc=`b`),l.imagescale||(r[`~userNodeAttr`]?.imagescale?a.imagescale=r[`~userNodeAttr`].imagescale:a.imagescale=`true`),r[`~trackNodeWithIcon`](u,s)),P(o),o[`~node`](n,u.label,a),se(this)&&r){let e=ce(this,r);r[`~trackPendingIconLoad`](e)}},to(e,t){if(ae(e)){if(e.node=u,e.forward=!0,t===void 0)return e;let n=t;if(Array.isArray(n)){for(let t of n){let n=j({...e.attrs,node:u,forward:!0});u[`~connect`](t,n)}return n}return u[`~connect`](n,e)}let n=e;if(Array.isArray(n)){for(let e of n){let t=j({node:u,forward:!0});u[`~connect`](e,t)}return n}let r=j({node:u,forward:!0});return u[`~connect`](n,r)},from(e,t){if(ae(e)){e.node=u,e.reverse=!0;let n=t;if(Array.isArray(n)){for(let t of n){let n=j({...e.attrs,node:u,reverse:!0});u[`~connect`](t,n)}return u}return u[`~connect`](n,e)}let n=e;if(Array.isArray(n)){for(let e of n){let t=j({node:e,reverse:!0});u[`~connect`](e,t)}return u}let r=j({node:n,reverse:!0});return u[`~connect`](n,r)},with(e,t){if(ae(e)){e.node=u;let n=t;if(Array.isArray(n)){for(let t of n){let n=j({...e.attrs,node:u});u[`~connect`](t,n)}return n}return u[`~connect`](n,e)}let n=e,r=j({node:u});if(Array.isArray(n)){for(let e of n)u[`~connect`](e,r);return n}return u[`~connect`](n,r)},"~connect"(e,t){if(!e||typeof e!=`object`||!(`nodeId`in e))throw Error(`${String(e)} is not a valid Node`);if(!t||typeof t!=`object`||!(`attrs`in t))throw Error(`${String(t)} is not a valid Edge`);if(!r)throw Error(`Node is not registered with a diagram`);return r[`~connect`](u,e,t),e}};return u}function P(e){return typeof e==`object`&&!!e&&`getNodes`in e&&typeof e.getNodes==`function`}function se(e){return`~getIconUrl`in e&&typeof e[`~getIconUrl`]==`function`}async function ce(e,t){let n=e,r=n[`~getIconUrl`]();if(r.startsWith(`data:`)){t[`~trackNodeWithIcon`](e,r);return}try{let r=await n.loadIcon();r&&t[`~trackNodeWithIcon`](e,r)}catch(t){console.warn(`Failed to load custom icon for node ${e.nodeId}:`,t)}}const le=new Map;function F(e,t,n){let r=t.startsWith(`data:`);return{...N(e,{shape:`none`,height:`0.9`,width:`0.8`,fixedsize:`true`,margin:`0,0`,labelloc:`b`,imagescale:`true`,...n,...r?{"~iconDataUrl":t}:{}}),"~iconUrl":t,get"~iconDataUrl"(){return t.startsWith(`data:`)?t:null},set"~iconDataUrl"(e){},"~getIconUrl"(){return t},async loadIcon(){if(ue(t))return t;if(le.has(t))return le.get(t);let e;return e=de(t)?fe(t):pe(t),le.set(t,e),e.then(()=>{}).catch(()=>{le.delete(t)}),e}}}function ue(e){return e.startsWith(`data:`)}function de(e){return e.startsWith(`http://`)||e.startsWith(`https://`)}async function fe(e){try{let t=new AbortController,n=setTimeout(()=>t.abort(),1e4),r=await globalThis.fetch(e,{signal:t.signal});if(clearTimeout(n),!r.ok)return console.warn(`Failed to fetch icon: ${e}`),null;if(typeof Blob<`u`&&typeof FileReader<`u`)return me(await r.blob());let i=await r.arrayBuffer(),a=Buffer.from(i);return`data:${r.headers.get(`content-type`)||`image/png`};base64,${a.toString(`base64`)}`}catch(t){return console.warn(`Error fetching icon: ${e}`,t),null}}async function pe(e){try{let t=await import(`node:fs/promises`),n=await import(`node:path`),r=await t.readFile(e),i=n.extname(e).toLowerCase(),a=`image/png`;return i===`.svg`?a=`image/svg+xml`:i===`.jpg`||i===`.jpeg`?a=`image/jpeg`:i===`.gif`?a=`image/gif`:i===`.webp`&&(a=`image/webp`),`data:${a};base64,${r.toString(`base64`)}`}catch(t){return console.warn(`Error loading local icon: ${e}`,t),null}}function me(e){return new Promise((t,n)=>{let r=new FileReader;r.onloadend=()=>t(r.result),r.onerror=n,r.readAsDataURL(e)})}function he(e,t){return F(e,`https://api.iconify.design/${t}.svg`)}let I=null;async function ge(e,t){let n=e=>e.filter(e=>e!=null).join(`/`);if(I===`relative`||!I&&!import.meta.url.endsWith(`.js`))try{let r=await import(n([`.`,e,t]));return I=`relative`,r}catch{}if(I===`direct`||!I)try{let r=await import(n([`.`,`providers`,e,t])+`.js`);return I=`direct`,r}catch{}if(I===`bare`||!I)try{let r=await import(n([`diagrams-js`,e,t]));return I=`bare`,r}catch{}if(I===`cdn`||!I)try{let r=await import(n([`https://esm.sh`,`diagrams-js`,e,t]));return I=`cdn`,r}catch{}return null}async function _e(e){let t=new Map;for(let[n,r]of e){let e=await ge(n,r);if(e)for(let[n,r]of Object.entries(e))typeof r==`function`&&!n.startsWith(`_`)&&t.set(n,r)}return t}async function ve(){return await ge(`resources-list`)}async function ye(){return await ge(`yaml`)}const be=new Set([`shape`,`height`,`width`,`fixedsize`,`margin`,`labelloc`,`imagescale`]),xe={shape:`none`,height:`0.9`,width:`0.8`,fixedsize:`true`,margin:`0,0`,labelloc:`b`,imagescale:`true`};function L(e,t,n,r){let i={id:e};if(t&&(i.label=t),r){let e=r,t=e[`~provider`],n=e[`~type`],a=e[`~resource`];t&&(i.provider=t),n&&(i.service=n),a&&(i.type=a),`~getIconUrl`in e&&typeof e[`~getIconUrl`]==`function`&&(i.iconUrl=e[`~getIconUrl`]()),r.metadata&&Object.keys(r.metadata).length>0&&(i.metadata=r.metadata)}let a=i.provider!=null,o=i.iconUrl!=null,s=a||o,c={};for(let[e,t]of Object.entries(n))if(!e.startsWith(`~`)){if(s&&be.has(e)){let n=xe[e];if(n!==void 0&&String(t)===n)continue}c[e]=typeof t==`number`?t:String(t)}return Object.keys(c).length>0&&(i.attrs=c),i}function R(e,t,n){let r={label:e.label},i=[];for(let[r,{label:a,attrs:o}]of e.getNodes())i.push(r),t.push(L(r,a,o,n.get(r)));i.length>0&&(r.nodes=i);let a={};for(let[t,n]of Object.entries(e.graphAttr))t===`shape`||t===`style`||t===`labeljust`||t===`fontname`||t===`fontsize`||t===`label`||t===`pencolor`||t===`bgcolor`||(a[t]=n);Object.keys(a).length>0&&(r.graphAttr=a);let o=e.getSubgraphs();return o.length>0&&(r.clusters=o.map(e=>R(e,t,n))),r}function Se(e){return e.map(e=>{let t={from:e.from,to:e.to},{dir:n,label:r,color:i,style:a,...o}=e.attrs;return n&&n!==`forward`&&(t.direction=n),r&&(t.label=r),i&&(t.color=i),a&&(t.style=a),Object.keys(o).length>0&&(t.attrs=o),t})}function Ce(e,t,n,r,i,a,o){let s={nodes:[]};e.name&&(s.name=e.name);let c=e.name?e.name.toLowerCase().replace(/\s+/g,`_`):`diagram`;e.filename!==c&&(s.filename=e.filename),e.direction!==`LR`&&(s.direction=e.direction),e.curveStyle!==`ortho`&&(s.curvestyle=e.curveStyle),e.autolabel&&(s.autolabel=e.autolabel),e.strict&&(s.strict=e.strict),e.theme!==`pastel`&&(s.theme=e.theme),o.graphAttr&&Object.keys(o.graphAttr).length>0&&(s.graphAttr={...o.graphAttr}),o.nodeAttr&&Object.keys(o.nodeAttr).length>0&&(s.nodeAttr={...o.nodeAttr}),o.edgeAttr&&Object.keys(o.edgeAttr).length>0&&(s.edgeAttr={...o.edgeAttr});let l=new Map(a);for(let e of i)l.has(e.node.nodeId)||l.set(e.node.nodeId,e.node);let u=new Set;function d(e){for(let[t]of e.getNodes())u.add(t);for(let t of e.getSubgraphs())d(t)}for(let e of r)d(e);let f=[];for(let[e,{label:n,attrs:r}]of t)u.has(e)||s.nodes.push(L(e,n,r,l.get(e)));return r.length>0&&(s.clusters=r.map(e=>R(e,f,l)),s.nodes.push(...f)),n.length>0&&(s.edges=Se(n)),s}async function z(e,t){let n=typeof e==`string`?JSON.parse(e):e;if(!n.nodes||!Array.isArray(n.nodes))throw Error(`Invalid diagram JSON: 'nodes' array is required`);let r=new Map;for(let e of n.nodes)if(e.provider&&e.service){let t=`${e.provider}/${e.service}`;r.set(t,[e.provider,e.service])}let i=await _e(Array.from(r.values()));if(t?.providers)for(let e of t.providers)for(let[t,n]of Object.entries(e))typeof n==`function`&&!t.startsWith(`_`)&&i.set(t,n);let a={};n.name!==void 0&&(a.name=n.name),n.filename!==void 0&&(a.filename=n.filename),n.direction!==void 0&&(a.direction=n.direction),n.curvestyle!==void 0&&(a.curvestyle=n.curvestyle),n.autolabel!==void 0&&(a.autolabel=n.autolabel),n.strict!==void 0&&(a.strict=n.strict),n.theme!==void 0&&(a.theme=n.theme),n.graphAttr!==void 0&&(a.graphAttr=n.graphAttr),n.nodeAttr!==void 0&&(a.nodeAttr=n.nodeAttr),n.edgeAttr!==void 0&&(a.edgeAttr=n.edgeAttr);let o=Q(n.name??``,a),s=new Map,c=new Map;if(n.clusters){function e(t,n){let r=[...n,t.label];if(t.nodes)for(let e of t.nodes)c.set(e,r);if(t.clusters)for(let n of t.clusters)e(n,r)}for(let t of n.clusters)e(t,[])}let l=new Map;if(n.clusters){function e(t,n){let r=n.cluster(t.label);if(t.graphAttr&&Object.assign(r.graphAttr,t.graphAttr),l.set(t.label,r),t.clusters)for(let n of t.clusters)e(n,r)}for(let t of n.clusters)e(t,o)}let u=new Set;for(let e of n.nodes){if(u.has(e.id))throw Error(`Duplicate node ID: "${e.id}"`);u.add(e.id);let t,n=e.type?i.get(e.type):void 0;if(n)t=n(e.label??``,{nodeId:e.id,...e.attrs});else if(e.iconUrl&&we(e.iconUrl)){let n={nodeId:e.id};e.attrs&&Object.assign(n,e.attrs),t=F(e.label??``,e.iconUrl,n)}else{let n={nodeId:e.id};e.attrs&&Object.assign(n,e.attrs),t=N(e.label??``,n);let r=t;e.provider&&(r[`~provider`]=e.provider),e.service&&(r[`~type`]=e.service),e.type&&(r[`~resource`]=e.type),e.iconUrl&&(r[`~iconDataUrl`]=e.iconUrl)}e.metadata&&(t.metadata=e.metadata);let r=c.get(e.id);if(r&&r.length>0){let e=r[r.length-1],n=l.get(e);n?n.add(t):o.add(t)}else o.add(t);s.set(e.id,t)}if(n.edges)for(let e of n.edges){let t=s.get(e.from),n=s.get(e.to);if(!t)throw Error(`Edge references unknown source node: "${e.from}"`);if(!n)throw Error(`Edge references unknown target node: "${e.to}"`);let r=e.direction??`forward`,i={};e.label&&(i.label=e.label),e.color&&(i.color=e.color),e.style&&(i.style=e.style),e.attrs&&Object.assign(i,e.attrs);let a=j(i);switch(r){case`forward`:a.forward=!0;break;case`back`:a.reverse=!0;break;case`both`:a.forward=!0,a.reverse=!0;break;case`none`:break;default:a.forward=!0}t[`~connect`](n,a)}return o}function we(e){return e.startsWith(`http://`)||e.startsWith(`https://`)}let B=function(e){return e.BEFORE_IMPORT=`before:import`,e.AFTER_IMPORT=`after:import`,e.BEFORE_EXPORT=`before:export`,e.AFTER_EXPORT=`after:export`,e.BEFORE_RENDER=`before:render`,e.AFTER_RENDER=`after:render`,e.BEFORE_SERIALIZE=`before:serialize`,e.AFTER_DESERIALIZE=`after:deserialize`,e.NODE_CREATE=`node:create`,e.EDGE_CREATE=`edge:create`,e.CLUSTER_CREATE=`cluster:create`,e.METADATA_ATTACH=`metadata:attach`,e.METADATA_CALCULATE=`metadata:calculate`,e}({});var V=class extends Error{constructor(e,t,n){super(e),this.pluginName=t,this.cause=n,this.name=`PluginError`}},Te=class extends V{constructor(e,t,n){super(`Plugin "${e}" has unresolved dependencies: ${t.join(`, `)}`,e),this.missingDependencies=t,this.circularDependencies=n,this.name=`DependencyError`}},H=class extends V{constructor(e,t,n){super(`Plugin "${e}" does not support ${n} runtime`,e),this.requiredRuntimes=t,this.currentRuntime=n,this.name=`RuntimeError`}};function Ee(){return globalThis.Deno===void 0?globalThis.Bun===void 0?typeof window<`u`&&typeof document<`u`?`browser`:(typeof process<`u`&&process.versions?.node,`node`):`bun`:`deno`}function De(e){let t=Ee();if(!e[t])throw new H(`unknown`,e,t)}function Oe(e){let t=new Set,n=new Set,r=[],i=[];function a(o,s=[]){if(n.has(o)){let e=s.indexOf(o);i.push(...s.slice(e),o);return}if(t.has(o))return;let c=e.get(o);if(c){n.add(o),s.push(o);for(let e of c.plugin.dependencies||[])a(e,[...s]);n.delete(o),t.add(o),r.push(c)}}for(let[n]of e)t.has(n)||a(n);if(i.length>0)throw new Te(i[0],[],i);return r}function ke(){let e=new Map,t=new Map,n=new Map,r=new Map,i=new Map,a=new Map,o=new Map,s=new Map;function c(){return{registry:f,fetch:globalThis.fetch.bind(globalThis),getPlugin:t=>e.get(t),getImporter:e=>n.get(e),getExporter:e=>r.get(e),getRenderer:e=>i.get(e),getMetadataProvider:e=>a.get(e),executeHooks:async(e,t)=>f.executeHooks(e,t),loadResourcesList:()=>ve(),loadYaml:()=>ye(),lib:Qe}}function l(e){for(let t of e.capabilities)switch(t.type){case`importer`:n.set(t.name,t);break;case`exporter`:r.set(t.name,t);break;case`renderer`:i.set(t.name,t);for(let e of t.formats)i.set(e,t);break;case`metadata`:a.set(t.provider,t);break;case`hook`:for(let{event:n,handler:r,priority:i=0}of t.hooks){let t=o.get(n)||[];t.push({handler:r,priority:i,pluginName:e.name}),t.sort((e,t)=>t.priority-e.priority),o.set(n,t)}break}}function u(e){for(let t of e.capabilities)switch(t.type){case`importer`:n.delete(t.name);break;case`exporter`:r.delete(t.name);break;case`renderer`:i.delete(t.name);for(let e of t.formats)i.delete(e);break;case`metadata`:a.delete(t.provider);break;case`hook`:for(let{event:n}of t.hooks){let t=o.get(n);if(t){let r=t.filter(t=>t.pluginName!==e.name);r.length===0?o.delete(n):o.set(n,r)}}break}}async function d(){if(s.size===0)return;for(let[t,{plugin:n}]of s)if(n.dependencies){let r=n.dependencies.filter(t=>!e.has(t)&&!s.has(t));if(r.length>0)throw new Te(t,r)}let n=Oe(s);for(let{plugin:r,config:i}of n){try{De(r.runtimeSupport)}catch(e){throw e instanceof H?new H(r.name,r.runtimeSupport,Ee()):e}if(r.requiredConfig){let e=i,t=r.requiredConfig.filter(t=>!e||e[t]===void 0);if(t.length>0)throw new V(`Plugin "${r.name}" requires configuration: ${t.join(`, `)}`,r.name)}if(r.initialize){let e=c();await r.initialize(i,e)}e.set(r.name,r),t.set(r.name,i),l(r)}s.clear()}let f={register:async t=>{if(!t.name)throw new V(`Plugin must have a name`);if(!t.version)throw new V(`Plugin must have a version`,t.name);if(!t.apiVersion)throw new V(`Plugin must have an apiVersion`,t.name);if(!t.capabilities||t.capabilities.length===0)throw new V(`Plugin must have at least one capability`,t.name);if(!t.runtimeSupport)throw new V(`Plugin must declare runtimeSupport`,t.name);if(e.has(t.name))throw new V(`Plugin "${t.name}" is already registered`,t.name);s.set(t.name,{plugin:t,config:void 0}),await d()},unregister:async n=>{let r=e.get(n);if(!r)throw new V(`Plugin "${n}" is not registered`,n);let i=[];for(let[t,r]of e)r.dependencies?.includes(n)&&i.push(t);if(i.length>0)throw new V(`Cannot unregister "${n}" - required by: ${i.join(`, `)}`,n);r.destroy&&await r.destroy(),u(r),e.delete(n),t.delete(n),console.log(`✗ Plugin "${n}" unregistered`)},getPlugin:t=>e.get(t),getImporter:e=>n.get(e),getExporter:e=>r.get(e),getRenderer:e=>i.get(e),getMetadataProvider:e=>a.get(e),executeHooks:async(e,t)=>{let n=o.get(e)||[],r=t;for(let{handler:t,pluginName:i}of n)try{let n=c(),i=await t(r,{...n,event:e});i!==void 0&&(r=i)}catch(t){console.error(`Hook error in plugin "${i}" for event "${e}":`,t)}return r},listPlugins:()=>Array.from(e.values()),listCapabilities:()=>({importers:Array.from(n.keys()),exporters:Array.from(r.keys()),renderers:Array.from(i.keys()),metadataProviders:Array.from(a.keys())}),loadResourcesList:()=>ve(),loadYaml:()=>ye()};return f}function U(){return{name:`json`,version:`1.0.0`,apiVersion:`1.0`,runtimeSupport:{node:!0,browser:!0,deno:!0,bun:!0},capabilities:[{type:`importer`,name:`json`,extensions:[`.json`],mimeTypes:[`application/json`],canImport:async e=>{try{let t=Array.isArray(e)?e:[e];for(let e of t){let t=JSON.parse(e);if(!t||typeof t!=`object`||!Array.isArray(t.nodes))return!1}return!0}catch{return!1}},import:async(e,t,n)=>{let r=Array.isArray(e)?e:[e];for(let e=0;e<r.length;e++)await W(t,await z(JSON.parse(r[e])),n.lib)}},{type:`exporter`,name:`json`,extension:`.json`,mimeType:`application/json`,export:async e=>{let t=e.toJSON();return JSON.stringify(t,null,2)}}]}}async function W(e,t,n){let r=t.toJSON(),i=new Map;for(let e of r.nodes)if(e.provider&&e.service){let t=`${e.provider}/${e.service}`;i.set(t,[e.provider,e.service])}let a=await _e(Array.from(i.values())),o=new Map;function s(t){let r=t.toJSON();for(let t of r.nodes){let r=t.type?a.get(t.type):void 0,i;if(r){let e={nodeId:t.id};t.attrs&&Object.assign(e,t.attrs),i=r(t.label||t.id,e)}else if(t.iconUrl&&Ae(t.iconUrl)){let e={nodeId:t.id};t.attrs&&Object.assign(e,t.attrs),i=n.Custom(t.label||t.id,t.iconUrl,e)}else{let e={nodeId:t.id};t.provider&&(e[`~provider`]=t.provider),t.service&&(e[`~type`]=t.service),t.type&&(e[`~resource`]=t.type),t.iconUrl&&(e[`~iconDataUrl`]=t.iconUrl),t.attrs&&Object.assign(e,t.attrs),i=n.Node(t.label||t.id,e)}t.metadata&&(i.metadata=t.metadata),e.add(i),o.set(t.id,i)}}if(s(t),r.clusters)for(let t of r.clusters){let n=e.cluster(t.label);if(t.graphAttr&&Object.assign(n.graphAttr,t.graphAttr),t.nodes)for(let e of t.nodes){let t=o.get(e);t&&n.add(t)}}if(r.edges)for(let e of r.edges){let t=o.get(e.from),r=o.get(e.to);if(t&&r){let i={};e.label&&(i.label=e.label),e.color&&(i.color=e.color),e.style&&(i.style=e.style),e.direction&&(i.dir=e.direction),e.attrs&&Object.assign(i,e.attrs);let a=n.Edge(i);switch(e.direction??`forward`){case`forward`:a.forward=!0;break;case`back`:a.reverse=!0;break;case`both`:a.forward=!0,a.reverse=!0;break;case`none`:break;default:a.forward=!0}t[`~connect`](r,a)}}}function Ae(e){return e.startsWith(`http://`)||e.startsWith(`https://`)}const je=U();function Me(e){if(typeof Buffer<`u`)return Buffer.from(e,`utf-8`).toString(`base64`);let t=new TextEncoder().encode(e),n=``;for(let e=0;e<t.length;e++)n+=String.fromCharCode(t[e]);return btoa(n)}function G(e){if(typeof Buffer<`u`)return Buffer.from(e,`base64`).toString(`utf-8`);let t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return new TextDecoder().decode(n)}function Ne(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`)}function Pe(e){let t={"data-node-id":e.id};return e.label!==void 0&&(t[`data-node-label`]=e.label),e.provider!==void 0&&(t[`data-node-provider`]=e.provider),e.service!==void 0&&(t[`data-node-service`]=e.service),e.type!==void 0&&(t[`data-node-type`]=e.type),e.metadata&&Object.keys(e.metadata).length>0&&(t[`data-node-metadata`]=Me(JSON.stringify(e.metadata))),Object.entries(t).map(([e,t])=>`${e}="${Ne(t)}"`).join(` `)}function Fe(e){let t={"data-cluster-label":e.label};return e.nodes&&e.nodes.length>0&&(t[`data-cluster-nodes`]=e.nodes.join(`,`)),Object.entries(t).map(([e,t])=>`${e}="${Ne(t)}"`).join(` `)}function Ie(e,t,n,r){if(!r)return e;let i=n.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`),a=RegExp(`<g\\b([^>]*)class="${t}"([^>]*)>((?:(?!<g\\b)[\\s\\S])*?)<title>${i}<\\/title>`,`g`);return e.replace(a,(e,i,a,o)=>`<g${i}class="${t}"${a} ${r}>${o}<title>${n}</title>`)}function Le(e,t){let n=Me(JSON.stringify(t));e=e.replace(/<svg\b([^>]*)>/,`<svg$1 data-diagram-version="1.0" data-diagram-json="${n}">`);for(let n of t.nodes){let t=Pe(n);e=Ie(e,`node`,n.id,t)}if(t.clusters)for(let n of t.clusters){let t=`cluster_${n.label.replace(/\s+/g,`_`)}`,r=Fe(n);e=Ie(e,`cluster`,t,r)}return e}function Re(e){let t=e.match(/data-diagram-json="([^"]+)"/);if(!t)return null;try{let e=G(t[1]);return JSON.parse(e)}catch{return null}}function K(){return{name:`svg`,version:`1.0.0`,apiVersion:`1.0`,runtimeSupport:{node:!0,browser:!0,deno:!0,bun:!0},capabilities:[{type:`importer`,name:`svg`,extensions:[`.svg`],mimeTypes:[`image/svg+xml`],canImport:async e=>{let t=Array.isArray(e)?e:[e];for(let e of t)if(typeof e!=`string`||!e.trim().startsWith(`<svg`)&&!e.includes(`<svg`)||!e.includes(`data-diagram-json="`))return!1;return!0},import:async(e,t,n)=>{let r=Array.isArray(e)?e:[e];for(let e of r){let r=Re(e);if(!r)throw Error(`Invalid diagram SVG: missing or corrupted embedded diagram data. Make sure the SVG was exported using diagrams-js SVG export.`);let i=n.getImporter(`json`);i?await i.import(JSON.stringify(r),t,n):await W(t,await z(r),n.lib)}}},{type:`exporter`,name:`svg`,extension:`.svg`,mimeType:`image/svg+xml`,export:async e=>await e.render({format:`svg`})}]}}const q=K(),J={light:{added:{border:`#2EA043`,fill:`rgba(46, 160, 67, 0.1)`,badge:`#2EA043`},removed:{border:`#F85149`,fill:`rgba(248, 81, 73, 0.1)`,badge:`#F85149`},modified:{border:`#D29922`,fill:`rgba(210, 153, 34, 0.1)`,badge:`#D29922`},unchanged:{border:`#8C959F`,fill:`transparent`,badge:`#8C959F`}},dark:{added:{border:`#3FB950`,fill:`rgba(63, 185, 80, 0.15)`,badge:`#3FB950`},removed:{border:`#F85149`,fill:`rgba(248, 81, 73, 0.15)`,badge:`#F85149`},modified:{border:`#D29922`,fill:`rgba(210, 153, 34, 0.15)`,badge:`#D29922`},unchanged:{border:`#6E7681`,fill:`transparent`,badge:`#6E7681`}}};function ze(e){return`${e.label||``}|${e.provider||``}|${e.service||``}|${e.type||``}`}function Y(e){return`${e.provider||``}|${e.service||``}|${e.type||``}`}function X(e,t){if(e.size!==t.size)return!1;for(let n of e)if(!t.has(n))return!1;return!0}function Be(e,t,n=[]){if(e===t)return!0;if(typeof e!=typeof t)return!1;if(e===null||t===null||typeof e!=`object`)return e===t;let r=e,i=t,a=Object.keys(r).filter(e=>!n.includes(e)),o=Object.keys(i).filter(e=>!n.includes(e));if(a.length!==o.length)return!1;for(let e of a){if(!o.includes(e))return!1;let t=n.filter(t=>t.startsWith(`${e}.`)).map(t=>t.slice(e.length+1));if(!Be(r[e],i[e],t))return!1}return!0}function Z(e,t,n=``,r=[]){let i=[],a=new Set([...Object.keys(e),...Object.keys(t)]);for(let o of a){let a=n?`${n}.${o}`:o;if(r.includes(a))continue;let s=e[o],c=t[o];if(!(o in e))i.push(`${a} (added)`);else if(!(o in t))i.push(`${a} (removed)`);else if(typeof s==`object`&&s&&typeof c==`object`&&c){let e=Z(s,c,a,r);i.push(...e)}else s!==c&&i.push(`${a}: ${JSON.stringify(s)} → ${JSON.stringify(c)}`)}return i}function Ve(e,t,n={}){let r=typeof e.toJSON==`function`?e.toJSON():e,i=typeof t.toJSON==`function`?t.toJSON():t,a=[];if(n.ignore?.position!==!1&&a.push(`attrs.pos`,`attrs.lp`),n.ignore?.attrs)for(let e of n.ignore.attrs)a.push(`attrs.${e}`);if(n.ignore?.metadata===!0)a.push(`metadata`);else if(Array.isArray(n.ignore?.metadata))for(let e of n.ignore.metadata)a.push(`metadata.${e}`);let o={nodes:new Map,edges:new Map,clusters:[],summary:{added:0,removed:0,modified:0,unchanged:0},meta:{}};r.name!==i.name&&(o.meta.name={before:r.name,after:i.name}),r.theme!==i.theme&&(o.meta.theme={before:r.theme,after:i.theme}),r.direction!==i.direction&&(o.meta.direction={before:r.direction,after:i.direction}),r.curvestyle!==i.curvestyle&&(o.meta.curvestyle={before:r.curvestyle,after:i.curvestyle}),Z(r.graphAttr||{},i.graphAttr||{},``).length>0&&(o.meta.graphAttr={before:r.graphAttr,after:i.graphAttr}),Z(r.nodeAttr||{},i.nodeAttr||{},``).length>0&&(o.meta.nodeAttr={before:r.nodeAttr,after:i.nodeAttr}),Z(r.edgeAttr||{},i.edgeAttr||{},``).length>0&&(o.meta.edgeAttr={before:r.edgeAttr,after:i.edgeAttr});let s=new Map(r.nodes.map(e=>[e.id,e])),c=new Map(i.nodes.map(e=>[e.id,e])),l=new Set,u=new Set,d=new Map,f=new Map(r.nodes.map(e=>[ze(e),e.id]));for(let[e,t]of c){let n=ze(t);if(f.has(n)){let r=f.get(n),i=s.get(r);if(l.has(r)||u.has(e))continue;l.add(r),u.add(e),d.set(e,r);let c=Z(i,t,``,[...a,`id`]);c.length===0?(o.nodes.set(e,{kind:`unchanged`,before:i,after:t}),o.summary.unchanged++):(o.nodes.set(e,{kind:`modified`,before:i,after:t,changes:c}),o.summary.modified++)}}let p=Array.from(s.entries()).filter(([e])=>!l.has(e)),m=Array.from(c.entries()).filter(([e])=>!u.has(e)),h=new Map;for(let[e,t]of p){let n=Y(t);h.has(n)||h.set(n,[]),h.get(n).push(e)}let g=new Map;for(let[e,t]of m){let n=Y(t);g.has(n)||g.set(n,[]),g.get(n).push(e)}function _(e,t,n){let r=new Set;for(let i of t){let t=i.from===e,a=i.to===e;if(!t&&!a)continue;let o=t?i.to:i.from,s=n?o:d.get(o)||o,c=n?l.has(o):d.has(o);s&&c&&r.add(s)}return r}for(let[e,t]of g){let n=h.get(e);if(!n||n.length===0)continue;let a=t.map(e=>({id:e,node:c.get(e),connected:_(e,i.edges||[],!1)})),f=n.map(e=>({id:e,node:s.get(e),connected:_(e,r.edges||[],!0)}));for(let e of a){if(u.has(e.id))continue;let t=f.find(t=>!l.has(t.id)&&X(e.connected,t.connected)&&e.connected.size>0);if(t){l.add(t.id),u.add(e.id),d.set(e.id,t.id);let n=t.node,r=e.node;n.label===r.label?(o.nodes.set(e.id,{kind:`unchanged`,before:n,after:r}),o.summary.unchanged++):(o.nodes.set(e.id,{kind:`modified`,before:n,after:r,changes:[`label: "${n.label}" → "${r.label}"`]}),o.summary.modified++)}}}let v=Array.from(s.entries()).filter(([e])=>!l.has(e)),y=Array.from(c.entries()).filter(([e])=>!u.has(e)),b=new Map;for(let[e,t]of v){let n=Y(t);b.has(n)||b.set(n,[]),b.get(n).push(e)}let x=new Map;for(let[e,t]of y){let n=Y(t);x.has(n)||x.set(n,[]),x.get(n).push(e)}for(let[e,t]of x){let n=b.get(e);if(!n)continue;let r=Math.min(t.length,n.length);for(let e=0;e<r;e++){let r=t[e],i=n[e];if(l.has(i)||u.has(r))continue;let a=s.get(i),f=c.get(r);l.add(i),u.add(r),d.set(r,i),a.label===f.label?(o.nodes.set(r,{kind:`unchanged`,before:a,after:f}),o.summary.unchanged++):(o.nodes.set(r,{kind:`modified`,before:a,after:f,changes:[`label: "${a.label}" → "${f.label}"`]}),o.summary.modified++)}}for(let[e,t]of s)l.has(e)||(o.nodes.set(e,{kind:`removed`,before:t}),o.summary.removed++);for(let[e,t]of c)u.has(e)||(o.nodes.set(e,{kind:`added`,after:t}),o.summary.added++);let S=r.edges||[],C=i.edges||[];function w(e,t){let n,r;return t===`before`?(n=e.from,r=e.to):(n=d.get(e.from)||e.from,r=d.get(e.to)||e.to),`${n}->${r}`}function T(e){return`${e.from}->${e.to}[${e.label||``}]`}let E=new Map;for(let e of S)E.set(w(e,`before`),e);let D=new Map;for(let e of C)D.set(w(e,`after`),e);let O=new Set,k=new Set;for(let[e,t]of D)if(E.has(e)){let n=E.get(e),r=T(n),i=T(t);O.add(r),k.add(i);let s=Z(n,t,``,[...a,`from`,`to`]);s.length===0?(o.edges.set(i,{kind:`unchanged`,before:n,after:t}),o.summary.unchanged++):(o.edges.set(i,{kind:`modified`,before:n,after:t,changes:s}),o.summary.modified++)}for(let e of C){let t=T(e);k.has(t)||(o.edges.set(t,{kind:`added`,after:e}),o.summary.added++)}for(let e of S){let t=T(e);O.has(t)||(o.edges.set(t,{kind:`removed`,before:e}),o.summary.removed++)}function A(e=[],t=[],n=[]){let r=[],i=new Map(e.map(e=>[e.label,e])),a=new Map(t.map(e=>[e.label,e])),s=new Set([...i.keys(),...a.keys()]);for(let e of s){let t=i.get(e),s=a.get(e);if(t&&s){let i=[];Be(t.graphAttr||{},s.graphAttr||{})||i.push(`graphAttr`);let a=A(t.clusters,s.clusters,[...n,e]),c={kind:i.length>0?`modified`:`unchanged`,label:e,before:t,after:s,changes:i.length>0?i:void 0,clusters:a.length>0?a:void 0};r.push(c),i.length>0?o.summary.modified++:o.summary.unchanged++}else t?(r.push({kind:`removed`,label:e,before:t}),o.summary.removed++):s&&(r.push({kind:`added`,label:e,after:s}),o.summary.added++)}return r}return o.clusters=A(r.clusters,i.clusters),o}function He(e,t,n,r,i){let a=J[r],o=e;return o=o.replace(/<g([^>]*)class="node"([^>]*)>([\s\S]*?)<title>([^<]+)<\/title>([\s\S]*?)<\/g>/g,(e,r,o,s,c,l)=>{let u=t.get(c);if(!u)return e;let d=u.kind;if(n===`before`&&d===`added`&&(d=`unchanged`),n===`after`&&d===`removed`&&(d=`unchanged`),d===`unchanged`&&i===`hide`)return``;let f=a[d],p=d===`unchanged`&&i===`dim`?`0.4`:`1`,m=d===`unchanged`?``:`filter: drop-shadow(0 0 4px ${f.border}) drop-shadow(0 0 4px ${f.border});`,h=`${r}data-diff-kind="${d}" ${o}`,g=e.replace(/<g([^>]*)class="node"/,`<g${h}class="node" style="opacity:${p};${m}"`);return d!==`unchanged`&&(g=g.replace(/<ellipse([^>]*)style="([^"]*)"/g,(e,t,n)=>`<ellipse${t}style="${n};fill:${f.fill}"`).replace(/<ellipse(?![^>]*style=)/g,`<ellipse style="fill:${f.fill}"`).replace(/<polygon([^>]*)style="([^"]*)"/g,(e,t,n)=>`<polygon${t}style="${n};fill:${f.fill}"`).replace(/<polygon(?![^>]*style=)/g,`<polygon style="fill:${f.fill}"`)),g}),o=o.replace(/<g([^>]*)class="edge"([^>]*)>([\s\S]*?)<title>([^<]+)<\/title>([\s\S]*?)<\/g>/g,(e,r,o,s,c,l)=>{let u=t.get(c);if(!u)return e;let d=u.kind;if(n===`before`&&d===`added`&&(d=`unchanged`),n===`after`&&d===`removed`&&(d=`unchanged`),d===`unchanged`&&i===`hide`)return``;let f=a[d],p=d===`unchanged`&&i===`dim`?`0.4`:`1`,m=d===`unchanged`?``:`stroke:${f.border};stroke-width:2;filter: drop-shadow(0 0 2px ${f.border});`,h=`${r}data-diff-kind="${d}" ${o}`;return e.replace(/<g([^>]*)class="edge"/,`<g${h}class="edge" style="opacity:${p}"`).replace(/<path/g,`<path style="${m}"`).replace(/<polygon/g,`<polygon style="fill:${f.border};stroke:${f.border}"`)}),o}function Ue(e,t,n,r){let i=J[r.theme],{summary:a,meta:o}=n,s=[];o.name&&s.push(`Name: "${o.name.before}" → "${o.name.after}"`),o.theme&&s.push(`Theme: ${o.theme.before} → ${o.theme.after}`),o.direction&&s.push(`Direction: ${o.direction.before} → ${o.direction.after}`),o.curvestyle&&s.push(`Curve style: ${o.curvestyle.before} → ${o.curvestyle.after}`);let c=[a.added>0?`<span class="pill added">+${a.added}</span>`:``,a.removed>0?`<span class="pill removed">−${a.removed}</span>`:``,a.modified>0?`<span class="pill modified">~${a.modified}</span>`:``].filter(Boolean).join(` `),l=r.layout===`stacked`?`stacked`:`side-by-side`;return`<!DOCTYPE html>
|
|
15
|
+
`+e:u.label=n}let s=u[`~iconDataUrl`];if(s&&(l.shape||(r[`~userNodeAttr`]?.shape?a.shape=r[`~userNodeAttr`].shape:a.shape=`none`),l.height||(r[`~userNodeAttr`]?.height?a.height=r[`~userNodeAttr`].height:a.height=`0.9`),l.width||(r[`~userNodeAttr`]?.width?a.width=r[`~userNodeAttr`].width:a.width=`0.8`),l.fixedsize||(r[`~userNodeAttr`]?.fixedsize?a.fixedsize=r[`~userNodeAttr`].fixedsize:a.fixedsize=`true`),l.margin||(r[`~userNodeAttr`]?.margin?a.margin=r[`~userNodeAttr`].margin:a.margin=`0,0`),l.labelloc||(r[`~userNodeAttr`]?.labelloc?a.labelloc=r[`~userNodeAttr`].labelloc:a.labelloc=`b`),l.imagescale||(r[`~userNodeAttr`]?.imagescale?a.imagescale=r[`~userNodeAttr`].imagescale:a.imagescale=`true`),r[`~trackNodeWithIcon`](u,s)),P(o),o[`~node`](n,u.label,a),se(this)&&r){let e=ce(this,r);r[`~trackPendingIconLoad`](e)}},to(e,t){if(ae(e)){if(e.node=u,e.forward=!0,t===void 0)return e;let n=t;if(Array.isArray(n)){for(let t of n){let n=j({...e.attrs,node:u,forward:!0});u[`~connect`](t,n)}return n}return u[`~connect`](n,e)}let n=e;if(Array.isArray(n)){for(let e of n){let t=j({node:u,forward:!0});u[`~connect`](e,t)}return n}let r=j({node:u,forward:!0});return u[`~connect`](n,r)},from(e,t){if(ae(e)){e.node=u,e.reverse=!0;let n=t;if(Array.isArray(n)){for(let t of n){let n=j({...e.attrs,node:u,reverse:!0});u[`~connect`](t,n)}return u}return u[`~connect`](n,e)}let n=e;if(Array.isArray(n)){for(let e of n){let t=j({node:e,reverse:!0});u[`~connect`](e,t)}return u}let r=j({node:n,reverse:!0});return u[`~connect`](n,r)},with(e,t){if(ae(e)){e.node=u;let n=t;if(Array.isArray(n)){for(let t of n){let n=j({...e.attrs,node:u});u[`~connect`](t,n)}return n}return u[`~connect`](n,e)}let n=e,r=j({node:u});if(Array.isArray(n)){for(let e of n)u[`~connect`](e,r);return n}return u[`~connect`](n,r)},"~connect"(e,t){if(!e||typeof e!=`object`||!(`nodeId`in e))throw Error(`${String(e)} is not a valid Node`);if(!t||typeof t!=`object`||!(`attrs`in t))throw Error(`${String(t)} is not a valid Edge`);if(!r)throw Error(`Node is not registered with a diagram`);return r[`~connect`](u,e,t),e}};return u}function P(e){return typeof e==`object`&&!!e&&`getNodes`in e&&typeof e.getNodes==`function`}function se(e){return`~getIconUrl`in e&&typeof e[`~getIconUrl`]==`function`}async function ce(e,t){let n=e,r=n[`~getIconUrl`]();if(r.startsWith(`data:`)){t[`~trackNodeWithIcon`](e,r);return}try{let r=await n.loadIcon();r&&t[`~trackNodeWithIcon`](e,r)}catch(t){console.warn(`Failed to load custom icon for node ${e.nodeId}:`,t)}}const le=new Map;function F(e,t,n){let r=t.startsWith(`data:`);return{...N(e,{shape:`none`,height:`0.9`,width:`0.8`,fixedsize:`true`,margin:`0,0`,labelloc:`b`,imagescale:`true`,...n,...r?{"~iconDataUrl":t}:{}}),"~iconUrl":t,get"~iconDataUrl"(){return t.startsWith(`data:`)?t:null},set"~iconDataUrl"(e){},"~getIconUrl"(){return t},async loadIcon(){if(ue(t))return t;if(le.has(t))return le.get(t);let e;return e=de(t)?fe(t):pe(t),le.set(t,e),e.then(()=>{}).catch(()=>{le.delete(t)}),e}}}function ue(e){return e.startsWith(`data:`)}function de(e){return e.startsWith(`http://`)||e.startsWith(`https://`)}async function fe(e){try{let t=new AbortController,n=setTimeout(()=>t.abort(),1e4),r=await globalThis.fetch(e,{signal:t.signal});if(clearTimeout(n),!r.ok)return console.warn(`Failed to fetch icon: ${e}`),null;if(typeof Blob<`u`&&typeof FileReader<`u`)return me(await r.blob());let i=await r.arrayBuffer(),a=Buffer.from(i);return`data:${r.headers.get(`content-type`)||`image/png`};base64,${a.toString(`base64`)}`}catch(t){return console.warn(`Error fetching icon: ${e}`,t),null}}async function pe(e){try{let t=await import(`node:fs/promises`),n=await import(`node:path`),r=await t.readFile(e),i=n.extname(e).toLowerCase(),a=`image/png`;return i===`.svg`?a=`image/svg+xml`:i===`.jpg`||i===`.jpeg`?a=`image/jpeg`:i===`.gif`?a=`image/gif`:i===`.webp`&&(a=`image/webp`),`data:${a};base64,${r.toString(`base64`)}`}catch(t){return console.warn(`Error loading local icon: ${e}`,t),null}}function me(e){return new Promise((t,n)=>{let r=new FileReader;r.onloadend=()=>t(r.result),r.onerror=n,r.readAsDataURL(e)})}function he(e,t){return F(e,`https://api.iconify.design/${t}.svg`)}let I=null;async function ge(e,t){let n=e=>e.filter(e=>e!=null).join(`/`);if(I===`relative`||!I&&!import.meta.url.endsWith(`.js`))try{let r=await import(n([`.`,e,t]));return I=`relative`,r}catch{}if(I===`direct`||!I)try{let r=await import(n([`.`,`providers`,e,t])+`.js`);return I=`direct`,r}catch{}if(I===`bare`||!I)try{let r=await import(n([`diagrams-js`,e,t]));return I=`bare`,r}catch{}if(I===`cdn`||!I)try{let r=await import(n([`https://esm.sh`,`diagrams-js`,e,t]));return I=`cdn`,r}catch{}return null}async function _e(e){let t=new Map;for(let[n,r]of e){let e=await ge(n,r);if(e)for(let[n,r]of Object.entries(e))typeof r==`function`&&!n.startsWith(`_`)&&t.set(n,r)}return t}async function ve(){return await ge(`resources-list`)}async function ye(){return await ge(`yaml`)}const be=new Set([`shape`,`height`,`width`,`fixedsize`,`margin`,`labelloc`,`imagescale`]),xe={shape:`none`,height:`0.9`,width:`0.8`,fixedsize:`true`,margin:`0,0`,labelloc:`b`,imagescale:`true`};function L(e,t,n,r){let i={id:e};if(t&&(i.label=t),r){let e=r,t=e[`~provider`],n=e[`~type`],a=e[`~resource`];t&&(i.provider=t),n&&(i.type=n),a&&(i.resource=a),`~getIconUrl`in e&&typeof e[`~getIconUrl`]==`function`&&(i.iconUrl=e[`~getIconUrl`]()),r.metadata&&Object.keys(r.metadata).length>0&&(i.metadata=r.metadata)}let a=i.provider!=null,o=i.iconUrl!=null,s=a||o,c={};for(let[e,t]of Object.entries(n))if(!e.startsWith(`~`)){if(s&&be.has(e)){let n=xe[e];if(n!==void 0&&String(t)===n)continue}c[e]=typeof t==`number`?t:String(t)}return Object.keys(c).length>0&&(i.attrs=c),i}function R(e,t,n){let r={label:e.label},i=[];for(let[r,{label:a,attrs:o}]of e.getNodes())i.push(r),t.push(L(r,a,o,n.get(r)));i.length>0&&(r.nodes=i);let a={};for(let[t,n]of Object.entries(e.graphAttr))t===`shape`||t===`style`||t===`labeljust`||t===`fontname`||t===`fontsize`||t===`label`||t===`pencolor`||t===`bgcolor`||(a[t]=n);Object.keys(a).length>0&&(r.graphAttr=a);let o=e.getSubgraphs();return o.length>0&&(r.clusters=o.map(e=>R(e,t,n))),r}function Se(e){return e.map(e=>{let t={from:e.from,to:e.to},{dir:n,label:r,color:i,style:a,...o}=e.attrs;return n&&n!==`forward`&&(t.direction=n),r&&(t.label=r),i&&(t.color=i),a&&(t.style=a),Object.keys(o).length>0&&(t.attrs=o),t})}function Ce(e,t,n,r,i,a,o){let s={nodes:[]};e.name&&(s.name=e.name);let c=e.name?e.name.toLowerCase().replace(/\s+/g,`_`):`diagram`;e.filename!==c&&(s.filename=e.filename),e.direction!==`LR`&&(s.direction=e.direction),e.curveStyle!==`ortho`&&(s.curvestyle=e.curveStyle),e.autolabel&&(s.autolabel=e.autolabel),e.strict&&(s.strict=e.strict),e.theme!==`pastel`&&(s.theme=e.theme),o.graphAttr&&Object.keys(o.graphAttr).length>0&&(s.graphAttr={...o.graphAttr}),o.nodeAttr&&Object.keys(o.nodeAttr).length>0&&(s.nodeAttr={...o.nodeAttr}),o.edgeAttr&&Object.keys(o.edgeAttr).length>0&&(s.edgeAttr={...o.edgeAttr});let l=new Map(a);for(let e of i)l.has(e.node.nodeId)||l.set(e.node.nodeId,e.node);let u=new Set;function d(e){for(let[t]of e.getNodes())u.add(t);for(let t of e.getSubgraphs())d(t)}for(let e of r)d(e);let f=[];for(let[e,{label:n,attrs:r}]of t)u.has(e)||s.nodes.push(L(e,n,r,l.get(e)));return r.length>0&&(s.clusters=r.map(e=>R(e,f,l)),s.nodes.push(...f)),n.length>0&&(s.edges=Se(n)),s}async function z(e,t){let n=typeof e==`string`?JSON.parse(e):e;if(!n.nodes||!Array.isArray(n.nodes))throw Error(`Invalid diagram JSON: 'nodes' array is required`);let r=new Map;for(let e of n.nodes){let t=e.type||e.service;if(e.provider&&t){let n=`${e.provider}/${t}`;r.set(n,[e.provider,t])}}let i=await _e(Array.from(r.values()));if(t?.providers)for(let e of t.providers)for(let[t,n]of Object.entries(e))typeof n==`function`&&!t.startsWith(`_`)&&i.set(t,n);let a={};n.name!==void 0&&(a.name=n.name),n.filename!==void 0&&(a.filename=n.filename),n.direction!==void 0&&(a.direction=n.direction),n.curvestyle!==void 0&&(a.curvestyle=n.curvestyle),n.autolabel!==void 0&&(a.autolabel=n.autolabel),n.strict!==void 0&&(a.strict=n.strict),n.theme!==void 0&&(a.theme=n.theme),n.graphAttr!==void 0&&(a.graphAttr=n.graphAttr),n.nodeAttr!==void 0&&(a.nodeAttr=n.nodeAttr),n.edgeAttr!==void 0&&(a.edgeAttr=n.edgeAttr);let o=Q(n.name??``,a),s=new Map,c=new Map;if(n.clusters){function e(t,n){let r=[...n,t.label];if(t.nodes)for(let e of t.nodes)c.set(e,r);if(t.clusters)for(let n of t.clusters)e(n,r)}for(let t of n.clusters)e(t,[])}let l=new Map;if(n.clusters){function e(t,n){let r=n.cluster(t.label);if(t.graphAttr&&Object.assign(r.graphAttr,t.graphAttr),l.set(t.label,r),t.clusters)for(let n of t.clusters)e(n,r)}for(let t of n.clusters)e(t,o)}let u=new Set;for(let e of n.nodes){if(u.has(e.id))throw Error(`Duplicate node ID: "${e.id}"`);u.add(e.id);let t,n=e.resource||e.type,r=n?i.get(n):void 0;if(r)t=r(e.label??``,{nodeId:e.id,...e.attrs});else if(e.iconUrl&&we(e.iconUrl)){let n={nodeId:e.id};e.attrs&&Object.assign(n,e.attrs),t=F(e.label??``,e.iconUrl,n)}else{let r={nodeId:e.id};e.attrs&&Object.assign(r,e.attrs),t=N(e.label??``,r);let i=t;e.provider&&(i[`~provider`]=e.provider);let a=e.type||e.service;a&&(i[`~type`]=a),n&&(i[`~resource`]=n),e.iconUrl&&(i[`~iconDataUrl`]=e.iconUrl)}e.metadata&&(t.metadata=e.metadata);let a=c.get(e.id);if(a&&a.length>0){let e=a[a.length-1],n=l.get(e);n?n.add(t):o.add(t)}else o.add(t);s.set(e.id,t)}if(n.edges)for(let e of n.edges){let t=s.get(e.from),n=s.get(e.to);if(!t)throw Error(`Edge references unknown source node: "${e.from}"`);if(!n)throw Error(`Edge references unknown target node: "${e.to}"`);let r=e.direction??`forward`,i={};e.label&&(i.label=e.label),e.color&&(i.color=e.color),e.style&&(i.style=e.style),e.attrs&&Object.assign(i,e.attrs);let a=j(i);switch(r){case`forward`:a.forward=!0;break;case`back`:a.reverse=!0;break;case`both`:a.forward=!0,a.reverse=!0;break;case`none`:break;default:a.forward=!0}t[`~connect`](n,a)}return o}function we(e){return e.startsWith(`http://`)||e.startsWith(`https://`)}let B=function(e){return e.BEFORE_IMPORT=`before:import`,e.AFTER_IMPORT=`after:import`,e.BEFORE_EXPORT=`before:export`,e.AFTER_EXPORT=`after:export`,e.BEFORE_RENDER=`before:render`,e.AFTER_RENDER=`after:render`,e.BEFORE_SERIALIZE=`before:serialize`,e.AFTER_DESERIALIZE=`after:deserialize`,e.NODE_CREATE=`node:create`,e.EDGE_CREATE=`edge:create`,e.CLUSTER_CREATE=`cluster:create`,e.METADATA_ATTACH=`metadata:attach`,e.METADATA_CALCULATE=`metadata:calculate`,e}({});var V=class extends Error{constructor(e,t,n){super(e),this.pluginName=t,this.cause=n,this.name=`PluginError`}},Te=class extends V{constructor(e,t,n){super(`Plugin "${e}" has unresolved dependencies: ${t.join(`, `)}`,e),this.missingDependencies=t,this.circularDependencies=n,this.name=`DependencyError`}},H=class extends V{constructor(e,t,n){super(`Plugin "${e}" does not support ${n} runtime`,e),this.requiredRuntimes=t,this.currentRuntime=n,this.name=`RuntimeError`}};function Ee(){return globalThis.Deno===void 0?globalThis.Bun===void 0?typeof window<`u`&&typeof document<`u`?`browser`:(typeof process<`u`&&process.versions?.node,`node`):`bun`:`deno`}function De(e){let t=Ee();if(!e[t])throw new H(`unknown`,e,t)}function Oe(e){let t=new Set,n=new Set,r=[],i=[];function a(o,s=[]){if(n.has(o)){let e=s.indexOf(o);i.push(...s.slice(e),o);return}if(t.has(o))return;let c=e.get(o);if(c){n.add(o),s.push(o);for(let e of c.plugin.dependencies||[])a(e,[...s]);n.delete(o),t.add(o),r.push(c)}}for(let[n]of e)t.has(n)||a(n);if(i.length>0)throw new Te(i[0],[],i);return r}function ke(){let e=new Map,t=new Map,n=new Map,r=new Map,i=new Map,a=new Map,o=new Map,s=new Map;function c(){return{registry:f,fetch:globalThis.fetch.bind(globalThis),getPlugin:t=>e.get(t),getImporter:e=>n.get(e),getExporter:e=>r.get(e),getRenderer:e=>i.get(e),getMetadataProvider:e=>a.get(e),executeHooks:async(e,t)=>f.executeHooks(e,t),loadResourcesList:()=>ve(),loadYaml:()=>ye(),lib:Qe}}function l(e){for(let t of e.capabilities)switch(t.type){case`importer`:n.set(t.name,t);break;case`exporter`:r.set(t.name,t);break;case`renderer`:i.set(t.name,t);for(let e of t.formats)i.set(e,t);break;case`metadata`:a.set(t.provider,t);break;case`hook`:for(let{event:n,handler:r,priority:i=0}of t.hooks){let t=o.get(n)||[];t.push({handler:r,priority:i,pluginName:e.name}),t.sort((e,t)=>t.priority-e.priority),o.set(n,t)}break}}function u(e){for(let t of e.capabilities)switch(t.type){case`importer`:n.delete(t.name);break;case`exporter`:r.delete(t.name);break;case`renderer`:i.delete(t.name);for(let e of t.formats)i.delete(e);break;case`metadata`:a.delete(t.provider);break;case`hook`:for(let{event:n}of t.hooks){let t=o.get(n);if(t){let r=t.filter(t=>t.pluginName!==e.name);r.length===0?o.delete(n):o.set(n,r)}}break}}async function d(){if(s.size===0)return;for(let[t,{plugin:n}]of s)if(n.dependencies){let r=n.dependencies.filter(t=>!e.has(t)&&!s.has(t));if(r.length>0)throw new Te(t,r)}let n=Oe(s);for(let{plugin:r,config:i}of n){try{De(r.runtimeSupport)}catch(e){throw e instanceof H?new H(r.name,r.runtimeSupport,Ee()):e}if(r.requiredConfig){let e=i,t=r.requiredConfig.filter(t=>!e||e[t]===void 0);if(t.length>0)throw new V(`Plugin "${r.name}" requires configuration: ${t.join(`, `)}`,r.name)}if(r.initialize){let e=c();await r.initialize(i,e)}e.set(r.name,r),t.set(r.name,i),l(r)}s.clear()}let f={register:async t=>{if(!t.name)throw new V(`Plugin must have a name`);if(!t.version)throw new V(`Plugin must have a version`,t.name);if(!t.apiVersion)throw new V(`Plugin must have an apiVersion`,t.name);if(!t.capabilities||t.capabilities.length===0)throw new V(`Plugin must have at least one capability`,t.name);if(!t.runtimeSupport)throw new V(`Plugin must declare runtimeSupport`,t.name);if(e.has(t.name))throw new V(`Plugin "${t.name}" is already registered`,t.name);s.set(t.name,{plugin:t,config:void 0}),await d()},unregister:async n=>{let r=e.get(n);if(!r)throw new V(`Plugin "${n}" is not registered`,n);let i=[];for(let[t,r]of e)r.dependencies?.includes(n)&&i.push(t);if(i.length>0)throw new V(`Cannot unregister "${n}" - required by: ${i.join(`, `)}`,n);r.destroy&&await r.destroy(),u(r),e.delete(n),t.delete(n),console.log(`✗ Plugin "${n}" unregistered`)},getPlugin:t=>e.get(t),getImporter:e=>n.get(e),getExporter:e=>r.get(e),getRenderer:e=>i.get(e),getMetadataProvider:e=>a.get(e),executeHooks:async(e,t)=>{let n=o.get(e)||[],r=t;for(let{handler:t,pluginName:i}of n)try{let n=c(),i=await t(r,{...n,event:e});i!==void 0&&(r=i)}catch(t){console.error(`Hook error in plugin "${i}" for event "${e}":`,t)}return r},listPlugins:()=>Array.from(e.values()),listCapabilities:()=>({importers:Array.from(n.keys()),exporters:Array.from(r.keys()),renderers:Array.from(i.keys()),metadataProviders:Array.from(a.keys())}),loadResourcesList:()=>ve(),loadYaml:()=>ye()};return f}function U(){return{name:`json`,version:`1.0.0`,apiVersion:`1.0`,runtimeSupport:{node:!0,browser:!0,deno:!0,bun:!0},capabilities:[{type:`importer`,name:`json`,extensions:[`.json`],mimeTypes:[`application/json`],canImport:async e=>{try{let t=Array.isArray(e)?e:[e];for(let e of t){let t=JSON.parse(e);if(!t||typeof t!=`object`||!Array.isArray(t.nodes))return!1}return!0}catch{return!1}},import:async(e,t,n)=>{let r=Array.isArray(e)?e:[e];for(let e=0;e<r.length;e++)await W(t,await z(JSON.parse(r[e])),n.lib)}},{type:`exporter`,name:`json`,extension:`.json`,mimeType:`application/json`,export:async e=>{let t=e.toJSON();return JSON.stringify(t,null,2)}}]}}async function W(e,t,n){let r=t.toJSON(),i=new Map;for(let e of r.nodes){let t=e.type||e.service;if(e.provider&&t){let n=`${e.provider}/${t}`;i.set(n,[e.provider,t])}}let a=await _e(Array.from(i.values())),o=new Map;function s(t){let r=t.toJSON();for(let t of r.nodes){let r=t.resource||t.type,i=r?a.get(r):void 0,s;if(i){let e={nodeId:t.id};t.attrs&&Object.assign(e,t.attrs),s=i(t.label||t.id,e)}else if(t.iconUrl&&Ae(t.iconUrl)){let e={nodeId:t.id};t.attrs&&Object.assign(e,t.attrs),s=n.Custom(t.label||t.id,t.iconUrl,e)}else{let e={nodeId:t.id};t.provider&&(e[`~provider`]=t.provider);let i=t.type||t.service;i&&(e[`~type`]=i),r&&(e[`~resource`]=r),t.iconUrl&&(e[`~iconDataUrl`]=t.iconUrl),t.attrs&&Object.assign(e,t.attrs),s=n.Node(t.label||t.id,e)}t.metadata&&(s.metadata=t.metadata),e.add(s),o.set(t.id,s)}}if(s(t),r.clusters)for(let t of r.clusters){let n=e.cluster(t.label);if(t.graphAttr&&Object.assign(n.graphAttr,t.graphAttr),t.nodes)for(let e of t.nodes){let t=o.get(e);t&&n.add(t)}}if(r.edges)for(let e of r.edges){let t=o.get(e.from),r=o.get(e.to);if(t&&r){let i={};e.label&&(i.label=e.label),e.color&&(i.color=e.color),e.style&&(i.style=e.style),e.direction&&(i.dir=e.direction),e.attrs&&Object.assign(i,e.attrs);let a=n.Edge(i);switch(e.direction??`forward`){case`forward`:a.forward=!0;break;case`back`:a.reverse=!0;break;case`both`:a.forward=!0,a.reverse=!0;break;case`none`:break;default:a.forward=!0}t[`~connect`](r,a)}}}function Ae(e){return e.startsWith(`http://`)||e.startsWith(`https://`)}const je=U();function Me(e){if(typeof Buffer<`u`)return Buffer.from(e,`utf-8`).toString(`base64`);let t=new TextEncoder().encode(e),n=``;for(let e=0;e<t.length;e++)n+=String.fromCharCode(t[e]);return btoa(n)}function G(e){if(typeof Buffer<`u`)return Buffer.from(e,`base64`).toString(`utf-8`);let t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return new TextDecoder().decode(n)}function Ne(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`)}function Pe(e){let t={"data-node-id":e.id};return e.label!==void 0&&(t[`data-node-label`]=e.label),e.provider!==void 0&&(t[`data-node-provider`]=e.provider),e.type!==void 0&&(t[`data-node-type`]=e.type),e.resource!==void 0&&(t[`data-node-resource`]=e.resource),e.metadata&&Object.keys(e.metadata).length>0&&(t[`data-node-metadata`]=Me(JSON.stringify(e.metadata))),Object.entries(t).map(([e,t])=>`${e}="${Ne(t)}"`).join(` `)}function Fe(e){let t={"data-cluster-label":e.label};return e.nodes&&e.nodes.length>0&&(t[`data-cluster-nodes`]=e.nodes.join(`,`)),Object.entries(t).map(([e,t])=>`${e}="${Ne(t)}"`).join(` `)}function Ie(e,t,n,r){if(!r)return e;let i=n.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`),a=RegExp(`<g\\b([^>]*)class="${t}"([^>]*)>((?:(?!<g\\b)[\\s\\S])*?)<title>${i}<\\/title>`,`g`);return e.replace(a,(e,i,a,o)=>`<g${i}class="${t}"${a} ${r}>${o}<title>${n}</title>`)}function Le(e,t){let n=Me(JSON.stringify(t));e=e.replace(/<svg\b([^>]*)>/,`<svg$1 data-diagram-version="1.0" data-diagram-json="${n}">`);for(let n of t.nodes){let t=Pe(n);e=Ie(e,`node`,n.id,t)}if(t.clusters)for(let n of t.clusters){let t=`cluster_${n.label.replace(/\s+/g,`_`)}`,r=Fe(n);e=Ie(e,`cluster`,t,r)}return e}function Re(e){let t=e.match(/data-diagram-json="([^"]+)"/);if(!t)return null;try{let e=G(t[1]);return JSON.parse(e)}catch{return null}}function K(){return{name:`svg`,version:`1.0.0`,apiVersion:`1.0`,runtimeSupport:{node:!0,browser:!0,deno:!0,bun:!0},capabilities:[{type:`importer`,name:`svg`,extensions:[`.svg`],mimeTypes:[`image/svg+xml`],canImport:async e=>{let t=Array.isArray(e)?e:[e];for(let e of t)if(typeof e!=`string`||!e.trim().startsWith(`<svg`)&&!e.includes(`<svg`)||!e.includes(`data-diagram-json="`))return!1;return!0},import:async(e,t,n)=>{let r=Array.isArray(e)?e:[e];for(let e of r){let r=Re(e);if(!r)throw Error(`Invalid diagram SVG: missing or corrupted embedded diagram data. Make sure the SVG was exported using diagrams-js SVG export.`);let i=n.getImporter(`json`);i?await i.import(JSON.stringify(r),t,n):await W(t,await z(r),n.lib)}}},{type:`exporter`,name:`svg`,extension:`.svg`,mimeType:`image/svg+xml`,export:async e=>await e.render({format:`svg`})}]}}const q=K(),J={light:{added:{border:`#2EA043`,fill:`rgba(46, 160, 67, 0.1)`,badge:`#2EA043`},removed:{border:`#F85149`,fill:`rgba(248, 81, 73, 0.1)`,badge:`#F85149`},modified:{border:`#D29922`,fill:`rgba(210, 153, 34, 0.1)`,badge:`#D29922`},unchanged:{border:`#8C959F`,fill:`transparent`,badge:`#8C959F`}},dark:{added:{border:`#3FB950`,fill:`rgba(63, 185, 80, 0.15)`,badge:`#3FB950`},removed:{border:`#F85149`,fill:`rgba(248, 81, 73, 0.15)`,badge:`#F85149`},modified:{border:`#D29922`,fill:`rgba(210, 153, 34, 0.15)`,badge:`#D29922`},unchanged:{border:`#6E7681`,fill:`transparent`,badge:`#6E7681`}}};function ze(e){return`${e.label||``}|${e.provider||``}|${e.type||``}|${e.resource||``}`}function Y(e){return`${e.provider||``}|${e.type||``}|${e.resource||``}`}function X(e,t){if(e.size!==t.size)return!1;for(let n of e)if(!t.has(n))return!1;return!0}function Be(e,t,n=[]){if(e===t)return!0;if(typeof e!=typeof t)return!1;if(e===null||t===null||typeof e!=`object`)return e===t;let r=e,i=t,a=Object.keys(r).filter(e=>!n.includes(e)),o=Object.keys(i).filter(e=>!n.includes(e));if(a.length!==o.length)return!1;for(let e of a){if(!o.includes(e))return!1;let t=n.filter(t=>t.startsWith(`${e}.`)).map(t=>t.slice(e.length+1));if(!Be(r[e],i[e],t))return!1}return!0}function Z(e,t,n=``,r=[]){let i=[],a=new Set([...Object.keys(e),...Object.keys(t)]);for(let o of a){let a=n?`${n}.${o}`:o;if(r.includes(a))continue;let s=e[o],c=t[o];if(!(o in e))i.push(`${a} (added)`);else if(!(o in t))i.push(`${a} (removed)`);else if(typeof s==`object`&&s&&typeof c==`object`&&c){let e=Z(s,c,a,r);i.push(...e)}else s!==c&&i.push(`${a}: ${JSON.stringify(s)} → ${JSON.stringify(c)}`)}return i}function Ve(e,t,n={}){let r=typeof e.toJSON==`function`?e.toJSON():e,i=typeof t.toJSON==`function`?t.toJSON():t,a=[];if(n.ignore?.position!==!1&&a.push(`attrs.pos`,`attrs.lp`),n.ignore?.attrs)for(let e of n.ignore.attrs)a.push(`attrs.${e}`);if(n.ignore?.metadata===!0)a.push(`metadata`);else if(Array.isArray(n.ignore?.metadata))for(let e of n.ignore.metadata)a.push(`metadata.${e}`);let o={nodes:new Map,edges:new Map,clusters:[],summary:{added:0,removed:0,modified:0,unchanged:0},meta:{}};r.name!==i.name&&(o.meta.name={before:r.name,after:i.name}),r.theme!==i.theme&&(o.meta.theme={before:r.theme,after:i.theme}),r.direction!==i.direction&&(o.meta.direction={before:r.direction,after:i.direction}),r.curvestyle!==i.curvestyle&&(o.meta.curvestyle={before:r.curvestyle,after:i.curvestyle}),Z(r.graphAttr||{},i.graphAttr||{},``).length>0&&(o.meta.graphAttr={before:r.graphAttr,after:i.graphAttr}),Z(r.nodeAttr||{},i.nodeAttr||{},``).length>0&&(o.meta.nodeAttr={before:r.nodeAttr,after:i.nodeAttr}),Z(r.edgeAttr||{},i.edgeAttr||{},``).length>0&&(o.meta.edgeAttr={before:r.edgeAttr,after:i.edgeAttr});let s=new Map(r.nodes.map(e=>[e.id,e])),c=new Map(i.nodes.map(e=>[e.id,e])),l=new Set,u=new Set,d=new Map,f=new Map(r.nodes.map(e=>[ze(e),e.id]));for(let[e,t]of c){let n=ze(t);if(f.has(n)){let r=f.get(n),i=s.get(r);if(l.has(r)||u.has(e))continue;l.add(r),u.add(e),d.set(e,r);let c=Z(i,t,``,[...a,`id`]);c.length===0?(o.nodes.set(e,{kind:`unchanged`,before:i,after:t}),o.summary.unchanged++):(o.nodes.set(e,{kind:`modified`,before:i,after:t,changes:c}),o.summary.modified++)}}let p=Array.from(s.entries()).filter(([e])=>!l.has(e)),m=Array.from(c.entries()).filter(([e])=>!u.has(e)),h=new Map;for(let[e,t]of p){let n=Y(t);h.has(n)||h.set(n,[]),h.get(n).push(e)}let g=new Map;for(let[e,t]of m){let n=Y(t);g.has(n)||g.set(n,[]),g.get(n).push(e)}function _(e,t,n){let r=new Set;for(let i of t){let t=i.from===e,a=i.to===e;if(!t&&!a)continue;let o=t?i.to:i.from,s=n?o:d.get(o)||o,c=n?l.has(o):d.has(o);s&&c&&r.add(s)}return r}for(let[e,t]of g){let n=h.get(e);if(!n||n.length===0)continue;let a=t.map(e=>({id:e,node:c.get(e),connected:_(e,i.edges||[],!1)})),f=n.map(e=>({id:e,node:s.get(e),connected:_(e,r.edges||[],!0)}));for(let e of a){if(u.has(e.id))continue;let t=f.find(t=>!l.has(t.id)&&X(e.connected,t.connected)&&e.connected.size>0);if(t){l.add(t.id),u.add(e.id),d.set(e.id,t.id);let n=t.node,r=e.node;n.label===r.label?(o.nodes.set(e.id,{kind:`unchanged`,before:n,after:r}),o.summary.unchanged++):(o.nodes.set(e.id,{kind:`modified`,before:n,after:r,changes:[`label: "${n.label}" → "${r.label}"`]}),o.summary.modified++)}}}let v=Array.from(s.entries()).filter(([e])=>!l.has(e)),y=Array.from(c.entries()).filter(([e])=>!u.has(e)),b=new Map;for(let[e,t]of v){let n=Y(t);b.has(n)||b.set(n,[]),b.get(n).push(e)}let x=new Map;for(let[e,t]of y){let n=Y(t);x.has(n)||x.set(n,[]),x.get(n).push(e)}for(let[e,t]of x){let n=b.get(e);if(!n)continue;let r=Math.min(t.length,n.length);for(let e=0;e<r;e++){let r=t[e],i=n[e];if(l.has(i)||u.has(r))continue;let a=s.get(i),f=c.get(r);l.add(i),u.add(r),d.set(r,i),a.label===f.label?(o.nodes.set(r,{kind:`unchanged`,before:a,after:f}),o.summary.unchanged++):(o.nodes.set(r,{kind:`modified`,before:a,after:f,changes:[`label: "${a.label}" → "${f.label}"`]}),o.summary.modified++)}}for(let[e,t]of s)l.has(e)||(o.nodes.set(e,{kind:`removed`,before:t}),o.summary.removed++);for(let[e,t]of c)u.has(e)||(o.nodes.set(e,{kind:`added`,after:t}),o.summary.added++);let S=r.edges||[],C=i.edges||[];function w(e,t){let n,r;return t===`before`?(n=e.from,r=e.to):(n=d.get(e.from)||e.from,r=d.get(e.to)||e.to),`${n}->${r}`}function T(e){return`${e.from}->${e.to}[${e.label||``}]`}let E=new Map;for(let e of S)E.set(w(e,`before`),e);let D=new Map;for(let e of C)D.set(w(e,`after`),e);let O=new Set,k=new Set;for(let[e,t]of D)if(E.has(e)){let n=E.get(e),r=T(n),i=T(t);O.add(r),k.add(i);let s=Z(n,t,``,[...a,`from`,`to`]);s.length===0?(o.edges.set(i,{kind:`unchanged`,before:n,after:t}),o.summary.unchanged++):(o.edges.set(i,{kind:`modified`,before:n,after:t,changes:s}),o.summary.modified++)}for(let e of C){let t=T(e);k.has(t)||(o.edges.set(t,{kind:`added`,after:e}),o.summary.added++)}for(let e of S){let t=T(e);O.has(t)||(o.edges.set(t,{kind:`removed`,before:e}),o.summary.removed++)}function A(e=[],t=[],n=[]){let r=[],i=new Map(e.map(e=>[e.label,e])),a=new Map(t.map(e=>[e.label,e])),s=new Set([...i.keys(),...a.keys()]);for(let e of s){let t=i.get(e),s=a.get(e);if(t&&s){let i=[];Be(t.graphAttr||{},s.graphAttr||{})||i.push(`graphAttr`);let a=A(t.clusters,s.clusters,[...n,e]),c={kind:i.length>0?`modified`:`unchanged`,label:e,before:t,after:s,changes:i.length>0?i:void 0,clusters:a.length>0?a:void 0};r.push(c),i.length>0?o.summary.modified++:o.summary.unchanged++}else t?(r.push({kind:`removed`,label:e,before:t}),o.summary.removed++):s&&(r.push({kind:`added`,label:e,after:s}),o.summary.added++)}return r}return o.clusters=A(r.clusters,i.clusters),o}function He(e,t,n,r,i){let a=J[r],o=e;return o=o.replace(/<g([^>]*)class="node"([^>]*)>([\s\S]*?)<title>([^<]+)<\/title>([\s\S]*?)<\/g>/g,(e,r,o,s,c,l)=>{let u=t.get(c);if(!u)return e;let d=u.kind;if(n===`before`&&d===`added`&&(d=`unchanged`),n===`after`&&d===`removed`&&(d=`unchanged`),d===`unchanged`&&i===`hide`)return``;let f=a[d],p=d===`unchanged`&&i===`dim`?`0.4`:`1`,m=d===`unchanged`?``:`filter: drop-shadow(0 0 4px ${f.border}) drop-shadow(0 0 4px ${f.border});`,h=`${r}data-diff-kind="${d}" ${o}`,g=e.replace(/<g([^>]*)class="node"/,`<g${h}class="node" style="opacity:${p};${m}"`);return d!==`unchanged`&&(g=g.replace(/<ellipse([^>]*)style="([^"]*)"/g,(e,t,n)=>`<ellipse${t}style="${n};fill:${f.fill}"`).replace(/<ellipse(?![^>]*style=)/g,`<ellipse style="fill:${f.fill}"`).replace(/<polygon([^>]*)style="([^"]*)"/g,(e,t,n)=>`<polygon${t}style="${n};fill:${f.fill}"`).replace(/<polygon(?![^>]*style=)/g,`<polygon style="fill:${f.fill}"`)),g}),o=o.replace(/<g([^>]*)class="edge"([^>]*)>([\s\S]*?)<title>([^<]+)<\/title>([\s\S]*?)<\/g>/g,(e,r,o,s,c,l)=>{let u=t.get(c);if(!u)return e;let d=u.kind;if(n===`before`&&d===`added`&&(d=`unchanged`),n===`after`&&d===`removed`&&(d=`unchanged`),d===`unchanged`&&i===`hide`)return``;let f=a[d],p=d===`unchanged`&&i===`dim`?`0.4`:`1`,m=d===`unchanged`?``:`stroke:${f.border};stroke-width:2;filter: drop-shadow(0 0 2px ${f.border});`,h=`${r}data-diff-kind="${d}" ${o}`;return e.replace(/<g([^>]*)class="edge"/,`<g${h}class="edge" style="opacity:${p}"`).replace(/<path/g,`<path style="${m}"`).replace(/<polygon/g,`<polygon style="fill:${f.border};stroke:${f.border}"`)}),o}function Ue(e,t,n,r){let i=J[r.theme],{summary:a,meta:o}=n,s=[];o.name&&s.push(`Name: "${o.name.before}" → "${o.name.after}"`),o.theme&&s.push(`Theme: ${o.theme.before} → ${o.theme.after}`),o.direction&&s.push(`Direction: ${o.direction.before} → ${o.direction.after}`),o.curvestyle&&s.push(`Curve style: ${o.curvestyle.before} → ${o.curvestyle.after}`);let c=[a.added>0?`<span class="pill added">+${a.added}</span>`:``,a.removed>0?`<span class="pill removed">−${a.removed}</span>`:``,a.modified>0?`<span class="pill modified">~${a.modified}</span>`:``].filter(Boolean).join(` `),l=r.layout===`stacked`?`stacked`:`side-by-side`;return`<!DOCTYPE html>
|
|
16
16
|
<html lang="en">
|
|
17
17
|
<head>
|
|
18
18
|
<meta charset="UTF-8">
|
package/package.json
CHANGED
|
@@ -106,21 +106,21 @@ Nodes are matched using a three-phase approach:
|
|
|
106
106
|
|
|
107
107
|
### Phase 1: Fingerprint Matching
|
|
108
108
|
|
|
109
|
-
Nodes with identical `(label, provider,
|
|
109
|
+
Nodes with identical `(label, provider, type, resource)` are matched directly as `unchanged` or `modified`.
|
|
110
110
|
|
|
111
111
|
```typescript
|
|
112
|
-
// Before: { label: "Web Server", provider: "aws",
|
|
113
|
-
// After: { label: "Web Server", provider: "aws",
|
|
112
|
+
// Before: { label: "Web Server", provider: "aws", type: "compute", resource: "EC2" }
|
|
113
|
+
// After: { label: "Web Server", provider: "aws", type: "compute", resource: "EC2" }
|
|
114
114
|
// Result: unchanged
|
|
115
115
|
|
|
116
|
-
// Before: { label: "Web Server", provider: "aws",
|
|
117
|
-
// After: { label: "Web Server v2", provider: "aws",
|
|
116
|
+
// Before: { label: "Web Server", provider: "aws", type: "compute", resource: "EC2" }
|
|
117
|
+
// After: { label: "Web Server v2", provider: "aws", type: "compute", resource: "EC2" }
|
|
118
118
|
// Result: modified (label change)
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
### Phase 2: Label Fingerprint + Edge Connectivity
|
|
122
122
|
|
|
123
|
-
Unmatched nodes with the same `(provider,
|
|
123
|
+
Unmatched nodes with the same `(provider, type, resource)` are matched using edge connectivity:
|
|
124
124
|
|
|
125
125
|
- If edge connectivity patterns match → paired and marked as `modified` (label change)
|
|
126
126
|
- If edge connectivity differs → treated as separate nodes (removed + added)
|
|
@@ -139,7 +139,7 @@ Unmatched nodes with the same `(provider, service, type)` are matched using edge
|
|
|
139
139
|
|
|
140
140
|
### Phase 3: Simple Label Fingerprint
|
|
141
141
|
|
|
142
|
-
Remaining unmatched nodes are matched 1:1 by `(provider,
|
|
142
|
+
Remaining unmatched nodes are matched 1:1 by `(provider, type, resource)`:
|
|
143
143
|
|
|
144
144
|
- Same label → `unchanged`
|
|
145
145
|
- Different labels → `modified`
|
|
@@ -245,12 +245,12 @@ Correct:
|
|
|
245
245
|
// Same provider/type with matching connectivity → detected as modified
|
|
246
246
|
```
|
|
247
247
|
|
|
248
|
-
Keep the same `(provider,
|
|
248
|
+
Keep the same `(provider, type, resource)` and similar edge connectivity for modification detection.
|
|
249
249
|
|
|
250
250
|
### MEDIUM Confusing modified with removed+added
|
|
251
251
|
|
|
252
|
-
- `modified`: Same `(provider,
|
|
253
|
-
- `removed` + `added`: Different labels without matching connectivity, or different `(provider,
|
|
252
|
+
- `modified`: Same `(provider, type, resource)` with matching edge connectivity, but different label
|
|
253
|
+
- `removed` + `added`: Different labels without matching connectivity, or different `(provider, type, resource)`
|
|
254
254
|
|
|
255
255
|
```typescript
|
|
256
256
|
if (nodeDiff.kind === "modified") {
|
|
@@ -273,7 +273,7 @@ if (nodeDiff.kind === "added") {
|
|
|
273
273
|
|
|
274
274
|
### Use Stable Node Identifiers
|
|
275
275
|
|
|
276
|
-
Node matching relies on `(label, provider,
|
|
276
|
+
Node matching relies on `(label, provider, type, resource)`, not IDs:
|
|
277
277
|
|
|
278
278
|
```typescript
|
|
279
279
|
// Good: Consistent identifiers across versions
|
|
@@ -147,8 +147,8 @@ const json = {
|
|
|
147
147
|
id: r.name,
|
|
148
148
|
label: r.name,
|
|
149
149
|
provider: "aws",
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
type: getService(r.type),
|
|
151
|
+
resource: getResourceType(r.type),
|
|
152
152
|
})),
|
|
153
153
|
};
|
|
154
154
|
|
|
@@ -235,10 +235,10 @@ const json = {
|
|
|
235
235
|
},
|
|
236
236
|
],
|
|
237
237
|
};
|
|
238
|
-
// Icons resolved from provider/
|
|
238
|
+
// Icons resolved from provider/type/resource - clean JSON
|
|
239
239
|
```
|
|
240
240
|
|
|
241
|
-
Use provider/
|
|
241
|
+
Use provider/type/resource fields for automatic icon resolution. Avoid embedding icon data URLs unless using Custom nodes.
|
|
242
242
|
|
|
243
243
|
Source: source code - provider-loader.ts dynamic imports
|
|
244
244
|
|
|
@@ -118,7 +118,7 @@ const json = diagram.toJSON();
|
|
|
118
118
|
console.log(JSON.stringify(json, null, 2));
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
-
JSON structure includes nodes with provider/
|
|
121
|
+
JSON structure includes nodes with provider/type/resource for automatic icon resolution, edges, clusters, and all diagram options.
|
|
122
122
|
|
|
123
123
|
### Import from JSON
|
|
124
124
|
|
|
@@ -130,8 +130,8 @@ import { Diagram } from "diagrams-js";
|
|
|
130
130
|
const json = {
|
|
131
131
|
name: "My Architecture",
|
|
132
132
|
nodes: [
|
|
133
|
-
{ id: "web", label: "Web Server", provider: "aws",
|
|
134
|
-
{ id: "db", label: "Database", provider: "aws",
|
|
133
|
+
{ id: "web", label: "Web Server", provider: "aws", type: "compute", resource: "EC2" },
|
|
134
|
+
{ id: "db", label: "Database", provider: "aws", type: "database", resource: "RDS" },
|
|
135
135
|
],
|
|
136
136
|
edges: [{ from: "web", to: "db", label: "SQL" }],
|
|
137
137
|
};
|
|
@@ -89,7 +89,7 @@ The embedded metadata includes:
|
|
|
89
89
|
- **`data-diagram-json`** (root `<svg>`): Base64-encoded full diagram JSON
|
|
90
90
|
- **`data-diagram-version`**: Format version for compatibility
|
|
91
91
|
- **`data-node-id`**, **`data-node-label`**: Per-node identifiers
|
|
92
|
-
- **`data-node-provider`**, **`data-node-
|
|
92
|
+
- **`data-node-provider`**, **`data-node-type`**, **`data-node-resource`**: Provider metadata
|
|
93
93
|
- **`data-node-metadata`**: Base64-encoded custom metadata
|
|
94
94
|
- **`data-cluster-label`**, **`data-cluster-nodes`**: Cluster information
|
|
95
95
|
|