libpetri 1.8.2 → 1.8.4
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.d.ts +6 -2
- package/dist/debug/index.js +20 -13
- package/dist/debug/index.js.map +1 -1
- package/dist/doclet/index.js +1 -1
- 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.d.ts
CHANGED
|
@@ -724,8 +724,12 @@ declare class SessionArchiveWriter {
|
|
|
724
724
|
writeV3(session: DebugSession): Buffer;
|
|
725
725
|
/**
|
|
726
726
|
* Shared framing logic: length-prefixed header JSON, then length-prefixed
|
|
727
|
-
* event JSON, then gzip.
|
|
728
|
-
*
|
|
727
|
+
* event JSON, then gzip. All header versions share the identical event wire
|
|
728
|
+
* format, so the body loop is version-agnostic.
|
|
729
|
+
*
|
|
730
|
+
* Takes the events snapshot directly so the body iterates the same array the
|
|
731
|
+
* caller used to populate {@link SessionArchive.eventCount} — preserving the
|
|
732
|
+
* `header.eventCount === events.length` invariant.
|
|
729
733
|
*/
|
|
730
734
|
private writeFramed;
|
|
731
735
|
}
|
package/dist/debug/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
dotExport,
|
|
3
3
|
sanitize
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-B2D5DMTO.js";
|
|
5
5
|
import "../chunk-FN773SSE.js";
|
|
6
6
|
|
|
7
7
|
// src/debug/debug-command.ts
|
|
@@ -1583,16 +1583,17 @@ var SessionArchiveWriter = class {
|
|
|
1583
1583
|
* testing or when producing archives for consumers pinned to libpetri ≤ 1.6.1.
|
|
1584
1584
|
*/
|
|
1585
1585
|
writeV1(session) {
|
|
1586
|
+
const events = Object.freeze([...session.eventStore.events()]);
|
|
1586
1587
|
const header = {
|
|
1587
1588
|
version: 1,
|
|
1588
1589
|
sessionId: session.sessionId,
|
|
1589
1590
|
netName: session.netName,
|
|
1590
1591
|
dotDiagram: session.dotDiagram,
|
|
1591
1592
|
startTime: new Date(session.startTime).toISOString(),
|
|
1592
|
-
eventCount:
|
|
1593
|
+
eventCount: events.length,
|
|
1593
1594
|
structure: buildNetStructure(session)
|
|
1594
1595
|
};
|
|
1595
|
-
return this.writeFramed(header,
|
|
1596
|
+
return this.writeFramed(header, events);
|
|
1596
1597
|
}
|
|
1597
1598
|
/**
|
|
1598
1599
|
* Writes a session in the v2 format — richer header with `endTime`, `tags`,
|
|
@@ -1604,7 +1605,8 @@ var SessionArchiveWriter = class {
|
|
|
1604
1605
|
* passes walk the same sequence from the start.
|
|
1605
1606
|
*/
|
|
1606
1607
|
writeV2(session) {
|
|
1607
|
-
const
|
|
1608
|
+
const events = Object.freeze([...session.eventStore.events()]);
|
|
1609
|
+
const metadata = computeMetadata(events);
|
|
1608
1610
|
const header = {
|
|
1609
1611
|
version: 2,
|
|
1610
1612
|
sessionId: session.sessionId,
|
|
@@ -1612,7 +1614,7 @@ var SessionArchiveWriter = class {
|
|
|
1612
1614
|
dotDiagram: session.dotDiagram,
|
|
1613
1615
|
startTime: new Date(session.startTime).toISOString(),
|
|
1614
1616
|
endTime: session.endTime !== void 0 ? new Date(session.endTime).toISOString() : void 0,
|
|
1615
|
-
eventCount:
|
|
1617
|
+
eventCount: events.length,
|
|
1616
1618
|
// Snapshot of tags at archive-write time — record the state that was
|
|
1617
1619
|
// current when the session was archived, not whatever happens on the
|
|
1618
1620
|
// live session afterwards.
|
|
@@ -1620,7 +1622,7 @@ var SessionArchiveWriter = class {
|
|
|
1620
1622
|
metadata,
|
|
1621
1623
|
structure: buildNetStructure(session)
|
|
1622
1624
|
};
|
|
1623
|
-
return this.writeFramed(header,
|
|
1625
|
+
return this.writeFramed(header, events);
|
|
1624
1626
|
}
|
|
1625
1627
|
/**
|
|
1626
1628
|
* Writes a session in the v3 format — same header shape as v2, with version=3
|
|
@@ -1628,7 +1630,8 @@ var SessionArchiveWriter = class {
|
|
|
1628
1630
|
* legacy `value` string (see {@link tokenInfo}).
|
|
1629
1631
|
*/
|
|
1630
1632
|
writeV3(session) {
|
|
1631
|
-
const
|
|
1633
|
+
const events = Object.freeze([...session.eventStore.events()]);
|
|
1634
|
+
const metadata = computeMetadata(events);
|
|
1632
1635
|
const header = {
|
|
1633
1636
|
version: 3,
|
|
1634
1637
|
sessionId: session.sessionId,
|
|
@@ -1636,25 +1639,29 @@ var SessionArchiveWriter = class {
|
|
|
1636
1639
|
dotDiagram: session.dotDiagram,
|
|
1637
1640
|
startTime: new Date(session.startTime).toISOString(),
|
|
1638
1641
|
endTime: session.endTime !== void 0 ? new Date(session.endTime).toISOString() : void 0,
|
|
1639
|
-
eventCount:
|
|
1642
|
+
eventCount: events.length,
|
|
1640
1643
|
tags: { ...session.tags },
|
|
1641
1644
|
metadata,
|
|
1642
1645
|
structure: buildNetStructure(session)
|
|
1643
1646
|
};
|
|
1644
|
-
return this.writeFramed(header,
|
|
1647
|
+
return this.writeFramed(header, events);
|
|
1645
1648
|
}
|
|
1646
1649
|
/**
|
|
1647
1650
|
* Shared framing logic: length-prefixed header JSON, then length-prefixed
|
|
1648
|
-
* event JSON, then gzip.
|
|
1649
|
-
*
|
|
1651
|
+
* event JSON, then gzip. All header versions share the identical event wire
|
|
1652
|
+
* format, so the body loop is version-agnostic.
|
|
1653
|
+
*
|
|
1654
|
+
* Takes the events snapshot directly so the body iterates the same array the
|
|
1655
|
+
* caller used to populate {@link SessionArchive.eventCount} — preserving the
|
|
1656
|
+
* `header.eventCount === events.length` invariant.
|
|
1650
1657
|
*/
|
|
1651
|
-
writeFramed(header,
|
|
1658
|
+
writeFramed(header, events) {
|
|
1652
1659
|
const parts = [];
|
|
1653
1660
|
const metaBytes = Buffer.from(JSON.stringify(header), "utf-8");
|
|
1654
1661
|
const metaLen = Buffer.alloc(4);
|
|
1655
1662
|
metaLen.writeUInt32BE(metaBytes.length);
|
|
1656
1663
|
parts.push(metaLen, metaBytes);
|
|
1657
|
-
for (const event of
|
|
1664
|
+
for (const event of events) {
|
|
1658
1665
|
const eventInfo = toEventInfo(event);
|
|
1659
1666
|
const eventBytes = Buffer.from(JSON.stringify(eventInfo), "utf-8");
|
|
1660
1667
|
const eventLen = Buffer.alloc(4);
|