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 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", service: "compute", type: "EC2" },
925
- * { id: "db", label: "Database", provider: "aws", service: "database", type: "RDS" }
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,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`)}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,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`)}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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diagrams-js",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "A TypeScript port of the diagrams Python library for drawing cloud system architecture diagrams as code",
5
5
  "keywords": [
6
6
  "architecture",
@@ -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, service, type)` are matched directly as `unchanged` or `modified`.
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", service: "compute", type: "EC2" }
113
- // After: { label: "Web Server", provider: "aws", service: "compute", type: "EC2" }
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", service: "compute", type: "EC2" }
117
- // After: { label: "Web Server v2", provider: "aws", service: "compute", type: "EC2" }
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, service, type)` are matched using edge connectivity:
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, service, type)`:
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, service, type)` and similar edge connectivity for modification detection.
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, service, type)` with matching edge connectivity, but different label
253
- - `removed` + `added`: Different labels without matching connectivity, or different `(provider, service, type)`
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, service, type)`, not IDs:
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
- service: getService(r.type),
151
- type: getResourceType(r.type),
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/service/type - clean JSON
238
+ // Icons resolved from provider/type/resource - clean JSON
239
239
  ```
240
240
 
241
- Use provider/service/type fields for automatic icon resolution. Avoid embedding icon data URLs unless using Custom nodes.
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/service/type for automatic icon resolution, edges, clusters, and all diagram options.
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", service: "compute", type: "EC2" },
134
- { id: "db", label: "Database", provider: "aws", service: "database", type: "RDS" },
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-service`**, **`data-node-type`**: Provider metadata
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