libpetri 1.8.5 → 2.0.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.
Files changed (41) hide show
  1. package/README.md +47 -0
  2. package/dist/{chunk-B2D5DMTO.js → chunk-4L6JVKH4.js} +165 -8
  3. package/dist/chunk-4L6JVKH4.js.map +1 -0
  4. package/dist/chunk-H62Z76FY.js +1346 -0
  5. package/dist/chunk-H62Z76FY.js.map +1 -0
  6. package/dist/chunk-SXK2Z45Z.js +50 -0
  7. package/dist/chunk-SXK2Z45Z.js.map +1 -0
  8. package/dist/debug/index.d.ts +50 -3
  9. package/dist/debug/index.js +64 -6
  10. package/dist/debug/index.js.map +1 -1
  11. package/dist/doclet/index.d.ts +152 -31
  12. package/dist/doclet/index.js +458 -57
  13. package/dist/doclet/index.js.map +1 -1
  14. package/dist/doclet/resources/petrinet-diagrams.css +384 -7
  15. package/dist/doclet/resources/petrinet-diagrams.js +7214 -114
  16. package/dist/dot-exporter-PMHOQHZ3.js +8 -0
  17. package/dist/{event-store-BnyHh3TF.d.ts → event-store-2zkXeQkd.d.ts} +1 -1
  18. package/dist/export/index.d.ts +16 -2
  19. package/dist/export/index.js +1 -1
  20. package/dist/index.d.ts +41 -5
  21. package/dist/index.js +1221 -35
  22. package/dist/index.js.map +1 -1
  23. package/dist/petri-net-BDrj4XZE.d.ts +1461 -0
  24. package/dist/render-P6GROU7J.js +16 -0
  25. package/dist/render-P6GROU7J.js.map +1 -0
  26. package/dist/verification/index.d.ts +3 -144
  27. package/dist/verification/index.js +30 -1214
  28. package/dist/verification/index.js.map +1 -1
  29. package/dist/viewer/index.d.ts +196 -0
  30. package/dist/viewer/index.js +442 -0
  31. package/dist/viewer/index.js.map +1 -0
  32. package/dist/viewer/viewer-static.iife.js +3 -0
  33. package/dist/viewer/viewer.css +503 -0
  34. package/dist/viewer/viewer.iife.js +7219 -0
  35. package/package.json +17 -8
  36. package/dist/chunk-B2D5DMTO.js.map +0 -1
  37. package/dist/chunk-VQ4XMJTD.js +0 -107
  38. package/dist/chunk-VQ4XMJTD.js.map +0 -1
  39. package/dist/dot-exporter-3CVCH6J4.js +0 -8
  40. package/dist/petri-net-D-GN9g_D.d.ts +0 -570
  41. /package/dist/{dot-exporter-3CVCH6J4.js.map → dot-exporter-PMHOQHZ3.js.map} +0 -0
@@ -1,3 +1,11 @@
1
+ import {
2
+ FONT,
3
+ dotExport,
4
+ nodeStyle,
5
+ sanitize
6
+ } from "../chunk-4L6JVKH4.js";
7
+ import "../chunk-FN773SSE.js";
8
+
1
9
  // src/doclet/petri-net-plugin.ts
2
10
  import {
3
11
  JSX
@@ -39,29 +47,53 @@ async function resolveNet(reference, sourceFilePath) {
39
47
  if (!mod) return null;
40
48
  const exported = mod[exportName];
41
49
  if (exported == null) return null;
42
- let net;
50
+ let value;
43
51
  if (isCall) {
44
52
  if (typeof exported !== "function") return null;
45
53
  const result = exported();
46
- net = "net" in result ? result.net : result;
54
+ if (result !== null && typeof result === "object" && "net" in result && isPetriNetLike(result.net)) {
55
+ value = result.net;
56
+ } else {
57
+ value = result;
58
+ }
47
59
  } else {
48
- net = exported;
60
+ value = exported;
49
61
  }
50
- if (typeof net?.name !== "string" || !(net?.transitions instanceof Set)) {
51
- return null;
62
+ return classify(value);
63
+ }
64
+ function classify(value) {
65
+ if (value === null || typeof value !== "object") return null;
66
+ if (isInstanceLike(value)) {
67
+ const inst = value;
68
+ return { kind: "instance", value: inst, title: `${inst.def.name} :: ${inst.prefix}` };
52
69
  }
53
- return { net, title: net.name };
70
+ if (isSubnetDefLike(value)) {
71
+ const def = value;
72
+ return { kind: "subnet", value: def, title: def.name };
73
+ }
74
+ if (isPetriNetLike(value)) {
75
+ const net = value;
76
+ return { kind: "net", value: net, title: net.name };
77
+ }
78
+ return null;
54
79
  }
55
-
56
- // src/doclet/svg-renderer.ts
57
- async function dotToSvg(dot) {
58
- const { instance } = await import("@viz-js/viz");
59
- const viz = await instance();
60
- let svg = viz.renderString(dot, { format: "svg", engine: "dot" });
61
- const svgStart = svg.indexOf("<svg");
62
- if (svgStart > 0) svg = svg.substring(svgStart);
63
- svg = svg.replace(/\s+(?:width|height)="[^"]*"/g, "");
64
- return svg;
80
+ function isPetriNetLike(value) {
81
+ if (value === null || typeof value !== "object") return false;
82
+ const candidate = value;
83
+ return typeof candidate.name === "string" && candidate.transitions instanceof Set;
84
+ }
85
+ function isSubnetDefLike(value) {
86
+ if (value === null || typeof value !== "object") return false;
87
+ const candidate = value;
88
+ if (typeof candidate.name !== "string") return false;
89
+ if (!isPetriNetLike(candidate.body)) return false;
90
+ const iface = candidate.iface;
91
+ return iface !== void 0 && iface !== null && iface.ports instanceof Map && iface.channels instanceof Map;
92
+ }
93
+ function isInstanceLike(value) {
94
+ if (value === null || typeof value !== "object") return false;
95
+ const candidate = value;
96
+ return typeof candidate.prefix === "string" && isSubnetDefLike(candidate.def) && isPetriNetLike(candidate.renamedBody);
65
97
  }
66
98
 
67
99
  // src/doclet/diagram-renderer.ts
@@ -83,41 +115,351 @@ function js() {
83
115
  function escapeHtml(text) {
84
116
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
85
117
  }
86
- function renderSvg(title, svgContent, dotSource) {
87
- const titleHtml = title ? `<h4>${escapeHtml(title)}</h4>
118
+ function escapeAttr(text) {
119
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
120
+ }
121
+ function renderSvg(title, dotSource) {
122
+ return renderInternal(
123
+ title ?? null,
124
+ /* headerHtml */
125
+ null,
126
+ dotSource
127
+ );
128
+ }
129
+ function renderSubnetSvg(title, headerHtml, dotSource) {
130
+ return renderInternal(title ?? null, headerHtml, dotSource);
131
+ }
132
+ var diagramIdCounter = 0;
133
+ function renderInternal(title, headerHtml, dotSource) {
134
+ const titleHtml = title !== null ? `<h4>${escapeHtml(title)}</h4>
88
135
  ` : "";
89
- const summaryText = title ? "View DOT Source" : "View Source";
136
+ const summaryText = title !== null ? "View DOT Source" : "View Source";
137
+ const diagramClass = headerHtml !== null ? "petrinet-diagram subnet-diagram" : "petrinet-diagram";
138
+ const headerBlock = headerHtml !== null ? headerHtml : "";
139
+ const dotAttr = escapeAttr(dotSource);
140
+ const containerId = `libpetri-diagram-${++diagramIdCounter}`;
90
141
  return `<style>${css()}</style>
91
- <script>if(!window._petriNetDiagramsInit){window._petriNetDiagramsInit=true;
142
+ <script>if(!window._libpetriViewerInit){window._libpetriViewerInit=true;
92
143
  ${js()}
93
144
  }</script>
94
- <div class="petrinet-diagram">
95
- ${titleHtml}<div class="diagram-container">
96
- <div class="diagram-controls">
97
- <button class="diagram-btn btn-reset" title="Reset zoom">Reset</button>
98
- <button class="diagram-btn btn-fullscreen" onclick="PetriNetDiagrams.toggleFullscreen(this)">Fullscreen</button>
99
- </div>
100
- ${svgContent}
101
- </div>
145
+ <div class="${diagramClass}">
146
+ ${titleHtml}${headerBlock}<div class="diagram-container" id="${containerId}" data-libpetri-diagram="true" data-dot="${dotAttr}"></div>
102
147
  <details>
103
148
  <summary>${summaryText}</summary>
104
149
  <pre><code>${escapeHtml(dotSource)}</code></pre>
105
150
  </details>
106
- </div>`;
151
+ </div>
152
+ <script>(function(){function mount(){var el=document.getElementById(${JSON.stringify(containerId)});if(!el)return;if(el.dataset.libpetriMounted)return;if(!window.LibpetriViewer||typeof window.LibpetriViewer.mount!=='function'){return setTimeout(mount,30);}el.dataset.libpetriMounted='1';try{window.LibpetriViewer.mount(el.dataset.dot,el,{chrome:true});}catch(e){console.error('[libpetri] mount failed',e);}}if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',mount);}else{mount();}})();</script>`;
153
+ }
154
+
155
+ // src/doclet/subnet-header.ts
156
+ function forSubnetDef(def, interfaceOnly2) {
157
+ const sb = [];
158
+ sb.push(`<div class="subnet-header">
159
+ `);
160
+ sb.push(` <div class="subnet-header-row">
161
+ `);
162
+ sb.push(` <span class="subnet-header-label">subnet</span>
163
+ `);
164
+ sb.push(` <span class="subnet-header-name">${escapeHtml(def.name)}</span>
165
+ `);
166
+ sb.push(` <span class="subnet-header-param">&lt;?&gt;</span>
167
+ `);
168
+ if (interfaceOnly2) {
169
+ sb.push(` <span class="subnet-header-badge subnet-header-badge-interface">interface only</span>
170
+ `);
171
+ }
172
+ sb.push(` </div>
173
+ `);
174
+ sb.push(renderPortChannelBadges(def.iface));
175
+ sb.push(`</div>
176
+ `);
177
+ return sb.join("");
178
+ }
179
+ function forInstance(instance) {
180
+ const def = instance.def;
181
+ const sb = [];
182
+ sb.push(`<div class="subnet-header">
183
+ `);
184
+ sb.push(` <div class="subnet-header-row">
185
+ `);
186
+ sb.push(` <span class="subnet-header-label">instance</span>
187
+ `);
188
+ sb.push(` <span class="subnet-header-name">${escapeHtml(instance.prefix)}</span>
189
+ `);
190
+ sb.push(` <span class="subnet-header-of">of</span>
191
+ `);
192
+ sb.push(` <span class="subnet-header-name">${escapeHtml(def.name)}</span>
193
+ `);
194
+ const params = instance.params;
195
+ if (params !== null && params !== void 0) {
196
+ sb.push(` <span class="subnet-header-params">params=${escapeHtml(String(params))}</span>
197
+ `);
198
+ }
199
+ sb.push(` </div>
200
+ `);
201
+ sb.push(renderPortChannelBadges(def.iface));
202
+ sb.push(`</div>
203
+ `);
204
+ return sb.join("");
205
+ }
206
+ function renderPortChannelBadges(iface) {
207
+ if (iface.ports.size === 0 && iface.channels.size === 0) {
208
+ return "";
209
+ }
210
+ const sb = [];
211
+ sb.push(` <div class="subnet-header-badges">
212
+ `);
213
+ for (const port of iface.ports.values()) {
214
+ const kind = portKind(port);
215
+ sb.push(` <span class="interface-port-badge interface-port-badge-${kind}">`);
216
+ sb.push(`<span class="badge-direction">${kind}</span>`);
217
+ sb.push(`<span class="badge-name">${escapeHtml(port.name)}</span>`);
218
+ sb.push(`</span>
219
+ `);
220
+ }
221
+ for (const channel of iface.channels.values()) {
222
+ sb.push(` <span class="interface-channel-badge">`);
223
+ sb.push(`<span class="badge-direction">channel</span>`);
224
+ sb.push(`<span class="badge-name">${escapeHtml(channel.name)}</span>`);
225
+ sb.push(`</span>
226
+ `);
227
+ }
228
+ sb.push(` </div>
229
+ `);
230
+ return sb.join("");
231
+ }
232
+ function portKind(port) {
233
+ return port.direction;
234
+ }
235
+
236
+ // src/doclet/subnet-dot-export.ts
237
+ function fullBody(def) {
238
+ let dot = dotExport(def.body);
239
+ const portIds = portNodeIds(def.iface);
240
+ const channelIds = channelNodeIds(def.iface);
241
+ for (const portId of portIds) {
242
+ dot = restyleNode(dot, portId, nodeStyle("interface-port"));
243
+ }
244
+ for (const chId of channelIds) {
245
+ dot = restyleNode(dot, chId, nodeStyle("sync-channel"));
246
+ }
247
+ if (portIds.size > 0 || channelIds.size > 0) {
248
+ let iface = "";
249
+ iface += ` subgraph cluster_iface_${sanitize(def.name)} {
250
+ `;
251
+ iface += ` label="interface";
252
+ `;
253
+ iface += ` style="rounded,dashed";
254
+ `;
255
+ iface += ` bgcolor="#F0F6FF";
256
+ `;
257
+ iface += ` penwidth=2;
258
+ `;
259
+ for (const id of portIds) {
260
+ iface += ` ${id};
261
+ `;
262
+ }
263
+ for (const id of channelIds) {
264
+ iface += ` ${id};
265
+ `;
266
+ }
267
+ iface += ` }
268
+ `;
269
+ const lastBrace = dot.lastIndexOf("}");
270
+ if (lastBrace > 0) {
271
+ dot = dot.substring(0, lastBrace) + iface + dot.substring(lastBrace);
272
+ }
273
+ }
274
+ return dot;
275
+ }
276
+ function interfaceOnly(def) {
277
+ const sb = [];
278
+ sb.push(`digraph ${sanitize(def.name)} {
279
+ `);
280
+ sb.push(` rankdir=LR;
281
+ `);
282
+ sb.push(` nodesep=0.5;
283
+ `);
284
+ sb.push(` ranksep=0.6;
285
+ `);
286
+ sb.push(` fontname="${FONT.family}";
287
+ `);
288
+ sb.push(` node [fontname="${FONT.family}", fontsize=12];
289
+ `);
290
+ sb.push(` edge [fontname="${FONT.family}", fontsize=10];
291
+ `);
292
+ sb.push("\n");
293
+ const stubId = "stub_" + sanitize(def.name);
294
+ const inputs = [];
295
+ const outputs = [];
296
+ const inouts = [];
297
+ for (const port of def.iface.ports.values()) {
298
+ switch (port.direction) {
299
+ case "input":
300
+ inputs.push(port);
301
+ break;
302
+ case "output":
303
+ outputs.push(port);
304
+ break;
305
+ case "inout":
306
+ inouts.push(port);
307
+ break;
308
+ }
309
+ }
310
+ for (const port of inputs) emitPortNode(sb, port, "input");
311
+ for (const port of outputs) emitPortNode(sb, port, "output");
312
+ for (const port of inouts) emitPortNode(sb, port, "inout");
313
+ const syncStyle = nodeStyle("sync-channel");
314
+ for (const ch of def.iface.channels.values()) {
315
+ sb.push(` ${channelNodeId(ch)} [`);
316
+ sb.push(`label="\u21C4 ${escapeLabel(ch.name)}", `);
317
+ sb.push(`shape=${syncStyle.shape}, `);
318
+ sb.push(`style="filled", `);
319
+ sb.push(`fillcolor="${syncStyle.fill}", `);
320
+ sb.push(`color="${syncStyle.stroke}", `);
321
+ sb.push(`penwidth=${trimNumber(syncStyle.penwidth)}`);
322
+ sb.push(`];
323
+ `);
324
+ }
325
+ sb.push(` ${stubId} [`);
326
+ sb.push(`label="${escapeLabel(def.name)}\\n(internals omitted)", `);
327
+ sb.push(`shape=box, style="filled,dashed,rounded", `);
328
+ sb.push(`fillcolor="#FFFFFF", color="#666666", penwidth=1, `);
329
+ sb.push(`fontsize=11`);
330
+ sb.push(`];
331
+ `);
332
+ sb.push("\n");
333
+ for (const port of inputs) {
334
+ sb.push(` ${portNodeId(port)} -> ${stubId} [color="#1d4ed8", arrowhead=normal];
335
+ `);
336
+ }
337
+ for (const port of outputs) {
338
+ sb.push(` ${stubId} -> ${portNodeId(port)} [color="#1d4ed8", arrowhead=normal];
339
+ `);
340
+ }
341
+ for (const port of inouts) {
342
+ sb.push(` ${portNodeId(port)} -> ${stubId} [color="#1d4ed8", arrowhead=normalnormal, dir=both];
343
+ `);
344
+ }
345
+ for (const ch of def.iface.channels.values()) {
346
+ sb.push(` ${channelNodeId(ch)} -> ${stubId} [color="#6d28d9", arrowhead=normal, style=dashed];
347
+ `);
348
+ }
349
+ sb.push(`}
350
+ `);
351
+ return sb.join("");
352
+ }
353
+ function emitPortNode(sb, port, direction) {
354
+ const style = nodeStyle("interface-port");
355
+ sb.push(` ${portNodeId(port)} [`);
356
+ sb.push(`label="${escapeLabel(port.name)}", `);
357
+ sb.push(`xlabel="${escapeLabel(direction)}", `);
358
+ sb.push(`shape=${style.shape}, `);
359
+ sb.push(`style="filled,${style.style ?? "solid"}", `);
360
+ sb.push(`fillcolor="${style.fill}", `);
361
+ sb.push(`color="${style.stroke}", `);
362
+ sb.push(`penwidth=${trimNumber(style.penwidth)}`);
363
+ if (style.width !== void 0) {
364
+ sb.push(`, width=${trimNumber(style.width)}, fixedsize=true`);
365
+ }
366
+ sb.push(`];
367
+ `);
368
+ }
369
+ function portNodeId(port) {
370
+ return "p_" + sanitize(port.place.name);
371
+ }
372
+ function channelNodeId(ch) {
373
+ return "t_" + sanitize(ch.transition.name);
374
+ }
375
+ function portNodeIds(iface) {
376
+ const ids = /* @__PURE__ */ new Set();
377
+ for (const port of iface.ports.values()) ids.add(portNodeId(port));
378
+ return ids;
379
+ }
380
+ function channelNodeIds(iface) {
381
+ const ids = /* @__PURE__ */ new Set();
382
+ for (const ch of iface.channels.values()) ids.add(channelNodeId(ch));
383
+ return ids;
384
+ }
385
+ function restyleNode(dot, nodeId, style) {
386
+ const marker = nodeId + " [";
387
+ let start = dot.indexOf("\n " + marker);
388
+ if (start < 0) start = dot.indexOf(" " + marker);
389
+ if (start < 0) return dot;
390
+ if (dot.charAt(start) === "\n") start += 1;
391
+ const lineEnd = dot.indexOf("];", start);
392
+ if (lineEnd < 0) return dot;
393
+ let line = dot.substring(start, lineEnd + 2);
394
+ line = replaceAttr(line, "fillcolor", style.fill);
395
+ line = replaceAttr(line, "color", style.stroke);
396
+ line = replaceAttr(line, "penwidth", trimNumber(style.penwidth));
397
+ if (style.style !== void 0) {
398
+ line = replaceAttr(line, "style", `"filled,${style.style}"`);
399
+ }
400
+ return dot.substring(0, start) + line + dot.substring(lineEnd + 2);
401
+ }
402
+ function replaceAttr(line, key, value) {
403
+ const quotedPat = key + '="';
404
+ const qStart = findAttrStart(line, quotedPat);
405
+ if (qStart >= 0) {
406
+ const valStart2 = qStart + quotedPat.length;
407
+ const valEnd2 = line.indexOf('"', valStart2);
408
+ if (valEnd2 < 0) return line;
409
+ let v = value;
410
+ if (v.startsWith('"') && v.endsWith('"') && v.length >= 2) {
411
+ v = v.substring(1, v.length - 1);
412
+ }
413
+ return line.substring(0, valStart2) + v + line.substring(valEnd2);
414
+ }
415
+ const unquoted = key + "=";
416
+ const uStart = findAttrStart(line, unquoted);
417
+ if (uStart < 0) return line;
418
+ const valStart = uStart + unquoted.length;
419
+ let valEnd = valStart;
420
+ while (valEnd < line.length) {
421
+ const c = line.charAt(valEnd);
422
+ if (c === "," || c === "]") break;
423
+ valEnd++;
424
+ }
425
+ return line.substring(0, valStart) + value + line.substring(valEnd);
426
+ }
427
+ function findAttrStart(line, pattern) {
428
+ let from = 0;
429
+ while (from < line.length) {
430
+ const idx = line.indexOf(pattern, from);
431
+ if (idx < 0) return -1;
432
+ if (idx === 0) return idx;
433
+ const prev = line.charAt(idx - 1);
434
+ if (!/[A-Za-z0-9_]/.test(prev)) return idx;
435
+ from = idx + 1;
436
+ }
437
+ return -1;
438
+ }
439
+ function trimNumber(d) {
440
+ if (Number.isInteger(d)) return String(d);
441
+ return String(d);
442
+ }
443
+ function escapeLabel(s) {
444
+ return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
107
445
  }
108
446
 
109
447
  // src/doclet/petri-net-plugin.ts
110
448
  async function getDotExport() {
111
- const mod = await import("../dot-exporter-3CVCH6J4.js");
449
+ const mod = await import("../dot-exporter-PMHOQHZ3.js");
112
450
  return mod.dotExport;
113
451
  }
114
- var TAG_NAME = "@petrinet";
452
+ var TAG_PETRINET = "@petrinet";
453
+ var TAG_SUBNET = "@subnet";
115
454
  var htmlCache = /* @__PURE__ */ new Map();
116
455
  function load(app) {
117
456
  app.on("bootstrapEnd", () => {
118
457
  const blockTags = app.options.getValue("blockTags");
119
- if (!blockTags.includes(TAG_NAME)) {
120
- app.options.setValue("blockTags", [...blockTags, TAG_NAME]);
458
+ const additions = [];
459
+ if (!blockTags.includes(TAG_PETRINET)) additions.push(TAG_PETRINET);
460
+ if (!blockTags.includes(TAG_SUBNET)) additions.push(TAG_SUBNET);
461
+ if (additions.length > 0) {
462
+ app.options.setValue("blockTags", [...blockTags, ...additions]);
121
463
  }
122
464
  });
123
465
  app.renderer.preRenderAsyncJobs.push(async (output) => {
@@ -128,7 +470,7 @@ function load(app) {
128
470
  const cached = htmlCache.get(reflection.id);
129
471
  if (!cached) return JSX.createElement(JSX.Raw, { html: "" });
130
472
  for (const tag of comment.blockTags) {
131
- if (tag.tag === TAG_NAME) {
473
+ if (tag.tag === TAG_PETRINET || tag.tag === TAG_SUBNET) {
132
474
  tag.skipRendering = true;
133
475
  }
134
476
  }
@@ -140,19 +482,19 @@ async function processProject(app, project) {
140
482
  for (const reflection of reflections) {
141
483
  const comment = reflection.comment;
142
484
  if (!comment) continue;
143
- const petrinetTags = comment.blockTags.filter(
144
- (tag) => tag.tag === TAG_NAME
145
- );
146
- if (petrinetTags.length === 0) continue;
147
- for (const tag of petrinetTags) {
485
+ for (const tag of comment.blockTags) {
486
+ const isPetrinet = tag.tag === TAG_PETRINET;
487
+ const isSubnet = tag.tag === TAG_SUBNET;
488
+ if (!isPetrinet && !isSubnet) continue;
148
489
  const reference = tagContent(tag);
149
490
  try {
150
- const html = await generateDiagram(reference, reflection, app);
491
+ const html = isSubnet ? await generateInterfaceOnly(reference, reflection) : await generateDiagram(reference, reflection);
151
492
  const existing = htmlCache.get(reflection.id) ?? "";
152
493
  htmlCache.set(reflection.id, existing + html);
153
494
  } catch (e) {
154
495
  const msg = e instanceof Error ? e.message : String(e);
155
- app.logger.warn(`@petrinet error for ${reflection.getFriendlyFullName()}: ${msg}`);
496
+ const tagName = isSubnet ? "@subnet" : "@petrinet";
497
+ app.logger.warn(`${tagName} error for ${reflection.getFriendlyFullName()}: ${msg}`);
156
498
  const html = errorHtml(`Error generating diagram for '${reference}': ${msg}`);
157
499
  const existing = htmlCache.get(reflection.id) ?? "";
158
500
  htmlCache.set(reflection.id, existing + html);
@@ -160,7 +502,7 @@ async function processProject(app, project) {
160
502
  }
161
503
  }
162
504
  }
163
- async function generateDiagram(reference, reflection, app) {
505
+ async function generateDiagram(reference, reflection) {
164
506
  const trimmed = reference.trim();
165
507
  if (!trimmed) {
166
508
  return errorHtml(`Empty @petrinet reference on ${reflection.getFriendlyFullName()}`);
@@ -171,21 +513,64 @@ async function generateDiagram(reference, reflection, app) {
171
513
  }
172
514
  const resolved = await resolveNet(trimmed, sourceFile);
173
515
  if (!resolved) {
174
- return errorHtml(`Cannot resolve PetriNet: ${trimmed}`);
175
- }
176
- const dotExport = await getDotExport();
177
- const dot = dotExport(resolved.net);
178
- try {
179
- const svg = await dotToSvg(dot);
180
- return renderSvg(resolved.title, svg, dot);
181
- } catch (e) {
182
- app.logger.warn(`SVG rendering failed, falling back to DOT source: ${e}`);
183
- return renderSvg(
184
- resolved.title,
185
- `<pre><code>${escapeHtml(dot)}</code></pre>`,
186
- dot
187
- );
516
+ return errorHtml(`Cannot resolve PetriNet/SubnetDef/Instance: ${trimmed}`);
188
517
  }
518
+ return await renderResolved(
519
+ resolved,
520
+ /* interfaceOnly */
521
+ false
522
+ );
523
+ }
524
+ async function generateInterfaceOnly(reference, reflection) {
525
+ const trimmed = reference.trim();
526
+ if (!trimmed) {
527
+ return errorHtml(`Empty @subnet reference on ${reflection.getFriendlyFullName()}`);
528
+ }
529
+ const sourceFile = getSourceFilePath(reflection);
530
+ if (!sourceFile) {
531
+ return errorHtml(`Cannot determine source file for ${reflection.getFriendlyFullName()}`);
532
+ }
533
+ const resolved = await resolveNet(trimmed, sourceFile);
534
+ if (!resolved) {
535
+ return errorHtml(`Cannot resolve SubnetDef: ${trimmed}`);
536
+ }
537
+ return await renderResolved(
538
+ resolved,
539
+ /* interfaceOnly */
540
+ true
541
+ );
542
+ }
543
+ async function renderResolved(resolved, interfaceOnly2) {
544
+ switch (resolved.kind) {
545
+ case "net": {
546
+ const dotExport2 = await getDotExport();
547
+ const dot = dotExport2(resolved.value);
548
+ return renderDiagramHtml(
549
+ resolved.title,
550
+ dot,
551
+ /* header */
552
+ null
553
+ );
554
+ }
555
+ case "subnet": {
556
+ const dot = interfaceOnly2 ? interfaceOnly(resolved.value) : fullBody(resolved.value);
557
+ const header = forSubnetDef(resolved.value, interfaceOnly2);
558
+ const title = resolved.value.name + (interfaceOnly2 ? " (interface)" : "");
559
+ return renderDiagramHtml(title, dot, header);
560
+ }
561
+ case "instance": {
562
+ const dotExport2 = await getDotExport();
563
+ const dot = dotExport2(resolved.value.renamedBody);
564
+ const header = forInstance(resolved.value);
565
+ return renderDiagramHtml(resolved.title, dot, header);
566
+ }
567
+ }
568
+ }
569
+ function renderDiagramHtml(title, dot, header) {
570
+ if (header !== null) {
571
+ return renderSubnetSvg(title, header, dot);
572
+ }
573
+ return renderSvg(title, dot);
189
574
  }
190
575
  function tagContent(tag) {
191
576
  return tag.content.map((part) => part.text).join("").trim();
@@ -205,11 +590,27 @@ function errorHtml(message) {
205
590
  <strong>@petrinet Error:</strong> ${escapeHtml(message)}
206
591
  </div>`;
207
592
  }
593
+
594
+ // src/doclet/svg-renderer.ts
595
+ async function dotToSvg(dot) {
596
+ const { instance } = await import("@viz-js/viz");
597
+ const viz = await instance();
598
+ let svg = viz.renderString(dot, { format: "svg", engine: "dot" });
599
+ const svgStart = svg.indexOf("<svg");
600
+ if (svgStart > 0) svg = svg.substring(svgStart);
601
+ svg = svg.replace(/\s+(?:width|height)="[^"]*"/g, "");
602
+ return svg;
603
+ }
208
604
  export {
209
605
  dotToSvg,
210
606
  escapeHtml,
607
+ forInstance,
608
+ forSubnetDef,
211
609
  load,
610
+ renderSubnetSvg,
212
611
  renderSvg,
213
- resolveNet
612
+ resolveNet,
613
+ fullBody as subnetFullBody,
614
+ interfaceOnly as subnetInterfaceOnly
214
615
  };
215
616
  //# sourceMappingURL=index.js.map