libpetri 2.4.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-B2CV5M3L.js → chunk-2GAZLDH3.js} +26 -8
- package/dist/chunk-2GAZLDH3.js.map +1 -0
- package/dist/debug/index.d.ts +2 -2
- package/dist/debug/index.js +1 -1
- package/dist/doclet/index.d.ts +1 -1
- package/dist/doclet/index.js +2 -2
- package/dist/doclet/resources/petrinet-diagrams.js +21 -21
- package/dist/dot-exporter-7YDFCBKC.js +8 -0
- package/dist/{event-store-DqXOj4j-.d.ts → event-store-z30VMn0T.d.ts} +1 -1
- package/dist/export/index.d.ts +22 -2
- package/dist/export/index.js +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +91 -7
- package/dist/index.js.map +1 -1
- package/dist/{petri-net-RpZxG0Io.d.ts → petri-net-DXdpd0r5.d.ts} +32 -1
- package/dist/verification/index.d.ts +2 -2
- package/dist/viewer/index.d.ts +10 -4
- package/dist/viewer/index.js +76 -4
- package/dist/viewer/index.js.map +1 -1
- package/dist/viewer/viewer.iife.js +21 -21
- package/package.json +1 -1
- package/dist/chunk-B2CV5M3L.js.map +0 -1
- package/dist/dot-exporter-WJMCJEFK.js +0 -8
- /package/dist/{dot-exporter-WJMCJEFK.js.map → dot-exporter-7YDFCBKC.js.map} +0 -0
|
@@ -1297,8 +1297,20 @@ declare class PetriNet {
|
|
|
1297
1297
|
readonly name: string;
|
|
1298
1298
|
readonly places: ReadonlySet<Place<any>>;
|
|
1299
1299
|
readonly transitions: ReadonlySet<Transition>;
|
|
1300
|
+
/**
|
|
1301
|
+
* Subnet-membership metadata per **MOD-026**: maps each node name (place or
|
|
1302
|
+
* transition) contributed by exactly one directly-composed subnet to that
|
|
1303
|
+
* subnet's name. Shared places contributed by two or more subnets, and every
|
|
1304
|
+
* node of a net not built via {@link PetriNetBuilder.compose}`(SubnetDef)`,
|
|
1305
|
+
* are absent. Never null — an empty map when there is no metadata. The DOT
|
|
1306
|
+
* exporter renders `subgraph cluster_*` blocks from this map.
|
|
1307
|
+
*
|
|
1308
|
+
* V8 `Map` is insertion-ordered, preserving compose order so cluster
|
|
1309
|
+
* subgraphs render deterministically (cross-language byte-parity).
|
|
1310
|
+
*/
|
|
1311
|
+
readonly subnetMembership: ReadonlyMap<string, string>;
|
|
1300
1312
|
/** @internal Use {@link PetriNet.builder} to create instances. */
|
|
1301
|
-
constructor(key: symbol, name: string, places: ReadonlySet<Place<any>>, transitions: ReadonlySet<Transition>);
|
|
1313
|
+
constructor(key: symbol, name: string, places: ReadonlySet<Place<any>>, transitions: ReadonlySet<Transition>, subnetMembership?: ReadonlyMap<string, string>);
|
|
1302
1314
|
/**
|
|
1303
1315
|
* Creates a new PetriNet with actions bound to transitions by name.
|
|
1304
1316
|
* Unbound transitions keep passthrough action.
|
|
@@ -1315,6 +1327,7 @@ declare class PetriNetBuilder {
|
|
|
1315
1327
|
private readonly _places;
|
|
1316
1328
|
private readonly _transitions;
|
|
1317
1329
|
private readonly _fusionSets;
|
|
1330
|
+
private readonly _subnetContributions;
|
|
1318
1331
|
constructor(name: string);
|
|
1319
1332
|
/** Add an explicit place. */
|
|
1320
1333
|
place(place: Place<any>): this;
|
|
@@ -1397,6 +1410,8 @@ declare class PetriNetBuilder {
|
|
|
1397
1410
|
* collides with a transition already in this builder.
|
|
1398
1411
|
*/
|
|
1399
1412
|
private composeDirect;
|
|
1413
|
+
/** @internal MOD-026: records a node as contributed by the named subnet. */
|
|
1414
|
+
private recordContribution;
|
|
1400
1415
|
/**
|
|
1401
1416
|
* Identity-default auto-compose per **MOD-024**.
|
|
1402
1417
|
*
|
|
@@ -1515,6 +1530,22 @@ declare class PetriNetBuilder {
|
|
|
1515
1530
|
* @throws when two fusion sets share a place
|
|
1516
1531
|
*/
|
|
1517
1532
|
build(): PetriNet;
|
|
1533
|
+
/**
|
|
1534
|
+
* @internal Resolves the per-compose contributions recorded by
|
|
1535
|
+
* `composeDirect` into the final node-name → subnet-name membership map per
|
|
1536
|
+
* **MOD-026**. A node contributed by exactly one subnet maps to that subnet;
|
|
1537
|
+
* a place contributed by two or more subnets is a shared rendezvous place
|
|
1538
|
+
* and is omitted (it renders top-level, outside any cluster). Returns an
|
|
1539
|
+
* empty map — the common case — when no subnet was composed directly.
|
|
1540
|
+
*/
|
|
1541
|
+
private resolveSubnetMembership;
|
|
1542
|
+
/**
|
|
1543
|
+
* @internal Drops membership entries for places removed by fusion: a
|
|
1544
|
+
* non-canonical fused member no longer exists in the net, so its
|
|
1545
|
+
* `node-name → subnet` entry would dangle. The surviving canonical place
|
|
1546
|
+
* keeps its own entry. Per **MOD-026**.
|
|
1547
|
+
*/
|
|
1548
|
+
private static filterFusedMembership;
|
|
1518
1549
|
/**
|
|
1519
1550
|
* @internal Fusion-resolution pass per **MOD-061**. Split out from
|
|
1520
1551
|
* {@link build} so the no-fusion fast path stays trivial.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { b as Transition, a as Place, P as PetriNet, E as EnvironmentPlace, aG as MarkingState, aH as PInvariant, aI as MarkingStateBuilder, aJ as SmtProperty, aK as SmtVerificationResult } from '../petri-net-
|
|
2
|
-
export { aL as DeadlockFree, aM as MutualExclusion, aN as PlaceBound, aO as Proven, aP as SmtStatistics, aQ as TokenSupplier, aR as Unknown, aS as Unreachable, aT as Verdict, X as VerificationHarness, Y as VerificationResult, aU as Violated, aV as deadlockFree, aW as isProven, aX as isViolated, aY as mutualExclusion, aZ as pInvariant, a_ as pInvariantToString, a$ as placeBound, b0 as propertyDescription, b1 as unreachable } from '../petri-net-
|
|
1
|
+
import { b as Transition, a as Place, P as PetriNet, E as EnvironmentPlace, aG as MarkingState, aH as PInvariant, aI as MarkingStateBuilder, aJ as SmtProperty, aK as SmtVerificationResult } from '../petri-net-DXdpd0r5.js';
|
|
2
|
+
export { aL as DeadlockFree, aM as MutualExclusion, aN as PlaceBound, aO as Proven, aP as SmtStatistics, aQ as TokenSupplier, aR as Unknown, aS as Unreachable, aT as Verdict, X as VerificationHarness, Y as VerificationResult, aU as Violated, aV as deadlockFree, aW as isProven, aX as isViolated, aY as mutualExclusion, aZ as pInvariant, a_ as pInvariantToString, a$ as placeBound, b0 as propertyDescription, b1 as unreachable } from '../petri-net-DXdpd0r5.js';
|
|
3
3
|
import { Expr, init, Bool, FuncDecl } from 'z3-solver';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/viewer/index.d.ts
CHANGED
|
@@ -63,10 +63,16 @@ interface ClusterDescriptor {
|
|
|
63
63
|
*
|
|
64
64
|
* Graphviz emits cluster nodes as SIBLINGS of the cluster `<g>`, not as
|
|
65
65
|
* children — `<g class="cluster">` only carries title, polygon border, and
|
|
66
|
-
* the cluster's text label.
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
66
|
+
* the cluster's text label. Cluster membership is recovered two ways:
|
|
67
|
+
*
|
|
68
|
+
* - **Name-based** — instance composition prefixes interior names
|
|
69
|
+
* (`p_<prefix>_…`, `t_<prefix>_…`, `j_<prefix>_…`) or uses slash-separated
|
|
70
|
+
* names (`<prefix>/…`).
|
|
71
|
+
* - **Geometric** — direct composition (spec MOD-026) keeps each node's
|
|
72
|
+
* original, un-prefixed name, so the only structural signal left in the
|
|
73
|
+
* rendered SVG is the node's drawn position: a node whose centre falls
|
|
74
|
+
* inside the cluster's border box belongs to it. This is what Graphviz
|
|
75
|
+
* itself draws the box around.
|
|
70
76
|
*
|
|
71
77
|
* Does not split nested-cluster prefixes (`outer_inner` could mean either
|
|
72
78
|
* `outer/inner` or a flat `outer_inner` prefix — Graphviz sanitisation is
|
package/dist/viewer/index.js
CHANGED
|
@@ -30,13 +30,14 @@ function discoverClusters(svg) {
|
|
|
30
30
|
if (!raw.startsWith("cluster_")) continue;
|
|
31
31
|
const prefix = raw.slice("cluster_".length);
|
|
32
32
|
if (out.has(prefix)) continue;
|
|
33
|
+
const rect = clusterRect(g);
|
|
33
34
|
let nodeCount = 0;
|
|
34
35
|
for (const node of allNodes) {
|
|
35
36
|
if (node.getAttribute("data-instance")) continue;
|
|
36
37
|
const nodeTitle = directChild(node, "title");
|
|
37
38
|
if (!nodeTitle) continue;
|
|
38
39
|
const id = (nodeTitle.textContent ?? "").trim();
|
|
39
|
-
if (
|
|
40
|
+
if (nodeBelongsToCluster(id, node, prefix, rect)) {
|
|
40
41
|
node.setAttribute("data-instance", prefix);
|
|
41
42
|
nodeCount++;
|
|
42
43
|
}
|
|
@@ -50,9 +51,77 @@ function discoverClusters(svg) {
|
|
|
50
51
|
}
|
|
51
52
|
return out;
|
|
52
53
|
}
|
|
54
|
+
function nodeBelongsToCluster(nodeId, node, prefix, rect) {
|
|
55
|
+
if (nodeBelongsToPrefix(nodeId, prefix)) return true;
|
|
56
|
+
if (rect) {
|
|
57
|
+
const c = nodeCenter(node);
|
|
58
|
+
if (c && c.x >= rect.minX && c.x <= rect.maxX && c.y >= rect.minY && c.y <= rect.maxY) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
53
64
|
function nodeBelongsToPrefix(nodeId, prefix) {
|
|
54
65
|
return nodeId === prefix || nodeId.startsWith(`${prefix}/`) || nodeId.startsWith(`p_${prefix}_`) || nodeId.startsWith(`t_${prefix}_`) || nodeId.startsWith(`j_${prefix}_`);
|
|
55
66
|
}
|
|
67
|
+
function clusterRect(group) {
|
|
68
|
+
const border = directChild(group, "polygon") ?? directChild(group, "path");
|
|
69
|
+
if (!border) return null;
|
|
70
|
+
const raw = border.getAttribute("points") ?? border.getAttribute("d") ?? "";
|
|
71
|
+
return boundsOf(raw);
|
|
72
|
+
}
|
|
73
|
+
function nodeCenter(node) {
|
|
74
|
+
const ellipse = directChild(node, "ellipse");
|
|
75
|
+
if (ellipse) {
|
|
76
|
+
const x = parseFloat(ellipse.getAttribute("cx") ?? "");
|
|
77
|
+
const y = parseFloat(ellipse.getAttribute("cy") ?? "");
|
|
78
|
+
if (!Number.isNaN(x) && !Number.isNaN(y)) return { x, y };
|
|
79
|
+
}
|
|
80
|
+
const shape = directChild(node, "polygon") ?? directChild(node, "path");
|
|
81
|
+
if (shape) {
|
|
82
|
+
const bounds = boundsOf(
|
|
83
|
+
shape.getAttribute("points") ?? shape.getAttribute("d") ?? ""
|
|
84
|
+
);
|
|
85
|
+
if (bounds) {
|
|
86
|
+
return {
|
|
87
|
+
x: (bounds.minX + bounds.maxX) / 2,
|
|
88
|
+
y: (bounds.minY + bounds.maxY) / 2
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
function boundsOf(coords) {
|
|
95
|
+
const nums = coords.match(/-?\d+(?:\.\d+)?/g);
|
|
96
|
+
if (!nums || nums.length < 2) return null;
|
|
97
|
+
let minX = Infinity;
|
|
98
|
+
let minY = Infinity;
|
|
99
|
+
let maxX = -Infinity;
|
|
100
|
+
let maxY = -Infinity;
|
|
101
|
+
for (let i = 0; i + 1 < nums.length; i += 2) {
|
|
102
|
+
const x = parseFloat(nums[i]);
|
|
103
|
+
const y = parseFloat(nums[i + 1]);
|
|
104
|
+
if (x < minX) minX = x;
|
|
105
|
+
if (x > maxX) maxX = x;
|
|
106
|
+
if (y < minY) minY = y;
|
|
107
|
+
if (y > maxY) maxY = y;
|
|
108
|
+
}
|
|
109
|
+
if (minX === Infinity) return null;
|
|
110
|
+
return { minX, minY, maxX, maxY };
|
|
111
|
+
}
|
|
112
|
+
function nodeClusterMap(svg) {
|
|
113
|
+
const map = /* @__PURE__ */ new Map();
|
|
114
|
+
svg.querySelectorAll("g.node[data-instance]").forEach((node) => {
|
|
115
|
+
const di = node.getAttribute("data-instance");
|
|
116
|
+
const title = directChild(node, "title");
|
|
117
|
+
const id = (title?.textContent ?? "").trim();
|
|
118
|
+
if (di && id) map.set(id, di);
|
|
119
|
+
});
|
|
120
|
+
return map;
|
|
121
|
+
}
|
|
122
|
+
function endpointInCluster(id, prefix, clusterMap) {
|
|
123
|
+
return nodeBelongsToPrefix(id, prefix) || clusterMap.get(id) === prefix;
|
|
124
|
+
}
|
|
56
125
|
function colorForPrefix(prefix) {
|
|
57
126
|
let h = 2166136261;
|
|
58
127
|
for (let i = 0; i < prefix.length; i++) {
|
|
@@ -84,6 +153,7 @@ function setClusterCollapsed(cluster, collapsed) {
|
|
|
84
153
|
node.classList.add("petri-collapsed-inside");
|
|
85
154
|
hidden.push(node);
|
|
86
155
|
});
|
|
156
|
+
const clusterMap = nodeClusterMap(root);
|
|
87
157
|
root.querySelectorAll("g.edge").forEach((edge) => {
|
|
88
158
|
const titleEl = directChild(edge, "title");
|
|
89
159
|
if (!titleEl) return;
|
|
@@ -92,7 +162,7 @@ function setClusterCollapsed(cluster, collapsed) {
|
|
|
92
162
|
if (arrow < 0) return;
|
|
93
163
|
const from = t.slice(0, arrow);
|
|
94
164
|
const to = t.slice(arrow + 2);
|
|
95
|
-
if (
|
|
165
|
+
if (endpointInCluster(from, cluster.prefix, clusterMap) && endpointInCluster(to, cluster.prefix, clusterMap)) {
|
|
96
166
|
edge.classList.add("petri-collapsed-inside");
|
|
97
167
|
hidden.push(edge);
|
|
98
168
|
}
|
|
@@ -135,6 +205,7 @@ function applyFilter(svg, prefix) {
|
|
|
135
205
|
}
|
|
136
206
|
svg.setAttribute("data-active-filter", prefix);
|
|
137
207
|
svg.classList.add("has-active-filter");
|
|
208
|
+
const clusterMap = nodeClusterMap(svg);
|
|
138
209
|
svg.querySelectorAll("g.node").forEach((node) => {
|
|
139
210
|
const di = node.getAttribute("data-instance");
|
|
140
211
|
const match = !!di && (di === prefix || di.indexOf(prefix + "_") === 0 || di.indexOf(prefix + "/") === 0);
|
|
@@ -152,7 +223,7 @@ function applyFilter(svg, prefix) {
|
|
|
152
223
|
}
|
|
153
224
|
const from = titleText.slice(0, arrow);
|
|
154
225
|
const to = titleText.slice(arrow + 2);
|
|
155
|
-
const matches = (name) => name.indexOf(prefix + "/") >= 0 || name.indexOf(prefix + "_") >= 0 || name === prefix;
|
|
226
|
+
const matches = (name) => name.indexOf(prefix + "/") >= 0 || name.indexOf(prefix + "_") >= 0 || name === prefix || clusterMap.get(name) === prefix;
|
|
156
227
|
if (matches(from) || matches(to)) edge.classList.remove("petri-dimmed");
|
|
157
228
|
else edge.classList.add("petri-dimmed");
|
|
158
229
|
});
|
|
@@ -160,9 +231,10 @@ function applyFilter(svg, prefix) {
|
|
|
160
231
|
function isInsideCollapsedCluster(svg, clusters, collapsedPrefixes, graphId) {
|
|
161
232
|
if (collapsedPrefixes.size === 0) return false;
|
|
162
233
|
if (!svg) return false;
|
|
234
|
+
const clusterMap = nodeClusterMap(svg);
|
|
163
235
|
for (const prefix of collapsedPrefixes) {
|
|
164
236
|
if (!clusters.has(prefix)) continue;
|
|
165
|
-
if (
|
|
237
|
+
if (endpointInCluster(graphId, prefix, clusterMap)) return true;
|
|
166
238
|
}
|
|
167
239
|
return false;
|
|
168
240
|
}
|