libpetri 1.8.3 → 1.8.5
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/{chunk-2HWR2675.js → chunk-B2D5DMTO.js} +93 -34
- package/dist/chunk-B2D5DMTO.js.map +1 -0
- package/dist/debug/index.js +1 -1
- package/dist/doclet/index.js +1 -1
- package/dist/doclet/resources/petrinet-diagrams.js +10 -2
- package/dist/dot-exporter-3CVCH6J4.js +8 -0
- package/dist/export/index.d.ts +12 -6
- package/dist/export/index.js +1 -1
- package/dist/render-dom/index.d.ts +67 -0
- package/dist/render-dom/index.js +31 -0
- package/dist/render-dom/index.js.map +1 -0
- package/package.json +15 -2
- package/dist/chunk-2HWR2675.js.map +0 -1
- package/dist/dot-exporter-DTLQPW5H.js +0 -8
- /package/dist/{dot-exporter-DTLQPW5H.js.map → dot-exporter-3CVCH6J4.js.map} +0 -0
|
@@ -10,14 +10,17 @@ var NODE_STYLES = {
|
|
|
10
10
|
start: { shape: "circle", fill: "#d4edda", stroke: "#28a745", penwidth: 2, width: 0.35 },
|
|
11
11
|
end: { shape: "doublecircle", fill: "#cce5ff", stroke: "#004085", penwidth: 2, width: 0.35 },
|
|
12
12
|
environment: { shape: "circle", fill: "#f8d7da", stroke: "#721c24", penwidth: 2, style: "dashed", width: 0.35 },
|
|
13
|
-
transition: { shape: "box", fill: "#fff3cd", stroke: "#856404", penwidth: 1, height: 0.4, width: 0.8 }
|
|
13
|
+
transition: { shape: "box", fill: "#fff3cd", stroke: "#856404", penwidth: 1, height: 0.4, width: 0.8 },
|
|
14
|
+
"xor-junction": { shape: "diamond", fill: "#FFFFFF", stroke: "#333333", penwidth: 1, height: 0.3, width: 0.3 },
|
|
15
|
+
"and-junction": { shape: "diamond", fill: "#FFFFFF", stroke: "#333333", penwidth: 1, height: 0.3, width: 0.3 }
|
|
14
16
|
};
|
|
15
17
|
var EDGE_STYLES = {
|
|
16
18
|
input: { color: "#333333", style: "solid", arrowhead: "normal" },
|
|
17
19
|
output: { color: "#333333", style: "solid", arrowhead: "normal" },
|
|
18
20
|
inhibitor: { color: "#dc3545", style: "solid", arrowhead: "odot" },
|
|
19
21
|
read: { color: "#6c757d", style: "dashed", arrowhead: "normal" },
|
|
20
|
-
reset: { color: "#fd7e14", style: "bold", arrowhead: "normal", penwidth: 2 }
|
|
22
|
+
reset: { color: "#fd7e14", style: "bold", arrowhead: "normal", penwidth: 2 },
|
|
23
|
+
"reset-output": { color: "#fd7e14", style: "bold", arrowhead: "normal", penwidth: 2 }
|
|
21
24
|
};
|
|
22
25
|
var FONT = { family: "Helvetica,Arial,sans-serif", nodeSize: 12, edgeSize: 10 };
|
|
23
26
|
var GRAPH = { nodesep: 0.5, ranksep: 0.75, forcelabels: true, overlap: false, outputorder: "edgesfirst" };
|
|
@@ -75,6 +78,9 @@ function mapToGraph(net, config = DEFAULT_DOT_CONFIG) {
|
|
|
75
78
|
}
|
|
76
79
|
for (const t of net.transitions) {
|
|
77
80
|
const tid = "t_" + sanitize(t.name);
|
|
81
|
+
const tSanitized = sanitize(t.name);
|
|
82
|
+
const resetPlaces = new Set(t.resets.map((r) => r.place.name));
|
|
83
|
+
const combined = /* @__PURE__ */ new Set();
|
|
78
84
|
for (const spec of t.inputSpecs) {
|
|
79
85
|
const pid = "p_" + sanitize(spec.place.name);
|
|
80
86
|
const inputStyle = edgeStyle("input");
|
|
@@ -101,7 +107,15 @@ function mapToGraph(net, config = DEFAULT_DOT_CONFIG) {
|
|
|
101
107
|
});
|
|
102
108
|
}
|
|
103
109
|
if (t.outputSpec !== null) {
|
|
104
|
-
|
|
110
|
+
const ctx = {
|
|
111
|
+
tSanitized,
|
|
112
|
+
resetPlaces,
|
|
113
|
+
combined,
|
|
114
|
+
nodes,
|
|
115
|
+
edges,
|
|
116
|
+
counter: 0
|
|
117
|
+
};
|
|
118
|
+
emitOutput(t.outputSpec, tid, null, ctx);
|
|
105
119
|
}
|
|
106
120
|
for (const inh of t.inhibitors) {
|
|
107
121
|
const pid = "p_" + sanitize(inh.place.name);
|
|
@@ -128,9 +142,8 @@ function mapToGraph(net, config = DEFAULT_DOT_CONFIG) {
|
|
|
128
142
|
arcType: "read"
|
|
129
143
|
});
|
|
130
144
|
}
|
|
131
|
-
const outputPlaceNames = t.outputSpec !== null ? new Set([...t.outputPlaces()].map((p) => p.name)) : /* @__PURE__ */ new Set();
|
|
132
145
|
for (const rst of t.resets) {
|
|
133
|
-
if (!
|
|
146
|
+
if (!combined.has(rst.place.name)) {
|
|
134
147
|
const pid = "p_" + sanitize(rst.place.name);
|
|
135
148
|
const resetStyle = edgeStyle("reset");
|
|
136
149
|
edges.push({
|
|
@@ -220,47 +233,93 @@ function transitionLabel(t, config) {
|
|
|
220
233
|
}
|
|
221
234
|
return parts.join(" ");
|
|
222
235
|
}
|
|
223
|
-
function
|
|
224
|
-
const outStyle = edgeStyle("output");
|
|
236
|
+
function emitOutput(out, parentId, branchLabel, ctx) {
|
|
225
237
|
switch (out.type) {
|
|
226
238
|
case "place": {
|
|
227
239
|
const pid = "p_" + sanitize(out.place.name);
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
to: pid,
|
|
231
|
-
label: branchLabel ?? void 0,
|
|
232
|
-
color: outStyle.color,
|
|
233
|
-
style: outStyle.style,
|
|
234
|
-
arrowhead: outStyle.arrowhead,
|
|
235
|
-
arcType: "output"
|
|
236
|
-
}];
|
|
240
|
+
pushLeafEdge(parentId, pid, out.place.name, branchLabel, ctx, false);
|
|
241
|
+
return;
|
|
237
242
|
}
|
|
238
243
|
case "forward-input": {
|
|
239
244
|
const pid = "p_" + sanitize(out.to.name);
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
to: pid,
|
|
244
|
-
label,
|
|
245
|
-
color: outStyle.color,
|
|
246
|
-
style: "dashed",
|
|
247
|
-
arrowhead: outStyle.arrowhead,
|
|
248
|
-
arcType: "output"
|
|
249
|
-
}];
|
|
245
|
+
const fwdLabel = (branchLabel ? branchLabel + " " : "") + "\u27F5" + out.from.name;
|
|
246
|
+
pushLeafEdge(parentId, pid, out.to.name, fwdLabel, ctx, true);
|
|
247
|
+
return;
|
|
250
248
|
}
|
|
251
249
|
case "and":
|
|
252
|
-
return out.children.flatMap((c) => outputEdges(transitionId, c, branchLabel));
|
|
253
250
|
case "xor": {
|
|
254
|
-
|
|
251
|
+
if (out.children.length < 2) {
|
|
252
|
+
if (out.children.length === 1) {
|
|
253
|
+
emitOutput(out.children[0], parentId, branchLabel, ctx);
|
|
254
|
+
}
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const kind = out.type;
|
|
258
|
+
const idx = ctx.counter++;
|
|
259
|
+
const junctionId = `j_${ctx.tSanitized}__${kind}_${idx}`;
|
|
260
|
+
const category = kind === "xor" ? "xor-junction" : "and-junction";
|
|
261
|
+
const jStyle = nodeStyle(category);
|
|
262
|
+
ctx.nodes.push({
|
|
263
|
+
id: junctionId,
|
|
264
|
+
label: kind === "xor" ? "\u2715" : "\u271A",
|
|
265
|
+
shape: jStyle.shape,
|
|
266
|
+
fill: jStyle.fill,
|
|
267
|
+
stroke: jStyle.stroke,
|
|
268
|
+
penwidth: jStyle.penwidth,
|
|
269
|
+
semanticId: junctionId,
|
|
270
|
+
height: jStyle.height,
|
|
271
|
+
width: jStyle.width,
|
|
272
|
+
attrs: { fixedsize: "true", fontsize: "14" }
|
|
273
|
+
});
|
|
274
|
+
const outStyle = edgeStyle("output");
|
|
275
|
+
ctx.edges.push({
|
|
276
|
+
from: parentId,
|
|
277
|
+
to: junctionId,
|
|
278
|
+
label: branchLabel ?? void 0,
|
|
279
|
+
color: outStyle.color,
|
|
280
|
+
style: outStyle.style,
|
|
281
|
+
arrowhead: outStyle.arrowhead,
|
|
282
|
+
arcType: "output"
|
|
283
|
+
});
|
|
255
284
|
for (const child of out.children) {
|
|
256
|
-
const
|
|
257
|
-
|
|
285
|
+
const childLabel = kind === "xor" ? inferBranchLabel(child) : null;
|
|
286
|
+
emitOutput(child, junctionId, childLabel, ctx);
|
|
258
287
|
}
|
|
259
|
-
return
|
|
288
|
+
return;
|
|
260
289
|
}
|
|
261
|
-
case "timeout":
|
|
262
|
-
|
|
290
|
+
case "timeout": {
|
|
291
|
+
const timeoutLabel = `\u23F1${out.afterMs}ms`;
|
|
292
|
+
emitOutput(out.child, parentId, timeoutLabel, ctx);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function pushLeafEdge(fromId, toId, placeName, branchLabel, ctx, isForwardInput) {
|
|
298
|
+
if (ctx.resetPlaces.has(placeName)) {
|
|
299
|
+
ctx.combined.add(placeName);
|
|
300
|
+
const ro = edgeStyle("reset-output");
|
|
301
|
+
ctx.edges.push({
|
|
302
|
+
from: fromId,
|
|
303
|
+
to: toId,
|
|
304
|
+
label: "reset+out",
|
|
305
|
+
color: ro.color,
|
|
306
|
+
style: ro.style,
|
|
307
|
+
arrowhead: ro.arrowhead,
|
|
308
|
+
penwidth: ro.penwidth,
|
|
309
|
+
arcType: "reset-output"
|
|
310
|
+
});
|
|
311
|
+
return;
|
|
263
312
|
}
|
|
313
|
+
const out = edgeStyle("output");
|
|
314
|
+
ctx.edges.push({
|
|
315
|
+
from: fromId,
|
|
316
|
+
to: toId,
|
|
317
|
+
label: branchLabel ?? void 0,
|
|
318
|
+
color: out.color,
|
|
319
|
+
style: isForwardInput ? "dashed" : out.style,
|
|
320
|
+
arrowhead: out.arrowhead,
|
|
321
|
+
arcType: "output"
|
|
322
|
+
});
|
|
264
323
|
}
|
|
265
324
|
function inferBranchLabel(out) {
|
|
266
325
|
switch (out.type) {
|
|
@@ -412,4 +471,4 @@ export {
|
|
|
412
471
|
renderDot,
|
|
413
472
|
dotExport
|
|
414
473
|
};
|
|
415
|
-
//# sourceMappingURL=chunk-
|
|
474
|
+
//# sourceMappingURL=chunk-B2D5DMTO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/export/styles.ts","../src/export/petri-net-mapper.ts","../src/export/dot-renderer.ts","../src/export/dot-exporter.ts"],"sourcesContent":["// GENERATED from spec/petri-net-styles.json — do not edit manually.\n// Regenerate with: scripts/generate-styles.sh\n\n/**\n * Style loader for Petri net visualization.\n *\n * Reads the shared style definition from `spec/petri-net-styles.json` and\n * exposes typed accessors for node and edge visual properties.\n *\n * @module export/styles\n */\n\nimport type { NodeShape, EdgeLineStyle, ArrowHead } from './graph.js';\n\n// ======================== Style Types ========================\n\nexport interface NodeVisual {\n readonly shape: NodeShape;\n readonly fill: string;\n readonly stroke: string;\n readonly penwidth: number;\n readonly style?: string;\n readonly height?: number;\n readonly width?: number;\n}\n\nexport interface EdgeVisual {\n readonly color: string;\n readonly style: EdgeLineStyle;\n readonly arrowhead: ArrowHead;\n readonly penwidth?: number;\n}\n\nexport interface FontStyle {\n readonly family: string;\n readonly nodeSize: number;\n readonly edgeSize: number;\n}\n\nexport interface GraphStyle {\n readonly nodesep: number;\n readonly ranksep: number;\n readonly forcelabels: boolean;\n readonly overlap: boolean;\n readonly outputorder: string;\n}\n\n// ======================== Inline Style Data ========================\n\n// Inlined from spec/petri-net-styles.json to avoid runtime JSON import issues.\n// Keep in sync with the spec file.\n\nconst NODE_STYLES: Record<NodeCategory, NodeVisual> = {\n place: { shape: 'circle', fill: '#FFFFFF', stroke: '#333333', penwidth: 1.5, width: 0.35 },\n start: { shape: 'circle', fill: '#d4edda', stroke: '#28a745', penwidth: 2.0, width: 0.35 },\n end: { shape: 'doublecircle', fill: '#cce5ff', stroke: '#004085', penwidth: 2.0, width: 0.35 },\n environment: { shape: 'circle', fill: '#f8d7da', stroke: '#721c24', penwidth: 2.0, style: 'dashed', width: 0.35 },\n transition: { shape: 'box', fill: '#fff3cd', stroke: '#856404', penwidth: 1.0, height: 0.4, width: 0.8 },\n 'xor-junction': { shape: 'diamond', fill: '#FFFFFF', stroke: '#333333', penwidth: 1.0, height: 0.3, width: 0.3 },\n 'and-junction': { shape: 'diamond', fill: '#FFFFFF', stroke: '#333333', penwidth: 1.0, height: 0.3, width: 0.3 },\n};\n\nconst EDGE_STYLES: Record<EdgeCategory, EdgeVisual> = {\n input: { color: '#333333', style: 'solid', arrowhead: 'normal' },\n output: { color: '#333333', style: 'solid', arrowhead: 'normal' },\n inhibitor: { color: '#dc3545', style: 'solid', arrowhead: 'odot' },\n read: { color: '#6c757d', style: 'dashed', arrowhead: 'normal' },\n reset: { color: '#fd7e14', style: 'bold', arrowhead: 'normal', penwidth: 2.0 },\n 'reset-output': { color: '#fd7e14', style: 'bold', arrowhead: 'normal', penwidth: 2.0 },\n};\n\nexport const FONT: FontStyle = { family: 'Helvetica,Arial,sans-serif', nodeSize: 12, edgeSize: 10 };\n\nexport const GRAPH: GraphStyle = { nodesep: 0.5, ranksep: 0.75, forcelabels: true, overlap: false, outputorder: 'edgesfirst' };\n\n// ======================== Public API ========================\n\nexport type NodeCategory = 'place' | 'start' | 'end' | 'environment' | 'transition' | 'xor-junction' | 'and-junction';\nexport type EdgeCategory = 'input' | 'output' | 'inhibitor' | 'read' | 'reset' | 'reset-output';\n\n/** Returns the visual style for the given node category. */\nexport function nodeStyle(category: NodeCategory): NodeVisual {\n return NODE_STYLES[category];\n}\n\n/** Returns the visual style for the given edge/arc type. */\nexport function edgeStyle(arcType: EdgeCategory): EdgeVisual {\n return EDGE_STYLES[arcType];\n}\n","/**\n * Maps a PetriNet definition to a format-agnostic Graph.\n *\n * Petri net semantics live here. The mapper applies the visualization rules\n * specified in spec/09-export.md (EXP-012, EXP-013, EXP-014):\n *\n * - XOR / AND output groups with ≥2 children become synthetic junction nodes\n * (diamond for XOR, square for AND).\n * - Output + reset arcs to the same place collapse into a single edge styled\n * as the reset-output category and labelled \"reset+out\".\n * - Junction IDs use the form j_<transition>__<kind>_<idx>, where idx is a\n * depth-first pre-order counter starting at 0.\n *\n * @module export/petri-net-mapper\n */\n\nimport type { PetriNet } from '../core/petri-net.js';\nimport type { Transition } from '../core/transition.js';\nimport type { Out } from '../core/out.js';\nimport { earliest, latest, hasDeadline } from '../core/timing.js';\nimport type { Graph, GraphNode, GraphEdge, RankDir } from './graph.js';\nimport { nodeStyle, edgeStyle, FONT, GRAPH } from './styles.js';\nimport type { NodeCategory } from './styles.js';\n\n// ======================== Configuration ========================\n\nexport interface DotConfig {\n readonly direction: RankDir;\n readonly showTypes: boolean;\n readonly showIntervals: boolean;\n readonly showPriority: boolean;\n readonly environmentPlaces?: ReadonlySet<string>;\n}\n\nexport const DEFAULT_DOT_CONFIG: DotConfig = {\n direction: 'TB',\n showTypes: true,\n showIntervals: true,\n showPriority: true,\n};\n\n// ======================== Public API ========================\n\n/** Sanitizes a name for use as a graph node ID. */\nexport function sanitize(name: string): string {\n return name.replace(/[^a-zA-Z0-9_]/g, '_');\n}\n\n/** Maps a PetriNet to a format-agnostic Graph. */\nexport function mapToGraph(net: PetriNet, config: DotConfig = DEFAULT_DOT_CONFIG): Graph {\n const places = analyzePlaces(net);\n const envNames = config.environmentPlaces ?? new Set<string>();\n\n const nodes: GraphNode[] = [];\n const edges: GraphEdge[] = [];\n\n // Place nodes\n for (const [name, info] of places) {\n const category = placeCategory(info, envNames.has(name));\n const style = nodeStyle(category);\n nodes.push({\n id: 'p_' + sanitize(name),\n label: '',\n shape: style.shape,\n fill: style.fill,\n stroke: style.stroke,\n penwidth: style.penwidth,\n semanticId: name,\n style: style.style,\n width: style.width,\n attrs: { xlabel: name, fixedsize: 'true' },\n });\n }\n\n // Transition nodes\n for (const t of net.transitions) {\n const style = nodeStyle('transition');\n nodes.push({\n id: 't_' + sanitize(t.name),\n label: transitionLabel(t, config),\n shape: style.shape,\n fill: style.fill,\n stroke: style.stroke,\n penwidth: style.penwidth,\n semanticId: t.name,\n height: style.height,\n width: style.width,\n });\n }\n\n // Edges (and junction nodes)\n for (const t of net.transitions) {\n const tid = 't_' + sanitize(t.name);\n const tSanitized = sanitize(t.name);\n const resetPlaces = new Set(t.resets.map(r => r.place.name));\n const combined = new Set<string>();\n\n // Input arcs from inputSpecs\n for (const spec of t.inputSpecs) {\n const pid = 'p_' + sanitize(spec.place.name);\n const inputStyle = edgeStyle('input');\n let label: string | undefined;\n switch (spec.type) {\n case 'exactly':\n label = `×${spec.count}`;\n break;\n case 'all':\n label = '*';\n break;\n case 'at-least':\n label = `≥${spec.minimum}`;\n break;\n }\n edges.push({\n from: pid,\n to: tid,\n label,\n color: inputStyle.color,\n style: inputStyle.style,\n arrowhead: inputStyle.arrowhead,\n arcType: 'input',\n });\n }\n\n // Output arcs from outputSpec — emits junction nodes + edges, marks combined places.\n if (t.outputSpec !== null) {\n const ctx: EmitCtx = {\n tSanitized,\n resetPlaces,\n combined,\n nodes,\n edges,\n counter: 0,\n };\n emitOutput(t.outputSpec, tid, null, ctx);\n }\n\n // Inhibitor arcs\n for (const inh of t.inhibitors) {\n const pid = 'p_' + sanitize(inh.place.name);\n const inhStyle = edgeStyle('inhibitor');\n edges.push({\n from: pid,\n to: tid,\n color: inhStyle.color,\n style: inhStyle.style,\n arrowhead: inhStyle.arrowhead,\n arcType: 'inhibitor',\n });\n }\n\n // Read arcs\n for (const r of t.reads) {\n const pid = 'p_' + sanitize(r.place.name);\n const readStyle = edgeStyle('read');\n edges.push({\n from: pid,\n to: tid,\n label: 'read',\n color: readStyle.color,\n style: readStyle.style,\n arrowhead: readStyle.arrowhead,\n arcType: 'read',\n });\n }\n\n // Standalone reset arcs (only those not already combined with an output)\n for (const rst of t.resets) {\n if (!combined.has(rst.place.name)) {\n const pid = 'p_' + sanitize(rst.place.name);\n const resetStyle = edgeStyle('reset');\n edges.push({\n from: tid,\n to: pid,\n label: 'reset',\n color: resetStyle.color,\n style: resetStyle.style,\n arrowhead: resetStyle.arrowhead,\n penwidth: resetStyle.penwidth,\n arcType: 'reset',\n });\n }\n }\n }\n\n return {\n id: sanitize(net.name),\n rankdir: config.direction,\n nodes,\n edges,\n subgraphs: [],\n graphAttrs: {\n nodesep: String(GRAPH.nodesep),\n ranksep: String(GRAPH.ranksep),\n forcelabels: String(GRAPH.forcelabels),\n overlap: String(GRAPH.overlap),\n fontname: FONT.family,\n outputorder: GRAPH.outputorder,\n },\n nodeDefaults: {\n fontname: FONT.family,\n fontsize: String(FONT.nodeSize),\n },\n edgeDefaults: {\n fontname: FONT.family,\n fontsize: String(FONT.edgeSize),\n },\n };\n}\n\n// ======================== Place Analysis ========================\n\ninterface PlaceInfo {\n hasIncoming: boolean;\n hasOutgoing: boolean;\n}\n\nfunction analyzePlaces(net: PetriNet): Map<string, PlaceInfo> {\n const map = new Map<string, PlaceInfo>();\n\n function ensure(name: string): PlaceInfo {\n let info = map.get(name);\n if (!info) {\n info = { hasIncoming: false, hasOutgoing: false };\n map.set(name, info);\n }\n return info;\n }\n\n for (const t of net.transitions) {\n for (const spec of t.inputSpecs) {\n ensure(spec.place.name).hasOutgoing = true;\n }\n if (t.outputSpec !== null) {\n for (const p of t.outputPlaces()) {\n ensure(p.name).hasIncoming = true;\n }\n }\n for (const inh of t.inhibitors) {\n ensure(inh.place.name);\n }\n for (const r of t.reads) {\n ensure(r.place.name).hasOutgoing = true;\n }\n for (const rst of t.resets) {\n ensure(rst.place.name);\n }\n }\n\n return map;\n}\n\nfunction placeCategory(info: PlaceInfo, isEnvironment: boolean): NodeCategory {\n if (isEnvironment) return 'environment';\n if (!info.hasIncoming) return 'start';\n if (!info.hasOutgoing) return 'end';\n return 'place';\n}\n\n// ======================== Helpers ========================\n\nfunction transitionLabel(t: Transition, config: DotConfig): string {\n const parts = [t.name];\n\n if (config.showIntervals) {\n const e = earliest(t.timing);\n const l = latest(t.timing);\n const max = hasDeadline(t.timing) ? String(l) : '∞';\n parts.push(`[${e}, ${max}]ms`);\n }\n\n if (config.showPriority && t.priority !== 0) {\n parts.push(`prio=${t.priority}`);\n }\n\n return parts.join(' ');\n}\n\n/**\n * Mutable per-transition state threaded through the recursive Out-tree walk.\n *\n * `counter` starts at 0 and increments once per emitted junction (depth-first\n * pre-order). `combined` accumulates place names where a reset+output combination\n * short-circuited the standalone reset edge.\n */\ninterface EmitCtx {\n readonly tSanitized: string;\n readonly resetPlaces: ReadonlySet<string>;\n readonly combined: Set<string>;\n readonly nodes: GraphNode[];\n readonly edges: GraphEdge[];\n counter: number;\n}\n\n/**\n * Emits output edges for an Out tree, inserting junction nodes for XOR/AND\n * groups with ≥2 children. Combined reset+output edges replace plain output\n * edges when a leaf place is also in `ctx.resetPlaces`.\n *\n * @param out current Out subtree\n * @param parentId id of the parent node (transition or junction)\n * @param branchLabel label to apply to the edge entering this Out (timeout/XOR-branch label)\n * @param ctx per-transition junction context (counter, reset set, accumulators)\n */\nfunction emitOutput(out: Out, parentId: string, branchLabel: string | null, ctx: EmitCtx): void {\n switch (out.type) {\n case 'place': {\n const pid = 'p_' + sanitize(out.place.name);\n pushLeafEdge(parentId, pid, out.place.name, branchLabel, ctx, false);\n return;\n }\n\n case 'forward-input': {\n const pid = 'p_' + sanitize(out.to.name);\n const fwdLabel = (branchLabel ? branchLabel + ' ' : '') + '⟵' + out.from.name;\n // ForwardInput is dashed; reset+out combination overrides if applicable.\n pushLeafEdge(parentId, pid, out.to.name, fwdLabel, ctx, true);\n return;\n }\n\n case 'and':\n case 'xor': {\n // Single-child groups collapse: pass through.\n if (out.children.length < 2) {\n if (out.children.length === 1) {\n emitOutput(out.children[0]!, parentId, branchLabel, ctx);\n }\n return;\n }\n\n // Insert junction node — diamond gateway with heavy ✕ / ✚ glyph as discriminator.\n const kind = out.type;\n const idx = ctx.counter++;\n const junctionId = `j_${ctx.tSanitized}__${kind}_${idx}`;\n const category: NodeCategory = kind === 'xor' ? 'xor-junction' : 'and-junction';\n const jStyle = nodeStyle(category);\n ctx.nodes.push({\n id: junctionId,\n label: kind === 'xor' ? '✕' : '✚',\n shape: jStyle.shape,\n fill: jStyle.fill,\n stroke: jStyle.stroke,\n penwidth: jStyle.penwidth,\n semanticId: junctionId,\n height: jStyle.height,\n width: jStyle.width,\n attrs: { fixedsize: 'true', fontsize: '14' },\n });\n\n // Edge parent → junction (carries any inherited branch/timeout label).\n const outStyle = edgeStyle('output');\n ctx.edges.push({\n from: parentId,\n to: junctionId,\n label: branchLabel ?? undefined,\n color: outStyle.color,\n style: outStyle.style,\n arrowhead: outStyle.arrowhead,\n arcType: 'output',\n });\n\n // Recurse children: XOR junction propagates per-branch labels; AND junction does not.\n for (const child of out.children) {\n const childLabel = kind === 'xor' ? inferBranchLabel(child) : null;\n emitOutput(child, junctionId, childLabel, ctx);\n }\n return;\n }\n\n case 'timeout': {\n // Override any inherited branchLabel: the timeout label fully describes\n // this branch (the XOR pre-inference resolves to the same string).\n const timeoutLabel = `⏱${out.afterMs}ms`;\n emitOutput(out.child, parentId, timeoutLabel, ctx);\n return;\n }\n }\n}\n\nfunction pushLeafEdge(\n fromId: string,\n toId: string,\n placeName: string,\n branchLabel: string | null,\n ctx: EmitCtx,\n isForwardInput: boolean,\n): void {\n if (ctx.resetPlaces.has(placeName)) {\n ctx.combined.add(placeName);\n const ro = edgeStyle('reset-output');\n ctx.edges.push({\n from: fromId,\n to: toId,\n label: 'reset+out',\n color: ro.color,\n style: ro.style,\n arrowhead: ro.arrowhead,\n penwidth: ro.penwidth,\n arcType: 'reset-output',\n });\n return;\n }\n\n const out = edgeStyle('output');\n ctx.edges.push({\n from: fromId,\n to: toId,\n label: branchLabel ?? undefined,\n color: out.color,\n style: isForwardInput ? 'dashed' : out.style,\n arrowhead: out.arrowhead,\n arcType: 'output',\n });\n}\n\nfunction inferBranchLabel(out: Out): string | null {\n switch (out.type) {\n case 'place': return out.place.name;\n case 'timeout': return `⏱${out.afterMs}ms`;\n case 'forward-input': return out.to.name;\n case 'and':\n case 'xor':\n return null;\n }\n}\n","/**\n * Renders a Graph to DOT (Graphviz) string.\n *\n * Pure function with zero Petri net knowledge. Operates solely on the\n * format-agnostic Graph model.\n *\n * @module export/dot-renderer\n */\n\nimport type { Graph, GraphNode, GraphEdge, Subgraph } from './graph.js';\n\n// ======================== Public API ========================\n\n/** Renders a Graph to a DOT string suitable for Graphviz. */\nexport function renderDot(graph: Graph): string {\n const lines: string[] = [];\n\n lines.push(`digraph ${quoteId(graph.id)} {`);\n\n // Graph attributes\n lines.push(` rankdir=${graph.rankdir};`);\n for (const [key, value] of Object.entries(graph.graphAttrs)) {\n lines.push(` ${key}=${quoteAttr(value)};`);\n }\n\n // Node defaults\n if (Object.keys(graph.nodeDefaults).length > 0) {\n lines.push(` node [${formatAttrs(graph.nodeDefaults)}];`);\n }\n\n // Edge defaults\n if (Object.keys(graph.edgeDefaults).length > 0) {\n lines.push(` edge [${formatAttrs(graph.edgeDefaults)}];`);\n }\n\n lines.push('');\n\n // Subgraphs\n for (const sg of graph.subgraphs) {\n lines.push(...renderSubgraph(sg, ' '));\n lines.push('');\n }\n\n // Nodes\n for (const node of graph.nodes) {\n lines.push(` ${renderNode(node)}`);\n }\n\n if (graph.nodes.length > 0) {\n lines.push('');\n }\n\n // Edges\n for (const edge of graph.edges) {\n lines.push(` ${renderEdge(edge)}`);\n }\n\n lines.push('}');\n\n return lines.join('\\n');\n}\n\n// ======================== Internal Rendering ========================\n\nfunction renderNode(node: GraphNode): string {\n const attrs: Record<string, string> = {\n label: node.label,\n shape: node.shape,\n style: node.style ? `\"filled,${node.style}\"` : 'filled',\n fillcolor: node.fill,\n color: node.stroke,\n penwidth: String(node.penwidth),\n };\n\n if (node.height !== undefined) {\n attrs['height'] = String(node.height);\n }\n if (node.width !== undefined) {\n attrs['width'] = String(node.width);\n }\n\n // Merge extra attrs\n if (node.attrs) {\n for (const [key, value] of Object.entries(node.attrs)) {\n attrs[key] = value;\n }\n }\n\n return `${quoteId(node.id)} [${formatNodeAttrs(attrs)}];`;\n}\n\nfunction renderEdge(edge: GraphEdge): string {\n const attrs: Record<string, string> = {\n color: edge.color,\n style: edge.style,\n arrowhead: edge.arrowhead,\n };\n\n if (edge.label !== undefined) {\n attrs['label'] = edge.label;\n }\n if (edge.penwidth !== undefined) {\n attrs['penwidth'] = String(edge.penwidth);\n }\n\n // Merge extra attrs\n if (edge.attrs) {\n for (const [key, value] of Object.entries(edge.attrs)) {\n attrs[key] = value;\n }\n }\n\n return `${quoteId(edge.from)} -> ${quoteId(edge.to)} [${formatNodeAttrs(attrs)}];`;\n}\n\nfunction renderSubgraph(sg: Subgraph, indent: string): string[] {\n const lines: string[] = [];\n lines.push(`${indent}subgraph ${quoteId('cluster_' + sg.id)} {`);\n\n if (sg.label !== undefined) {\n lines.push(`${indent} label=${quoteAttr(sg.label)};`);\n }\n\n if (sg.attrs) {\n for (const [key, value] of Object.entries(sg.attrs)) {\n lines.push(`${indent} ${key}=${quoteAttr(value)};`);\n }\n }\n\n for (const node of sg.nodes) {\n lines.push(`${indent} ${renderNode(node)}`);\n }\n\n lines.push(`${indent}}`);\n return lines;\n}\n\n// ======================== DOT Quoting ========================\n\n/** Quotes a DOT identifier. Always quotes to be safe with special chars. */\nfunction quoteId(id: string): string {\n // DOT keywords that must be quoted\n if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(id) && !isDotKeyword(id)) {\n return id;\n }\n return `\"${escapeDot(id)}\"`;\n}\n\n/** Quotes a DOT attribute value. */\nfunction quoteAttr(value: string): string {\n // Numbers don't need quoting\n if (/^-?\\d+(\\.\\d+)?$/.test(value)) {\n return value;\n }\n return `\"${escapeDot(value)}\"`;\n}\n\n/** Escapes special characters for DOT strings. */\nfunction escapeDot(s: string): string {\n return s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\n/**\n * Formats node/edge attributes where certain values (like style with commas)\n * need special handling.\n */\nfunction formatNodeAttrs(attrs: Record<string, string>): string {\n return Object.entries(attrs)\n .map(([key, value]) => {\n // Style values that are already pre-quoted (contain the quote char)\n if (value.startsWith('\"') && value.endsWith('\"')) {\n return `${key}=${value}`;\n }\n return `${key}=${quoteAttr(value)}`;\n })\n .join(', ');\n}\n\n/** Formats simple key=value attributes. */\nfunction formatAttrs(attrs: Readonly<Record<string, string>>): string {\n return Object.entries(attrs)\n .map(([key, value]) => `${key}=${quoteAttr(value)}`)\n .join(', ');\n}\n\n/** DOT language keywords that must be quoted when used as identifiers. */\nfunction isDotKeyword(id: string): boolean {\n const lower = id.toLowerCase();\n return lower === 'graph' || lower === 'digraph' || lower === 'subgraph'\n || lower === 'node' || lower === 'edge' || lower === 'strict';\n}\n","/**\n * Convenience function for exporting a PetriNet to DOT format.\n *\n * @module export/dot-exporter\n */\n\nimport type { PetriNet } from '../core/petri-net.js';\nimport type { DotConfig } from './petri-net-mapper.js';\nimport { mapToGraph } from './petri-net-mapper.js';\nimport { renderDot } from './dot-renderer.js';\n\n/**\n * Exports a PetriNet to DOT (Graphviz) format.\n *\n * @param net the Petri net to export\n * @param config optional export configuration\n * @returns DOT string suitable for rendering with Graphviz\n */\nexport function dotExport(net: PetriNet, config?: DotConfig): string {\n return renderDot(mapToGraph(net, config));\n}\n"],"mappings":"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","names":[]}
|
package/dist/debug/index.js
CHANGED
package/dist/doclet/index.js
CHANGED
|
@@ -108,7 +108,7 @@ ${svgContent}
|
|
|
108
108
|
|
|
109
109
|
// src/doclet/petri-net-plugin.ts
|
|
110
110
|
async function getDotExport() {
|
|
111
|
-
const mod = await import("../dot-exporter-
|
|
111
|
+
const mod = await import("../dot-exporter-3CVCH6J4.js");
|
|
112
112
|
return mod.dotExport;
|
|
113
113
|
}
|
|
114
114
|
var TAG_NAME = "@petrinet";
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
// Petri Net Diagram Viewer
|
|
2
|
-
// Provides: zoom/pan and fullscreen for pre-rendered SVG diagrams
|
|
2
|
+
// Provides: zoom/pan and fullscreen for pre-rendered SVG diagrams.
|
|
3
|
+
//
|
|
4
|
+
// IMPORTANT: this file is mirrored at:
|
|
5
|
+
// - java/src/main/resources/javadoc/petrinet-diagrams.js
|
|
6
|
+
// - typescript/src/doclet/resources/petrinet-diagrams.js
|
|
7
|
+
// Keep both copies byte-identical until they're unified into one source.
|
|
8
|
+
// Same applies to the sibling petrinet-diagrams.css.
|
|
3
9
|
|
|
4
10
|
(function() {
|
|
5
11
|
if (document.readyState === 'loading') {
|
|
@@ -42,7 +48,9 @@ function enhanceDiagrams() {
|
|
|
42
48
|
if (!e.ctrlKey) return;
|
|
43
49
|
e.preventDefault();
|
|
44
50
|
var delta = e.deltaY > 0 ? 0.9 : 1.1;
|
|
45
|
-
|
|
51
|
+
// Bounds match libpetri/render-dom (debug-ui, dev-preview) so doc and
|
|
52
|
+
// live diagrams zoom to the same depth.
|
|
53
|
+
var newScale = Math.max(0.02, Math.min(1000, scale * delta));
|
|
46
54
|
var rect = container.getBoundingClientRect();
|
|
47
55
|
var mouseX = e.clientX - rect.left - rect.width / 2;
|
|
48
56
|
var mouseY = e.clientY - rect.top - rect.height / 2;
|
package/dist/export/index.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ interface GraphEdge {
|
|
|
38
38
|
readonly arrowhead: ArrowHead;
|
|
39
39
|
readonly penwidth?: number;
|
|
40
40
|
/** The arc type that produced this edge (for semantic queries). */
|
|
41
|
-
readonly arcType: 'input' | 'output' | 'inhibitor' | 'read' | 'reset';
|
|
41
|
+
readonly arcType: 'input' | 'output' | 'inhibitor' | 'read' | 'reset' | 'reset-output';
|
|
42
42
|
readonly attrs?: Readonly<Record<string, string>>;
|
|
43
43
|
}
|
|
44
44
|
interface Subgraph {
|
|
@@ -96,8 +96,8 @@ interface GraphStyle {
|
|
|
96
96
|
}
|
|
97
97
|
declare const FONT: FontStyle;
|
|
98
98
|
declare const GRAPH: GraphStyle;
|
|
99
|
-
type NodeCategory = 'place' | 'start' | 'end' | 'environment' | 'transition';
|
|
100
|
-
type EdgeCategory = 'input' | 'output' | 'inhibitor' | 'read' | 'reset';
|
|
99
|
+
type NodeCategory = 'place' | 'start' | 'end' | 'environment' | 'transition' | 'xor-junction' | 'and-junction';
|
|
100
|
+
type EdgeCategory = 'input' | 'output' | 'inhibitor' | 'read' | 'reset' | 'reset-output';
|
|
101
101
|
/** Returns the visual style for the given node category. */
|
|
102
102
|
declare function nodeStyle(category: NodeCategory): NodeVisual;
|
|
103
103
|
/** Returns the visual style for the given edge/arc type. */
|
|
@@ -106,9 +106,15 @@ declare function edgeStyle(arcType: EdgeCategory): EdgeVisual;
|
|
|
106
106
|
/**
|
|
107
107
|
* Maps a PetriNet definition to a format-agnostic Graph.
|
|
108
108
|
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
109
|
+
* Petri net semantics live here. The mapper applies the visualization rules
|
|
110
|
+
* specified in spec/09-export.md (EXP-012, EXP-013, EXP-014):
|
|
111
|
+
*
|
|
112
|
+
* - XOR / AND output groups with ≥2 children become synthetic junction nodes
|
|
113
|
+
* (diamond for XOR, square for AND).
|
|
114
|
+
* - Output + reset arcs to the same place collapse into a single edge styled
|
|
115
|
+
* as the reset-output category and labelled "reset+out".
|
|
116
|
+
* - Junction IDs use the form j_<transition>__<kind>_<idx>, where idx is a
|
|
117
|
+
* depth-first pre-order counter starting at 0.
|
|
112
118
|
*
|
|
113
119
|
* @module export/petri-net-mapper
|
|
114
120
|
*/
|
package/dist/export/index.js
CHANGED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import panzoom from 'panzoom';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* DOM rendering helper for libpetri DOT diagrams.
|
|
5
|
+
*
|
|
6
|
+
* Single source of truth for the viz.js + panzoom pipeline used by both
|
|
7
|
+
* debug-ui (live sessions) and the internal dev-preview Vite app
|
|
8
|
+
* (sample.dot iteration loop). Doc generators (Javadoc taglet, Rustdoc
|
|
9
|
+
* build.rs, TypeDoc plugin) take a different path — they shell out to the
|
|
10
|
+
* Graphviz `dot` binary at build time — and do not consume this module.
|
|
11
|
+
*
|
|
12
|
+
* Browser-only: requires `@viz-js/viz` and `panzoom` as peer dependencies.
|
|
13
|
+
*
|
|
14
|
+
* @module render-dom
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
type PanzoomInstance = ReturnType<typeof panzoom>;
|
|
18
|
+
interface RenderDotOptions {
|
|
19
|
+
/**
|
|
20
|
+
* Existing panzoom instance to dispose before initializing a new one.
|
|
21
|
+
* Pass the value previously returned from {@link renderDotToContainer}
|
|
22
|
+
* so callers don't need to track it themselves.
|
|
23
|
+
*/
|
|
24
|
+
readonly previousPanzoom?: PanzoomInstance | null;
|
|
25
|
+
/**
|
|
26
|
+
* Per-call panzoom overrides. Merged on top of {@link DEFAULT_PANZOOM_OPTS},
|
|
27
|
+
* so partial overrides keep the unspecified defaults (e.g. passing only
|
|
28
|
+
* `smoothScroll: true` retains `maxZoom: 1000` and `minZoom: 0.02`).
|
|
29
|
+
*/
|
|
30
|
+
readonly panzoom?: Parameters<typeof panzoom>[1];
|
|
31
|
+
}
|
|
32
|
+
interface RenderDotResult {
|
|
33
|
+
readonly svg: SVGSVGElement;
|
|
34
|
+
readonly panzoom: PanzoomInstance;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Default panzoom configuration shared by debug-ui and the dev preview.
|
|
38
|
+
*
|
|
39
|
+
* - `maxZoom: 1000` — effectively unlimited; lets users dive into ~150-node
|
|
40
|
+
* diagrams without hitting an artificial ceiling.
|
|
41
|
+
* - `minZoom: 0.02` — allows fitting very large nets in a small viewport.
|
|
42
|
+
* - `smoothScroll: false` — sharper interactive feel.
|
|
43
|
+
* - `zoomDoubleClickSpeed: 1` — single-click double-zoom toggle.
|
|
44
|
+
*/
|
|
45
|
+
declare const DEFAULT_PANZOOM_OPTS: {
|
|
46
|
+
readonly smoothScroll: false;
|
|
47
|
+
readonly zoomDoubleClickSpeed: 1;
|
|
48
|
+
readonly maxZoom: 1000;
|
|
49
|
+
readonly minZoom: 0.02;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Render a DOT string into a container element and wrap it with panzoom.
|
|
53
|
+
*
|
|
54
|
+
* Engine is pinned to `'dot'` so layout is deterministic across reloads;
|
|
55
|
+
* libpetri mappers emit byte-stable DOT (per spec EXP-014) and the `dot`
|
|
56
|
+
* engine produces identical SVG for identical input.
|
|
57
|
+
*
|
|
58
|
+
* The container's existing children are removed before the new SVG is
|
|
59
|
+
* appended.
|
|
60
|
+
*
|
|
61
|
+
* @returns the rendered SVG element and the panzoom instance, so callers
|
|
62
|
+
* can build secondary indexes (node caches, highlighting state) on the SVG
|
|
63
|
+
* and dispose the panzoom on the next render.
|
|
64
|
+
*/
|
|
65
|
+
declare function renderDotToContainer(dotSource: string, container: HTMLElement, opts?: RenderDotOptions): Promise<RenderDotResult>;
|
|
66
|
+
|
|
67
|
+
export { DEFAULT_PANZOOM_OPTS, type PanzoomInstance, type RenderDotOptions, type RenderDotResult, renderDotToContainer };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// src/render-dom/index.ts
|
|
2
|
+
import { instance as vizInstance } from "@viz-js/viz";
|
|
3
|
+
import panzoom from "panzoom";
|
|
4
|
+
var vizPromise = null;
|
|
5
|
+
function getViz() {
|
|
6
|
+
if (!vizPromise) vizPromise = vizInstance();
|
|
7
|
+
return vizPromise;
|
|
8
|
+
}
|
|
9
|
+
var DEFAULT_PANZOOM_OPTS = {
|
|
10
|
+
smoothScroll: false,
|
|
11
|
+
zoomDoubleClickSpeed: 1,
|
|
12
|
+
maxZoom: 1e3,
|
|
13
|
+
minZoom: 0.02
|
|
14
|
+
};
|
|
15
|
+
async function renderDotToContainer(dotSource, container, opts = {}) {
|
|
16
|
+
const viz = await getViz();
|
|
17
|
+
const svg = viz.renderSVGElement(dotSource, { engine: "dot" });
|
|
18
|
+
container.innerHTML = "";
|
|
19
|
+
container.appendChild(svg);
|
|
20
|
+
if (opts.previousPanzoom) {
|
|
21
|
+
opts.previousPanzoom.dispose();
|
|
22
|
+
}
|
|
23
|
+
const panzoomOpts = { ...DEFAULT_PANZOOM_OPTS, ...opts.panzoom };
|
|
24
|
+
const instance = panzoom(svg, panzoomOpts);
|
|
25
|
+
return { svg, panzoom: instance };
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
DEFAULT_PANZOOM_OPTS,
|
|
29
|
+
renderDotToContainer
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/render-dom/index.ts"],"sourcesContent":["/**\n * DOM rendering helper for libpetri DOT diagrams.\n *\n * Single source of truth for the viz.js + panzoom pipeline used by both\n * debug-ui (live sessions) and the internal dev-preview Vite app\n * (sample.dot iteration loop). Doc generators (Javadoc taglet, Rustdoc\n * build.rs, TypeDoc plugin) take a different path — they shell out to the\n * Graphviz `dot` binary at build time — and do not consume this module.\n *\n * Browser-only: requires `@viz-js/viz` and `panzoom` as peer dependencies.\n *\n * @module render-dom\n */\n\nimport { instance as vizInstance } from '@viz-js/viz';\nimport panzoom from 'panzoom';\n\ntype Viz = Awaited<ReturnType<typeof vizInstance>>;\nexport type PanzoomInstance = ReturnType<typeof panzoom>;\n\nlet vizPromise: Promise<Viz> | null = null;\n\n/** Memoizes the viz.js instance so loading happens only once per page. */\nfunction getViz(): Promise<Viz> {\n if (!vizPromise) vizPromise = vizInstance();\n return vizPromise;\n}\n\nexport interface RenderDotOptions {\n /**\n * Existing panzoom instance to dispose before initializing a new one.\n * Pass the value previously returned from {@link renderDotToContainer}\n * so callers don't need to track it themselves.\n */\n readonly previousPanzoom?: PanzoomInstance | null;\n /**\n * Per-call panzoom overrides. Merged on top of {@link DEFAULT_PANZOOM_OPTS},\n * so partial overrides keep the unspecified defaults (e.g. passing only\n * `smoothScroll: true` retains `maxZoom: 1000` and `minZoom: 0.02`).\n */\n readonly panzoom?: Parameters<typeof panzoom>[1];\n}\n\nexport interface RenderDotResult {\n readonly svg: SVGSVGElement;\n readonly panzoom: PanzoomInstance;\n}\n\n/**\n * Default panzoom configuration shared by debug-ui and the dev preview.\n *\n * - `maxZoom: 1000` — effectively unlimited; lets users dive into ~150-node\n * diagrams without hitting an artificial ceiling.\n * - `minZoom: 0.02` — allows fitting very large nets in a small viewport.\n * - `smoothScroll: false` — sharper interactive feel.\n * - `zoomDoubleClickSpeed: 1` — single-click double-zoom toggle.\n */\nexport const DEFAULT_PANZOOM_OPTS = {\n smoothScroll: false,\n zoomDoubleClickSpeed: 1,\n maxZoom: 1000,\n minZoom: 0.02,\n} as const satisfies Parameters<typeof panzoom>[1];\n\n/**\n * Render a DOT string into a container element and wrap it with panzoom.\n *\n * Engine is pinned to `'dot'` so layout is deterministic across reloads;\n * libpetri mappers emit byte-stable DOT (per spec EXP-014) and the `dot`\n * engine produces identical SVG for identical input.\n *\n * The container's existing children are removed before the new SVG is\n * appended.\n *\n * @returns the rendered SVG element and the panzoom instance, so callers\n * can build secondary indexes (node caches, highlighting state) on the SVG\n * and dispose the panzoom on the next render.\n */\nexport async function renderDotToContainer(\n dotSource: string,\n container: HTMLElement,\n opts: RenderDotOptions = {},\n): Promise<RenderDotResult> {\n const viz = await getViz();\n const svg = viz.renderSVGElement(dotSource, { engine: 'dot' });\n\n container.innerHTML = '';\n container.appendChild(svg);\n\n if (opts.previousPanzoom) {\n opts.previousPanzoom.dispose();\n }\n\n const panzoomOpts = { ...DEFAULT_PANZOOM_OPTS, ...opts.panzoom };\n const instance = panzoom(svg, panzoomOpts);\n\n return { svg, panzoom: instance };\n}\n"],"mappings":";AAcA,SAAS,YAAY,mBAAmB;AACxC,OAAO,aAAa;AAKpB,IAAI,aAAkC;AAGtC,SAAS,SAAuB;AAC9B,MAAI,CAAC,WAAY,cAAa,YAAY;AAC1C,SAAO;AACT;AA+BO,IAAM,uBAAuB;AAAA,EAClC,cAAc;AAAA,EACd,sBAAsB;AAAA,EACtB,SAAS;AAAA,EACT,SAAS;AACX;AAgBA,eAAsB,qBACpB,WACA,WACA,OAAyB,CAAC,GACA;AAC1B,QAAM,MAAM,MAAM,OAAO;AACzB,QAAM,MAAM,IAAI,iBAAiB,WAAW,EAAE,QAAQ,MAAM,CAAC;AAE7D,YAAU,YAAY;AACtB,YAAU,YAAY,GAAG;AAEzB,MAAI,KAAK,iBAAiB;AACxB,SAAK,gBAAgB,QAAQ;AAAA,EAC/B;AAEA,QAAM,cAAc,EAAE,GAAG,sBAAsB,GAAG,KAAK,QAAQ;AAC/D,QAAM,WAAW,QAAQ,KAAK,WAAW;AAEzC,SAAO,EAAE,KAAK,SAAS,SAAS;AAClC;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "libpetri",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.5",
|
|
4
4
|
"description": "Coloured Time Petri Net engine — TypeScript port",
|
|
5
5
|
"homepage": "https://libpetri.org",
|
|
6
6
|
"repository": {
|
|
@@ -42,6 +42,10 @@
|
|
|
42
42
|
"import": "./dist/doclet/index.js",
|
|
43
43
|
"types": "./dist/doclet/index.d.ts"
|
|
44
44
|
}
|
|
45
|
+
},
|
|
46
|
+
"./render-dom": {
|
|
47
|
+
"import": "./dist/render-dom/index.js",
|
|
48
|
+
"types": "./dist/render-dom/index.d.ts"
|
|
45
49
|
}
|
|
46
50
|
},
|
|
47
51
|
"scripts": {
|
|
@@ -50,18 +54,24 @@
|
|
|
50
54
|
"test": "vitest run",
|
|
51
55
|
"test:watch": "vitest",
|
|
52
56
|
"bench": "vitest bench",
|
|
53
|
-
"docs": "typedoc"
|
|
57
|
+
"docs": "typedoc",
|
|
58
|
+
"dev:preview": "vite -c dev-preview/vite.config.ts",
|
|
59
|
+
"regenerate-sample": "tsx scripts/regenerate-sample.ts"
|
|
54
60
|
},
|
|
55
61
|
"devDependencies": {
|
|
56
62
|
"@types/node": "^25.4.0",
|
|
57
63
|
"@viz-js/viz": "^3.25.0",
|
|
64
|
+
"panzoom": "^9.4.4",
|
|
58
65
|
"tsup": "^8.5.1",
|
|
66
|
+
"tsx": "^4.21.0",
|
|
59
67
|
"typedoc": "^0.28.17",
|
|
60
68
|
"typescript": "^5.9.3",
|
|
69
|
+
"vite": "^6.4.2",
|
|
61
70
|
"vitest": "^4.0.18"
|
|
62
71
|
},
|
|
63
72
|
"peerDependencies": {
|
|
64
73
|
"@viz-js/viz": ">=3.0.0",
|
|
74
|
+
"panzoom": ">=9.0.0",
|
|
65
75
|
"typedoc": ">=0.27.0"
|
|
66
76
|
},
|
|
67
77
|
"peerDependenciesMeta": {
|
|
@@ -70,6 +80,9 @@
|
|
|
70
80
|
},
|
|
71
81
|
"@viz-js/viz": {
|
|
72
82
|
"optional": true
|
|
83
|
+
},
|
|
84
|
+
"panzoom": {
|
|
85
|
+
"optional": true
|
|
73
86
|
}
|
|
74
87
|
},
|
|
75
88
|
"dependencies": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/export/styles.ts","../src/export/petri-net-mapper.ts","../src/export/dot-renderer.ts","../src/export/dot-exporter.ts"],"sourcesContent":["// GENERATED from spec/petri-net-styles.json — do not edit manually.\n// Regenerate with: scripts/generate-styles.sh\n\n/**\n * Style loader for Petri net visualization.\n *\n * Reads the shared style definition from `spec/petri-net-styles.json` and\n * exposes typed accessors for node and edge visual properties.\n *\n * @module export/styles\n */\n\nimport type { NodeShape, EdgeLineStyle, ArrowHead } from './graph.js';\n\n// ======================== Style Types ========================\n\nexport interface NodeVisual {\n readonly shape: NodeShape;\n readonly fill: string;\n readonly stroke: string;\n readonly penwidth: number;\n readonly style?: string;\n readonly height?: number;\n readonly width?: number;\n}\n\nexport interface EdgeVisual {\n readonly color: string;\n readonly style: EdgeLineStyle;\n readonly arrowhead: ArrowHead;\n readonly penwidth?: number;\n}\n\nexport interface FontStyle {\n readonly family: string;\n readonly nodeSize: number;\n readonly edgeSize: number;\n}\n\nexport interface GraphStyle {\n readonly nodesep: number;\n readonly ranksep: number;\n readonly forcelabels: boolean;\n readonly overlap: boolean;\n readonly outputorder: string;\n}\n\n// ======================== Inline Style Data ========================\n\n// Inlined from spec/petri-net-styles.json to avoid runtime JSON import issues.\n// Keep in sync with the spec file.\n\nconst NODE_STYLES: Record<NodeCategory, NodeVisual> = {\n place: { shape: 'circle', fill: '#FFFFFF', stroke: '#333333', penwidth: 1.5, width: 0.35 },\n start: { shape: 'circle', fill: '#d4edda', stroke: '#28a745', penwidth: 2.0, width: 0.35 },\n end: { shape: 'doublecircle', fill: '#cce5ff', stroke: '#004085', penwidth: 2.0, width: 0.35 },\n environment: { shape: 'circle', fill: '#f8d7da', stroke: '#721c24', penwidth: 2.0, style: 'dashed', width: 0.35 },\n transition: { shape: 'box', fill: '#fff3cd', stroke: '#856404', penwidth: 1.0, height: 0.4, width: 0.8 },\n};\n\nconst EDGE_STYLES: Record<EdgeCategory, EdgeVisual> = {\n input: { color: '#333333', style: 'solid', arrowhead: 'normal' },\n output: { color: '#333333', style: 'solid', arrowhead: 'normal' },\n inhibitor: { color: '#dc3545', style: 'solid', arrowhead: 'odot' },\n read: { color: '#6c757d', style: 'dashed', arrowhead: 'normal' },\n reset: { color: '#fd7e14', style: 'bold', arrowhead: 'normal', penwidth: 2.0 },\n};\n\nexport const FONT: FontStyle = { family: 'Helvetica,Arial,sans-serif', nodeSize: 12, edgeSize: 10 };\n\nexport const GRAPH: GraphStyle = { nodesep: 0.5, ranksep: 0.75, forcelabels: true, overlap: false, outputorder: 'edgesfirst' };\n\n// ======================== Public API ========================\n\nexport type NodeCategory = 'place' | 'start' | 'end' | 'environment' | 'transition';\nexport type EdgeCategory = 'input' | 'output' | 'inhibitor' | 'read' | 'reset';\n\n/** Returns the visual style for the given node category. */\nexport function nodeStyle(category: NodeCategory): NodeVisual {\n return NODE_STYLES[category];\n}\n\n/** Returns the visual style for the given edge/arc type. */\nexport function edgeStyle(arcType: EdgeCategory): EdgeVisual {\n return EDGE_STYLES[arcType];\n}\n","/**\n * Maps a PetriNet definition to a format-agnostic Graph.\n *\n * This is where all the Petri net semantics live. The mapper understands\n * places, transitions, arcs, timing, and priority. It produces a Graph\n * that can be rendered to DOT (or any other format) without Petri net knowledge.\n *\n * @module export/petri-net-mapper\n */\n\nimport type { PetriNet } from '../core/petri-net.js';\nimport type { Transition } from '../core/transition.js';\nimport type { Out } from '../core/out.js';\nimport { earliest, latest, hasDeadline } from '../core/timing.js';\nimport type { Graph, GraphNode, GraphEdge, RankDir } from './graph.js';\nimport { nodeStyle, edgeStyle, FONT, GRAPH } from './styles.js';\nimport type { NodeCategory } from './styles.js';\n\n// ======================== Configuration ========================\n\nexport interface DotConfig {\n readonly direction: RankDir;\n readonly showTypes: boolean;\n readonly showIntervals: boolean;\n readonly showPriority: boolean;\n readonly environmentPlaces?: ReadonlySet<string>;\n}\n\nexport const DEFAULT_DOT_CONFIG: DotConfig = {\n direction: 'TB',\n showTypes: true,\n showIntervals: true,\n showPriority: true,\n};\n\n// ======================== Public API ========================\n\n/** Sanitizes a name for use as a graph node ID. */\nexport function sanitize(name: string): string {\n return name.replace(/[^a-zA-Z0-9_]/g, '_');\n}\n\n/** Maps a PetriNet to a format-agnostic Graph. */\nexport function mapToGraph(net: PetriNet, config: DotConfig = DEFAULT_DOT_CONFIG): Graph {\n const places = analyzePlaces(net);\n const envNames = config.environmentPlaces ?? new Set<string>();\n\n const nodes: GraphNode[] = [];\n const edges: GraphEdge[] = [];\n\n // Place nodes\n for (const [name, info] of places) {\n const category = placeCategory(info, envNames.has(name));\n const style = nodeStyle(category);\n nodes.push({\n id: 'p_' + sanitize(name),\n label: '',\n shape: style.shape,\n fill: style.fill,\n stroke: style.stroke,\n penwidth: style.penwidth,\n semanticId: name,\n style: style.style,\n width: style.width,\n attrs: { xlabel: name, fixedsize: 'true' },\n });\n }\n\n // Transition nodes\n for (const t of net.transitions) {\n const style = nodeStyle('transition');\n nodes.push({\n id: 't_' + sanitize(t.name),\n label: transitionLabel(t, config),\n shape: style.shape,\n fill: style.fill,\n stroke: style.stroke,\n penwidth: style.penwidth,\n semanticId: t.name,\n height: style.height,\n width: style.width,\n });\n }\n\n // Edges\n for (const t of net.transitions) {\n const tid = 't_' + sanitize(t.name);\n\n // Input arcs from inputSpecs\n for (const spec of t.inputSpecs) {\n const pid = 'p_' + sanitize(spec.place.name);\n const inputStyle = edgeStyle('input');\n let label: string | undefined;\n switch (spec.type) {\n case 'exactly':\n label = `\\u00d7${spec.count}`;\n break;\n case 'all':\n label = '*';\n break;\n case 'at-least':\n label = `\\u2265${spec.minimum}`;\n break;\n }\n edges.push({\n from: pid,\n to: tid,\n label,\n color: inputStyle.color,\n style: inputStyle.style,\n arrowhead: inputStyle.arrowhead,\n arcType: 'input',\n });\n }\n\n // Output arcs from outputSpec\n if (t.outputSpec !== null) {\n edges.push(...outputEdges(tid, t.outputSpec, null));\n }\n\n // Inhibitor arcs\n for (const inh of t.inhibitors) {\n const pid = 'p_' + sanitize(inh.place.name);\n const inhStyle = edgeStyle('inhibitor');\n edges.push({\n from: pid,\n to: tid,\n color: inhStyle.color,\n style: inhStyle.style,\n arrowhead: inhStyle.arrowhead,\n arcType: 'inhibitor',\n });\n }\n\n // Read arcs\n for (const r of t.reads) {\n const pid = 'p_' + sanitize(r.place.name);\n const readStyle = edgeStyle('read');\n edges.push({\n from: pid,\n to: tid,\n label: 'read',\n color: readStyle.color,\n style: readStyle.style,\n arrowhead: readStyle.arrowhead,\n arcType: 'read',\n });\n }\n\n // Reset arcs (only those without matching output)\n const outputPlaceNames = t.outputSpec !== null\n ? new Set([...t.outputPlaces()].map(p => p.name))\n : new Set<string>();\n for (const rst of t.resets) {\n if (!outputPlaceNames.has(rst.place.name)) {\n const pid = 'p_' + sanitize(rst.place.name);\n const resetStyle = edgeStyle('reset');\n edges.push({\n from: tid,\n to: pid,\n label: 'reset',\n color: resetStyle.color,\n style: resetStyle.style,\n arrowhead: resetStyle.arrowhead,\n penwidth: resetStyle.penwidth,\n arcType: 'reset',\n });\n }\n }\n }\n\n return {\n id: sanitize(net.name),\n rankdir: config.direction,\n nodes,\n edges,\n subgraphs: [],\n graphAttrs: {\n nodesep: String(GRAPH.nodesep),\n ranksep: String(GRAPH.ranksep),\n forcelabels: String(GRAPH.forcelabels),\n overlap: String(GRAPH.overlap),\n fontname: FONT.family,\n outputorder: GRAPH.outputorder,\n },\n nodeDefaults: {\n fontname: FONT.family,\n fontsize: String(FONT.nodeSize),\n },\n edgeDefaults: {\n fontname: FONT.family,\n fontsize: String(FONT.edgeSize),\n },\n };\n}\n\n// ======================== Place Analysis ========================\n\ninterface PlaceInfo {\n hasIncoming: boolean;\n hasOutgoing: boolean;\n}\n\nfunction analyzePlaces(net: PetriNet): Map<string, PlaceInfo> {\n const map = new Map<string, PlaceInfo>();\n\n function ensure(name: string): PlaceInfo {\n let info = map.get(name);\n if (!info) {\n info = { hasIncoming: false, hasOutgoing: false };\n map.set(name, info);\n }\n return info;\n }\n\n for (const t of net.transitions) {\n for (const spec of t.inputSpecs) {\n ensure(spec.place.name).hasOutgoing = true;\n }\n if (t.outputSpec !== null) {\n for (const p of t.outputPlaces()) {\n ensure(p.name).hasIncoming = true;\n }\n }\n for (const inh of t.inhibitors) {\n ensure(inh.place.name);\n }\n for (const r of t.reads) {\n ensure(r.place.name).hasOutgoing = true;\n }\n for (const rst of t.resets) {\n ensure(rst.place.name);\n }\n }\n\n return map;\n}\n\nfunction placeCategory(info: PlaceInfo, isEnvironment: boolean): NodeCategory {\n if (isEnvironment) return 'environment';\n if (!info.hasIncoming) return 'start';\n if (!info.hasOutgoing) return 'end';\n return 'place';\n}\n\n// ======================== Helpers ========================\n\nfunction transitionLabel(t: Transition, config: DotConfig): string {\n const parts = [t.name];\n\n if (config.showIntervals) {\n const e = earliest(t.timing);\n const l = latest(t.timing);\n const max = hasDeadline(t.timing) ? String(l) : '\\u221e';\n parts.push(`[${e}, ${max}]ms`);\n }\n\n if (config.showPriority && t.priority !== 0) {\n parts.push(`prio=${t.priority}`);\n }\n\n return parts.join(' ');\n}\n\nfunction outputEdges(transitionId: string, out: Out, branchLabel: string | null): GraphEdge[] {\n const outStyle = edgeStyle('output');\n\n switch (out.type) {\n case 'place': {\n const pid = 'p_' + sanitize(out.place.name);\n return [{\n from: transitionId,\n to: pid,\n label: branchLabel ?? undefined,\n color: outStyle.color,\n style: outStyle.style,\n arrowhead: outStyle.arrowhead,\n arcType: 'output',\n }];\n }\n\n case 'forward-input': {\n const pid = 'p_' + sanitize(out.to.name);\n const label = (branchLabel ? branchLabel + ' ' : '') + '\\u27f5' + out.from.name;\n return [{\n from: transitionId,\n to: pid,\n label,\n color: outStyle.color,\n style: 'dashed' as const,\n arrowhead: outStyle.arrowhead,\n arcType: 'output',\n }];\n }\n\n case 'and':\n return out.children.flatMap(c => outputEdges(transitionId, c, branchLabel));\n\n case 'xor': {\n const edges: GraphEdge[] = [];\n for (const child of out.children) {\n const label = inferBranchLabel(child);\n edges.push(...outputEdges(transitionId, child, label));\n }\n return edges;\n }\n\n case 'timeout':\n return outputEdges(transitionId, out.child, `\\u23f1${out.afterMs}ms`);\n }\n}\n\nfunction inferBranchLabel(out: Out): string | null {\n switch (out.type) {\n case 'place': return out.place.name;\n case 'timeout': return `\\u23f1${out.afterMs}ms`;\n case 'forward-input': return out.to.name;\n case 'and':\n case 'xor':\n return null;\n }\n}\n","/**\n * Renders a Graph to DOT (Graphviz) string.\n *\n * Pure function with zero Petri net knowledge. Operates solely on the\n * format-agnostic Graph model.\n *\n * @module export/dot-renderer\n */\n\nimport type { Graph, GraphNode, GraphEdge, Subgraph } from './graph.js';\n\n// ======================== Public API ========================\n\n/** Renders a Graph to a DOT string suitable for Graphviz. */\nexport function renderDot(graph: Graph): string {\n const lines: string[] = [];\n\n lines.push(`digraph ${quoteId(graph.id)} {`);\n\n // Graph attributes\n lines.push(` rankdir=${graph.rankdir};`);\n for (const [key, value] of Object.entries(graph.graphAttrs)) {\n lines.push(` ${key}=${quoteAttr(value)};`);\n }\n\n // Node defaults\n if (Object.keys(graph.nodeDefaults).length > 0) {\n lines.push(` node [${formatAttrs(graph.nodeDefaults)}];`);\n }\n\n // Edge defaults\n if (Object.keys(graph.edgeDefaults).length > 0) {\n lines.push(` edge [${formatAttrs(graph.edgeDefaults)}];`);\n }\n\n lines.push('');\n\n // Subgraphs\n for (const sg of graph.subgraphs) {\n lines.push(...renderSubgraph(sg, ' '));\n lines.push('');\n }\n\n // Nodes\n for (const node of graph.nodes) {\n lines.push(` ${renderNode(node)}`);\n }\n\n if (graph.nodes.length > 0) {\n lines.push('');\n }\n\n // Edges\n for (const edge of graph.edges) {\n lines.push(` ${renderEdge(edge)}`);\n }\n\n lines.push('}');\n\n return lines.join('\\n');\n}\n\n// ======================== Internal Rendering ========================\n\nfunction renderNode(node: GraphNode): string {\n const attrs: Record<string, string> = {\n label: node.label,\n shape: node.shape,\n style: node.style ? `\"filled,${node.style}\"` : 'filled',\n fillcolor: node.fill,\n color: node.stroke,\n penwidth: String(node.penwidth),\n };\n\n if (node.height !== undefined) {\n attrs['height'] = String(node.height);\n }\n if (node.width !== undefined) {\n attrs['width'] = String(node.width);\n }\n\n // Merge extra attrs\n if (node.attrs) {\n for (const [key, value] of Object.entries(node.attrs)) {\n attrs[key] = value;\n }\n }\n\n return `${quoteId(node.id)} [${formatNodeAttrs(attrs)}];`;\n}\n\nfunction renderEdge(edge: GraphEdge): string {\n const attrs: Record<string, string> = {\n color: edge.color,\n style: edge.style,\n arrowhead: edge.arrowhead,\n };\n\n if (edge.label !== undefined) {\n attrs['label'] = edge.label;\n }\n if (edge.penwidth !== undefined) {\n attrs['penwidth'] = String(edge.penwidth);\n }\n\n // Merge extra attrs\n if (edge.attrs) {\n for (const [key, value] of Object.entries(edge.attrs)) {\n attrs[key] = value;\n }\n }\n\n return `${quoteId(edge.from)} -> ${quoteId(edge.to)} [${formatNodeAttrs(attrs)}];`;\n}\n\nfunction renderSubgraph(sg: Subgraph, indent: string): string[] {\n const lines: string[] = [];\n lines.push(`${indent}subgraph ${quoteId('cluster_' + sg.id)} {`);\n\n if (sg.label !== undefined) {\n lines.push(`${indent} label=${quoteAttr(sg.label)};`);\n }\n\n if (sg.attrs) {\n for (const [key, value] of Object.entries(sg.attrs)) {\n lines.push(`${indent} ${key}=${quoteAttr(value)};`);\n }\n }\n\n for (const node of sg.nodes) {\n lines.push(`${indent} ${renderNode(node)}`);\n }\n\n lines.push(`${indent}}`);\n return lines;\n}\n\n// ======================== DOT Quoting ========================\n\n/** Quotes a DOT identifier. Always quotes to be safe with special chars. */\nfunction quoteId(id: string): string {\n // DOT keywords that must be quoted\n if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(id) && !isDotKeyword(id)) {\n return id;\n }\n return `\"${escapeDot(id)}\"`;\n}\n\n/** Quotes a DOT attribute value. */\nfunction quoteAttr(value: string): string {\n // Numbers don't need quoting\n if (/^-?\\d+(\\.\\d+)?$/.test(value)) {\n return value;\n }\n return `\"${escapeDot(value)}\"`;\n}\n\n/** Escapes special characters for DOT strings. */\nfunction escapeDot(s: string): string {\n return s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\n/**\n * Formats node/edge attributes where certain values (like style with commas)\n * need special handling.\n */\nfunction formatNodeAttrs(attrs: Record<string, string>): string {\n return Object.entries(attrs)\n .map(([key, value]) => {\n // Style values that are already pre-quoted (contain the quote char)\n if (value.startsWith('\"') && value.endsWith('\"')) {\n return `${key}=${value}`;\n }\n return `${key}=${quoteAttr(value)}`;\n })\n .join(', ');\n}\n\n/** Formats simple key=value attributes. */\nfunction formatAttrs(attrs: Readonly<Record<string, string>>): string {\n return Object.entries(attrs)\n .map(([key, value]) => `${key}=${quoteAttr(value)}`)\n .join(', ');\n}\n\n/** DOT language keywords that must be quoted when used as identifiers. */\nfunction isDotKeyword(id: string): boolean {\n const lower = id.toLowerCase();\n return lower === 'graph' || lower === 'digraph' || lower === 'subgraph'\n || lower === 'node' || lower === 'edge' || lower === 'strict';\n}\n","/**\n * Convenience function for exporting a PetriNet to DOT format.\n *\n * @module export/dot-exporter\n */\n\nimport type { PetriNet } from '../core/petri-net.js';\nimport type { DotConfig } from './petri-net-mapper.js';\nimport { mapToGraph } from './petri-net-mapper.js';\nimport { renderDot } from './dot-renderer.js';\n\n/**\n * Exports a PetriNet to DOT (Graphviz) format.\n *\n * @param net the Petri net to export\n * @param config optional export configuration\n * @returns DOT string suitable for rendering with Graphviz\n */\nexport function dotExport(net: PetriNet, config?: DotConfig): string {\n return renderDot(mapToGraph(net, config));\n}\n"],"mappings":"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","names":[]}
|
|
File without changes
|