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.
@@ -0,0 +1,8 @@
1
+ import {
2
+ dotExport
3
+ } from "./chunk-2GAZLDH3.js";
4
+ import "./chunk-FN773SSE.js";
5
+ export {
6
+ dotExport
7
+ };
8
+ //# sourceMappingURL=dot-exporter-7YDFCBKC.js.map
@@ -1,4 +1,4 @@
1
- import { T as Token } from './petri-net-RpZxG0Io.js';
1
+ import { T as Token } from './petri-net-DXdpd0r5.js';
2
2
 
3
3
  /**
4
4
  * Events emitted during Petri Net execution.
@@ -1,4 +1,4 @@
1
- import { P as PetriNet } from '../petri-net-RpZxG0Io.js';
1
+ import { P as PetriNet } from '../petri-net-DXdpd0r5.js';
2
2
 
3
3
  /**
4
4
  * Format-agnostic typed graph model.
@@ -133,12 +133,32 @@ declare function edgeStyle(arcType: EdgeCategory): EdgeVisual;
133
133
  * @module export/petri-net-mapper
134
134
  */
135
135
 
136
+ /**
137
+ * Selects how DOT export groups nodes into `subgraph cluster_*` blocks (per
138
+ * **MOD-040** / **EXP-016**).
139
+ *
140
+ * - `'auto'` — use subnet-membership metadata (per MOD-026) when the net
141
+ * carries any; otherwise fall back to instance-prefix name detection.
142
+ * Default.
143
+ * - `'metadata'` — strictly cluster from subnet-membership metadata; a node
144
+ * with no metadata entry — including a prefix-named instance node — is not
145
+ * clustered. Use `'auto'` to also cluster prefix-named nodes.
146
+ * - `'prefix'` — always cluster from instance-prefix name segments; ignore
147
+ * metadata.
148
+ * - `'none'` — emit no clusters; every node renders at the top level.
149
+ */
150
+ type ClusterSource = 'auto' | 'metadata' | 'prefix' | 'none';
136
151
  interface DotConfig {
137
152
  readonly direction: RankDir;
138
153
  readonly showTypes: boolean;
139
154
  readonly showIntervals: boolean;
140
155
  readonly showPriority: boolean;
141
156
  readonly environmentPlaces?: ReadonlySet<string>;
157
+ /**
158
+ * How to group nodes into `subgraph cluster_*` blocks (per MOD-040 /
159
+ * EXP-016). Additive; defaults to `'auto'` when omitted.
160
+ */
161
+ readonly clusterSource?: ClusterSource;
142
162
  }
143
163
  declare const DEFAULT_DOT_CONFIG: DotConfig;
144
164
  /** Sanitizes a name for use as a graph node ID. */
@@ -173,4 +193,4 @@ declare function renderDot(graph: Graph): string;
173
193
  */
174
194
  declare function dotExport(net: PetriNet, config?: DotConfig): string;
175
195
 
176
- export { type ArrowHead, DEFAULT_DOT_CONFIG, type DotConfig, type EdgeCategory, type EdgeLineStyle, type EdgeVisual, FONT, GRAPH, type Graph, type GraphEdge, type GraphNode, type NodeCategory, type NodeShape, type NodeVisual, type RankDir, type Subgraph, dotExport, edgeStyle, mapToGraph, nodeStyle, renderDot, sanitize };
196
+ export { type ArrowHead, type ClusterSource, DEFAULT_DOT_CONFIG, type DotConfig, type EdgeCategory, type EdgeLineStyle, type EdgeVisual, FONT, GRAPH, type Graph, type GraphEdge, type GraphNode, type NodeCategory, type NodeShape, type NodeVisual, type RankDir, type Subgraph, dotExport, edgeStyle, mapToGraph, nodeStyle, renderDot, sanitize };
@@ -8,7 +8,7 @@ import {
8
8
  nodeStyle,
9
9
  renderDot,
10
10
  sanitize
11
- } from "../chunk-B2CV5M3L.js";
11
+ } from "../chunk-2GAZLDH3.js";
12
12
  import "../chunk-FN773SSE.js";
13
13
  export {
14
14
  DEFAULT_DOT_CONFIG,
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { P as PetriNet, S as SubnetDef, a as Place, T as Token, b as Transition, E as EnvironmentPlace, c as TransitionContext, O as Out } from './petri-net-RpZxG0Io.js';
2
- export { A as Arc, d as ArcInhibitor, e as ArcInput, f as ArcOutput, g as ArcRead, h as ArcReset, C as Channel, i as ComposeBindings, F as FusionSet, j as FusionSetBuilder, I as In, k as InAll, l as InAtLeast, m as InExactly, n as InOne, o as Instance, p as Interface, q as InterfaceBuilder, L as LogFn, M as MAX_DURATION_MS, r as OutAnd, s as OutForwardInput, t as OutPlace, u as OutTimeout, v as OutXor, w as OutputEntry, x as PetriNetBuilder, y as Port, z as PortDirection, B as SubnetDefBuilder, D as SubnetInstance, G as Timing, H as TimingDeadline, J as TimingDelayed, K as TimingExact, N as TimingImmediate, Q as TimingWindow, R as TokenInput, U as TokenOutput, V as TransitionAction, W as TransitionBuilder, X as VerificationHarness, Y as VerificationResult, Z as all, _ as allPlaces, $ as and, a0 as andPlaces, a1 as arcPlace, a2 as atLeast, a3 as consumptionCount, a4 as deadline, a5 as delayed, a6 as earliest, a7 as enumerateBranches, a8 as environmentPlace, a9 as exact, aa as exactly, ab as fork, ac as forwardInput, ad as hasDeadline, ae as hasGuard, af as immediate, ag as inhibitorArc, ah as inputArc, ai as isUnit, aj as latest, ak as matchesGuard, al as one, am as outPlace, an as outputArc, ao as passthrough, ap as place, aq as produce, ar as readArc, as as requiredCount, at as resetArc, au as timeout, av as timeoutPlace, aw as tokenAt, ax as tokenOf, ay as transform, az as transformAsync, aA as transformFrom, aB as unitToken, aC as window, aD as withTimeout, aE as xor, aF as xorPlaces } from './petri-net-RpZxG0Io.js';
3
- import { E as EventStore } from './event-store-DqXOj4j-.js';
4
- export { A as ActionTimedOut, a as ExecutionCompleted, b as ExecutionStarted, I as InMemoryEventStore, L as LogMessage, M as MarkingSnapshot, N as NetEvent, T as TokenAdded, c as TokenRemoved, d as TransitionClockRestarted, e as TransitionCompleted, f as TransitionEnabled, g as TransitionFailed, h as TransitionStarted, i as TransitionTimedOut, j as eventTransitionName, k as eventsOfType, l as failures, m as filterEvents, n as inMemoryEventStore, o as isFailureEvent, p as noopEventStore, t as transitionEvents } from './event-store-DqXOj4j-.js';
1
+ import { P as PetriNet, S as SubnetDef, a as Place, T as Token, b as Transition, E as EnvironmentPlace, c as TransitionContext, O as Out } from './petri-net-DXdpd0r5.js';
2
+ export { A as Arc, d as ArcInhibitor, e as ArcInput, f as ArcOutput, g as ArcRead, h as ArcReset, C as Channel, i as ComposeBindings, F as FusionSet, j as FusionSetBuilder, I as In, k as InAll, l as InAtLeast, m as InExactly, n as InOne, o as Instance, p as Interface, q as InterfaceBuilder, L as LogFn, M as MAX_DURATION_MS, r as OutAnd, s as OutForwardInput, t as OutPlace, u as OutTimeout, v as OutXor, w as OutputEntry, x as PetriNetBuilder, y as Port, z as PortDirection, B as SubnetDefBuilder, D as SubnetInstance, G as Timing, H as TimingDeadline, J as TimingDelayed, K as TimingExact, N as TimingImmediate, Q as TimingWindow, R as TokenInput, U as TokenOutput, V as TransitionAction, W as TransitionBuilder, X as VerificationHarness, Y as VerificationResult, Z as all, _ as allPlaces, $ as and, a0 as andPlaces, a1 as arcPlace, a2 as atLeast, a3 as consumptionCount, a4 as deadline, a5 as delayed, a6 as earliest, a7 as enumerateBranches, a8 as environmentPlace, a9 as exact, aa as exactly, ab as fork, ac as forwardInput, ad as hasDeadline, ae as hasGuard, af as immediate, ag as inhibitorArc, ah as inputArc, ai as isUnit, aj as latest, ak as matchesGuard, al as one, am as outPlace, an as outputArc, ao as passthrough, ap as place, aq as produce, ar as readArc, as as requiredCount, at as resetArc, au as timeout, av as timeoutPlace, aw as tokenAt, ax as tokenOf, ay as transform, az as transformAsync, aA as transformFrom, aB as unitToken, aC as window, aD as withTimeout, aE as xor, aF as xorPlaces } from './petri-net-DXdpd0r5.js';
3
+ import { E as EventStore } from './event-store-z30VMn0T.js';
4
+ export { A as ActionTimedOut, a as ExecutionCompleted, b as ExecutionStarted, I as InMemoryEventStore, L as LogMessage, M as MarkingSnapshot, N as NetEvent, T as TokenAdded, c as TokenRemoved, d as TransitionClockRestarted, e as TransitionCompleted, f as TransitionEnabled, g as TransitionFailed, h as TransitionStarted, i as TransitionTimedOut, j as eventTransitionName, k as eventsOfType, l as failures, m as filterEvents, n as inMemoryEventStore, o as isFailureEvent, p as noopEventStore, t as transitionEvents } from './event-store-z30VMn0T.js';
5
5
 
6
6
  /**
7
7
  * Discriminated sum-type abstraction over Petri nets, distinguishing **closed**
package/dist/index.js CHANGED
@@ -1646,12 +1646,25 @@ var PetriNet = class _PetriNet {
1646
1646
  name;
1647
1647
  places;
1648
1648
  transitions;
1649
+ /**
1650
+ * Subnet-membership metadata per **MOD-026**: maps each node name (place or
1651
+ * transition) contributed by exactly one directly-composed subnet to that
1652
+ * subnet's name. Shared places contributed by two or more subnets, and every
1653
+ * node of a net not built via {@link PetriNetBuilder.compose}`(SubnetDef)`,
1654
+ * are absent. Never null — an empty map when there is no metadata. The DOT
1655
+ * exporter renders `subgraph cluster_*` blocks from this map.
1656
+ *
1657
+ * V8 `Map` is insertion-ordered, preserving compose order so cluster
1658
+ * subgraphs render deterministically (cross-language byte-parity).
1659
+ */
1660
+ subnetMembership;
1649
1661
  /** @internal Use {@link PetriNet.builder} to create instances. */
1650
- constructor(key, name, places, transitions) {
1662
+ constructor(key, name, places, transitions, subnetMembership = /* @__PURE__ */ new Map()) {
1651
1663
  if (key !== PETRI_NET_KEY) throw new Error("Use PetriNet.builder() to create instances");
1652
1664
  this.name = name;
1653
1665
  this.places = places;
1654
1666
  this.transitions = transitions;
1667
+ this.subnetMembership = subnetMembership;
1655
1668
  }
1656
1669
  /**
1657
1670
  * Creates a new PetriNet with actions bound to transitions by name.
@@ -1676,17 +1689,28 @@ var PetriNet = class _PetriNet {
1676
1689
  boundTransitions.add(t);
1677
1690
  }
1678
1691
  }
1679
- return new _PetriNet(PETRI_NET_KEY, this.name, this.places, boundTransitions);
1692
+ return new _PetriNet(
1693
+ PETRI_NET_KEY,
1694
+ this.name,
1695
+ this.places,
1696
+ boundTransitions,
1697
+ this.subnetMembership
1698
+ );
1680
1699
  }
1681
1700
  static builder(name) {
1682
1701
  return new PetriNetBuilder(name);
1683
1702
  }
1684
1703
  };
1685
- var PetriNetBuilder = class {
1704
+ var PetriNetBuilder = class _PetriNetBuilder {
1686
1705
  _name;
1687
1706
  _places = /* @__PURE__ */ new Set();
1688
1707
  _transitions = /* @__PURE__ */ new Set();
1689
1708
  _fusionSets = [];
1709
+ // MOD-026: node name -> set of subnet names that contributed it via
1710
+ // compose(SubnetDef), in first-contribution order. Resolved to single-owner
1711
+ // membership at build(). V8 Map/Set iterate in insertion order, preserving
1712
+ // compose order for cross-language byte-parity.
1713
+ _subnetContributions = /* @__PURE__ */ new Map();
1690
1714
  constructor(name) {
1691
1715
  this._name = name;
1692
1716
  }
@@ -1795,17 +1819,29 @@ var PetriNetBuilder = class {
1795
1819
  }
1796
1820
  const hostByName = /* @__PURE__ */ new Map();
1797
1821
  for (const p of this._places) hostByName.set(p.name, p);
1822
+ const subnetName = def.name.replace(/\//g, "_");
1798
1823
  const mergeMap = /* @__PURE__ */ new Map();
1799
1824
  for (const p of body.places) {
1800
1825
  const host = hostByName.get(p.name);
1801
1826
  this.place(host ?? p);
1802
1827
  if (host !== void 0) mergeMap.set(p.name, host);
1828
+ this.recordContribution(p.name, subnetName);
1803
1829
  }
1804
1830
  for (const t of body.transitions) {
1805
1831
  this.transition(substitutePlaces(t, mergeMap));
1832
+ this.recordContribution(t.name, subnetName);
1806
1833
  }
1807
1834
  return this;
1808
1835
  }
1836
+ /** @internal MOD-026: records a node as contributed by the named subnet. */
1837
+ recordContribution(nodeName, subnetName) {
1838
+ let owners = this._subnetContributions.get(nodeName);
1839
+ if (owners === void 0) {
1840
+ owners = /* @__PURE__ */ new Set();
1841
+ this._subnetContributions.set(nodeName, owners);
1842
+ }
1843
+ owners.add(subnetName);
1844
+ }
1809
1845
  /**
1810
1846
  * Identity-default auto-compose per **MOD-024**.
1811
1847
  *
@@ -1990,16 +2026,58 @@ var PetriNetBuilder = class {
1990
2026
  * @throws when two fusion sets share a place
1991
2027
  */
1992
2028
  build() {
2029
+ const membership = this.resolveSubnetMembership();
1993
2030
  if (this._fusionSets.length === 0) {
1994
- return new PetriNet(PETRI_NET_KEY, this._name, this._places, this._transitions);
2031
+ return new PetriNet(
2032
+ PETRI_NET_KEY,
2033
+ this._name,
2034
+ this._places,
2035
+ this._transitions,
2036
+ membership
2037
+ );
1995
2038
  }
1996
- return this.buildWithFusion();
2039
+ return this.buildWithFusion(membership);
2040
+ }
2041
+ /**
2042
+ * @internal Resolves the per-compose contributions recorded by
2043
+ * `composeDirect` into the final node-name → subnet-name membership map per
2044
+ * **MOD-026**. A node contributed by exactly one subnet maps to that subnet;
2045
+ * a place contributed by two or more subnets is a shared rendezvous place
2046
+ * and is omitted (it renders top-level, outside any cluster). Returns an
2047
+ * empty map — the common case — when no subnet was composed directly.
2048
+ */
2049
+ resolveSubnetMembership() {
2050
+ const resolved = /* @__PURE__ */ new Map();
2051
+ for (const [nodeName, owners] of this._subnetContributions) {
2052
+ if (owners.size === 1) {
2053
+ resolved.set(nodeName, owners.values().next().value);
2054
+ }
2055
+ }
2056
+ return resolved;
2057
+ }
2058
+ /**
2059
+ * @internal Drops membership entries for places removed by fusion: a
2060
+ * non-canonical fused member no longer exists in the net, so its
2061
+ * `node-name → subnet` entry would dangle. The surviving canonical place
2062
+ * keeps its own entry. Per **MOD-026**.
2063
+ */
2064
+ static filterFusedMembership(membership, nonCanonicalNames) {
2065
+ if (membership.size === 0 || nonCanonicalNames.size === 0) {
2066
+ return membership;
2067
+ }
2068
+ const filtered = /* @__PURE__ */ new Map();
2069
+ for (const [key, value] of membership) {
2070
+ if (!nonCanonicalNames.has(key)) {
2071
+ filtered.set(key, value);
2072
+ }
2073
+ }
2074
+ return filtered;
1997
2075
  }
1998
2076
  /**
1999
2077
  * @internal Fusion-resolution pass per **MOD-061**. Split out from
2000
2078
  * {@link build} so the no-fusion fast path stays trivial.
2001
2079
  */
2002
- buildWithFusion() {
2080
+ buildWithFusion(membership) {
2003
2081
  const ownership = /* @__PURE__ */ new Map();
2004
2082
  for (const set of this._fusionSets) {
2005
2083
  for (const member of set.members) {
@@ -2035,7 +2113,13 @@ var PetriNetBuilder = class {
2035
2113
  for (const r of t.reads) rebuiltPlaces.add(r.place);
2036
2114
  for (const r of t.resets) rebuiltPlaces.add(r.place);
2037
2115
  }
2038
- return new PetriNet(PETRI_NET_KEY, this._name, rebuiltPlaces, rewrittenTransitions);
2116
+ return new PetriNet(
2117
+ PETRI_NET_KEY,
2118
+ this._name,
2119
+ rebuiltPlaces,
2120
+ rewrittenTransitions,
2121
+ _PetriNetBuilder.filterFusedMembership(membership, nonCanonicalNames)
2122
+ );
2039
2123
  }
2040
2124
  };
2041
2125
  function rebuildWithAction(t, action) {