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.
- package/README.md +47 -0
- package/dist/{chunk-B2D5DMTO.js → chunk-4L6JVKH4.js} +165 -8
- package/dist/chunk-4L6JVKH4.js.map +1 -0
- package/dist/chunk-H62Z76FY.js +1346 -0
- package/dist/chunk-H62Z76FY.js.map +1 -0
- package/dist/chunk-SXK2Z45Z.js +50 -0
- package/dist/chunk-SXK2Z45Z.js.map +1 -0
- package/dist/debug/index.d.ts +50 -3
- package/dist/debug/index.js +64 -6
- package/dist/debug/index.js.map +1 -1
- package/dist/doclet/index.d.ts +152 -31
- package/dist/doclet/index.js +458 -57
- package/dist/doclet/index.js.map +1 -1
- package/dist/doclet/resources/petrinet-diagrams.css +384 -7
- package/dist/doclet/resources/petrinet-diagrams.js +7214 -114
- package/dist/dot-exporter-PMHOQHZ3.js +8 -0
- package/dist/{event-store-BnyHh3TF.d.ts → event-store-2zkXeQkd.d.ts} +1 -1
- package/dist/export/index.d.ts +16 -2
- package/dist/export/index.js +1 -1
- package/dist/index.d.ts +41 -5
- package/dist/index.js +1221 -35
- package/dist/index.js.map +1 -1
- package/dist/petri-net-BDrj4XZE.d.ts +1461 -0
- package/dist/render-P6GROU7J.js +16 -0
- package/dist/render-P6GROU7J.js.map +1 -0
- package/dist/verification/index.d.ts +3 -144
- package/dist/verification/index.js +30 -1214
- package/dist/verification/index.js.map +1 -1
- package/dist/viewer/index.d.ts +196 -0
- package/dist/viewer/index.js +442 -0
- package/dist/viewer/index.js.map +1 -0
- package/dist/viewer/viewer-static.iife.js +3 -0
- package/dist/viewer/viewer.css +503 -0
- package/dist/viewer/viewer.iife.js +7219 -0
- package/package.json +17 -8
- package/dist/chunk-B2D5DMTO.js.map +0 -1
- package/dist/chunk-VQ4XMJTD.js +0 -107
- package/dist/chunk-VQ4XMJTD.js.map +0 -1
- package/dist/dot-exporter-3CVCH6J4.js +0 -8
- package/dist/petri-net-D-GN9g_D.d.ts +0 -570
- /package/dist/{dot-exporter-3CVCH6J4.js.map → dot-exporter-PMHOQHZ3.js.map} +0 -0
package/dist/doclet/index.js
CHANGED
|
@@ -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
|
|
50
|
+
let value;
|
|
43
51
|
if (isCall) {
|
|
44
52
|
if (typeof exported !== "function") return null;
|
|
45
53
|
const result = exported();
|
|
46
|
-
|
|
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
|
-
|
|
60
|
+
value = exported;
|
|
49
61
|
}
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
85
117
|
}
|
|
86
|
-
function
|
|
87
|
-
|
|
118
|
+
function escapeAttr(text) {
|
|
119
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
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.
|
|
142
|
+
<script>if(!window._libpetriViewerInit){window._libpetriViewerInit=true;
|
|
92
143
|
${js()}
|
|
93
144
|
}</script>
|
|
94
|
-
<div class="
|
|
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"><?></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-
|
|
449
|
+
const mod = await import("../dot-exporter-PMHOQHZ3.js");
|
|
112
450
|
return mod.dotExport;
|
|
113
451
|
}
|
|
114
|
-
var
|
|
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
|
-
|
|
120
|
-
|
|
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 ===
|
|
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
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|