libpetri 2.1.0 → 2.3.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/debug/index.d.ts +2 -2
- package/dist/doclet/index.d.ts +1 -1
- package/dist/doclet/resources/petrinet-diagrams.css +7 -0
- package/dist/doclet/resources/petrinet-diagrams.js +28 -26
- package/dist/{event-store-2zkXeQkd.d.ts → event-store-I3QiQABh.d.ts} +1 -1
- package/dist/export/index.d.ts +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -1
- package/dist/{petri-net-BDrj4XZE.d.ts → petri-net-CKZYPOsL.d.ts} +30 -0
- package/dist/verification/index.d.ts +2 -2
- package/dist/viewer/index.d.ts +36 -1
- package/dist/viewer/index.js +74 -2
- package/dist/viewer/index.js.map +1 -1
- package/dist/viewer/viewer-static.iife.js +4 -2
- package/dist/viewer/viewer.css +7 -0
- package/dist/viewer/viewer.iife.js +28 -26
- package/package.json +1 -1
|
@@ -1360,8 +1360,28 @@ declare class PetriNetBuilder {
|
|
|
1360
1360
|
* channel name is unknown on the interface, or when caller- and
|
|
1361
1361
|
* instance-side transition timings conflict (per [MOD-021]).
|
|
1362
1362
|
*/
|
|
1363
|
+
compose(instance: Instance<unknown>): this;
|
|
1363
1364
|
compose(instance: Instance<unknown>, portMappings: ReadonlyMap<string, Place<unknown>> | Record<string, Place<unknown>>): this;
|
|
1364
1365
|
compose(instance: Instance<unknown>, bind: (b: ComposeBindings) => void): this;
|
|
1366
|
+
/**
|
|
1367
|
+
* Identity-default auto-compose per **MOD-024**.
|
|
1368
|
+
*
|
|
1369
|
+
* Each declared interface port auto-binds to its own `port.place` — the
|
|
1370
|
+
* Place the SubnetDef builder declared via `.inputPort(name, hostPlace)`
|
|
1371
|
+
* (or `outputPort` / `inoutPort`). If the host builder already declares
|
|
1372
|
+
* the equal place, the two merge; if not, the place arrives implicitly
|
|
1373
|
+
* via the rewritten transitions' arcs (same flow as explicit `bindPort`).
|
|
1374
|
+
*
|
|
1375
|
+
* If the subnet declares no interface ports at all, body places are
|
|
1376
|
+
* checked against this builder's place set **by name** (matching the
|
|
1377
|
+
* existing TS Place equality semantics; see [CORE-002] note in
|
|
1378
|
+
* `spec/11-modular-composition.md` MOD-024). Body places that don't match
|
|
1379
|
+
* stay private under their prefixed names per [MOD-010].
|
|
1380
|
+
*
|
|
1381
|
+
* Channels are NOT auto-bound — transition identity is too delicate for
|
|
1382
|
+
* inference. If the subnet declares any channel, this overload throws.
|
|
1383
|
+
*/
|
|
1384
|
+
private composeAuto;
|
|
1365
1385
|
/**
|
|
1366
1386
|
* @internal Shared compose implementation: validates port and channel
|
|
1367
1387
|
* bindings, builds the place-substitution map, walks every renamed-body
|
|
@@ -1396,6 +1416,16 @@ declare class PetriNetBuilder {
|
|
|
1396
1416
|
* channel-handle map — but the prefixed names remain stable.
|
|
1397
1417
|
*/
|
|
1398
1418
|
private composeInternal;
|
|
1419
|
+
/**
|
|
1420
|
+
* Shared post-mergeMap pipeline: rewrites renamed-body transitions
|
|
1421
|
+
* through `mergeMap`, applies channel merges per **MOD-021**, and adds
|
|
1422
|
+
* the surviving transitions to the builder.
|
|
1423
|
+
*
|
|
1424
|
+
* Used by both the explicit-binding path (`composeInternal`) and the
|
|
1425
|
+
* auto-compose path (`composeAuto` per **MOD-024**). The two paths differ
|
|
1426
|
+
* only in how the (renamed Place name → host Place) `mergeMap` is built.
|
|
1427
|
+
*/
|
|
1428
|
+
private applyComposition;
|
|
1399
1429
|
/**
|
|
1400
1430
|
* Registers one or more {@link FusionSet} declarations on this builder, per
|
|
1401
1431
|
* **MOD-060** (fusion set declaration) and **MOD-061** (fusion resolution
|
|
@@ -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-CKZYPOsL.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-CKZYPOsL.js';
|
|
3
3
|
import { Expr, init, Bool, FuncDecl } from 'z3-solver';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/viewer/index.d.ts
CHANGED
|
@@ -135,6 +135,16 @@ declare const VIEWER_CSS_VARIABLES: readonly ["--lpv-bg", "--lpv-header-bg", "--
|
|
|
135
135
|
* @module viewer
|
|
136
136
|
*/
|
|
137
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Subnet (cluster) visibility mode.
|
|
140
|
+
*
|
|
141
|
+
* - `'show'` — render the DOT as-is with `subgraph cluster_*` groupings
|
|
142
|
+
* visible (the default; the post-subnets layout).
|
|
143
|
+
* - `'hide'` — render a flattened variant that drops cluster wrappers
|
|
144
|
+
* and the `ltail`/`lhead` cluster references on cross-cluster edges.
|
|
145
|
+
* Same nodes and edges, no visual grouping.
|
|
146
|
+
*/
|
|
147
|
+
type SubnetVisibility = 'show' | 'hide';
|
|
138
148
|
/** Handle returned by {@link mount}. */
|
|
139
149
|
interface ViewerHandle {
|
|
140
150
|
/** The rendered root `<svg>` element. */
|
|
@@ -147,6 +157,22 @@ interface ViewerHandle {
|
|
|
147
157
|
readonly collapsedPrefixes: ReadonlySet<string>;
|
|
148
158
|
/** Currently active "show only <prefix>" filter, or null. */
|
|
149
159
|
readonly activeFilter: string | null;
|
|
160
|
+
/** Current subnet visibility mode. */
|
|
161
|
+
readonly subnets: SubnetVisibility;
|
|
162
|
+
/**
|
|
163
|
+
* Switch between the clustered (`'show'`) and flat (`'hide'`) views.
|
|
164
|
+
* Triggers an internal re-mount via the canonical {@link mount} path —
|
|
165
|
+
* the SVG is regenerated because Graphviz lays out cluster boundaries
|
|
166
|
+
* during layout. Returns the new handle; the old one is disposed.
|
|
167
|
+
*
|
|
168
|
+
* After re-mount, the host container dispatches a bubbling
|
|
169
|
+
* `libpetri-viewer:remount` CustomEvent whose `detail.handle` is the
|
|
170
|
+
* fresh handle. Consumers that cache the handle (e.g. the debug-ui)
|
|
171
|
+
* should listen on the container and update their reference.
|
|
172
|
+
*/
|
|
173
|
+
setSubnets(mode: SubnetVisibility): Promise<ViewerHandle>;
|
|
174
|
+
/** Flip {@link subnets} to the other mode and re-mount. See {@link setSubnets}. */
|
|
175
|
+
toggleSubnets(): Promise<ViewerHandle>;
|
|
150
176
|
/** Collapse a single cluster by prefix. No-op if unknown. */
|
|
151
177
|
collapse(prefix: string): void;
|
|
152
178
|
/** Expand a single cluster by prefix. No-op if unknown. */
|
|
@@ -203,6 +229,15 @@ interface MountOptions {
|
|
|
203
229
|
* cached on the DOT hash; re-mounts on identical DOT skip the pipeline.
|
|
204
230
|
*/
|
|
205
231
|
readonly layout?: 'elk' | 'graphviz';
|
|
232
|
+
/**
|
|
233
|
+
* Initial subnet visibility mode. Defaults to `'show'` (clustered view).
|
|
234
|
+
* Pass `'hide'` to mount with the flat view from the start. The chrome
|
|
235
|
+
* button toggles between the two at runtime; see {@link ViewerHandle.setSubnets}.
|
|
236
|
+
*
|
|
237
|
+
* If `previousHandle` is supplied and `subnets` is omitted, the previous
|
|
238
|
+
* handle's mode is inherited so live re-renders preserve the user's choice.
|
|
239
|
+
*/
|
|
240
|
+
readonly subnets?: SubnetVisibility;
|
|
206
241
|
}
|
|
207
242
|
/**
|
|
208
243
|
* Render a DOT source, mount the resulting SVG into `container`, wire pan/zoom,
|
|
@@ -224,4 +259,4 @@ interface MountOptions {
|
|
|
224
259
|
*/
|
|
225
260
|
declare function mount(dotSource: string | null, container: HTMLElement, opts?: MountOptions): Promise<ViewerHandle>;
|
|
226
261
|
|
|
227
|
-
export { type ClusterDescriptor, DEFAULT_PANZOOM_OPTS, type MountOptions, type PanzoomInstance, type PanzoomOptions, VIEWER_CSS_VARIABLES, type ViewerHandle, colorForPrefix, discoverClusters, mount };
|
|
262
|
+
export { type ClusterDescriptor, DEFAULT_PANZOOM_OPTS, type MountOptions, type PanzoomInstance, type PanzoomOptions, type SubnetVisibility, VIEWER_CSS_VARIABLES, type ViewerHandle, colorForPrefix, discoverClusters, mount };
|
package/dist/viewer/index.js
CHANGED
|
@@ -655,11 +655,46 @@ var VIEWER_CSS_VARIABLES = [
|
|
|
655
655
|
"--lpv-faded-cluster-opacity"
|
|
656
656
|
];
|
|
657
657
|
|
|
658
|
+
// src/viewer/dot-flatten.ts
|
|
659
|
+
var CLUSTER_OPEN = /^\s*subgraph\s+cluster_[A-Za-z0-9_]+\s*\{\s*$/;
|
|
660
|
+
var ONLY_CLOSE_BRACE = /^\s*\}\s*$/;
|
|
661
|
+
function flattenClusters(dot) {
|
|
662
|
+
const lines = dot.split("\n");
|
|
663
|
+
const out = [];
|
|
664
|
+
let clusterDepth = 0;
|
|
665
|
+
for (const line of lines) {
|
|
666
|
+
if (CLUSTER_OPEN.test(line)) {
|
|
667
|
+
clusterDepth++;
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
if (clusterDepth > 0) {
|
|
671
|
+
if (ONLY_CLOSE_BRACE.test(line)) {
|
|
672
|
+
clusterDepth--;
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
if (line.includes("[") || line.includes("->")) {
|
|
676
|
+
out.push(stripClusterEdgeAttrs(line));
|
|
677
|
+
}
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
if (line.includes("->") && (line.includes("ltail=") || line.includes("lhead="))) {
|
|
681
|
+
out.push(stripClusterEdgeAttrs(line));
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
out.push(line);
|
|
685
|
+
}
|
|
686
|
+
return out.join("\n");
|
|
687
|
+
}
|
|
688
|
+
function stripClusterEdgeAttrs(line) {
|
|
689
|
+
return line.replace(/,\s*ltail="cluster_[^"]*"/g, "").replace(/,\s*lhead="cluster_[^"]*"/g, "").replace(/\[\s*ltail="cluster_[^"]*"\s*,\s*/g, "[").replace(/\[\s*lhead="cluster_[^"]*"\s*,\s*/g, "[").replace(/\[\s*ltail="cluster_[^"]*"\s*\]/g, "[]").replace(/\[\s*lhead="cluster_[^"]*"\s*\]/g, "[]");
|
|
690
|
+
}
|
|
691
|
+
|
|
658
692
|
// src/viewer/index.ts
|
|
659
693
|
async function mount(dotSource, container, opts = {}) {
|
|
660
694
|
const previousHandle = opts.previousHandle ?? null;
|
|
661
695
|
const preservedCollapsed = previousHandle ? new Set(previousHandle.collapsedPrefixes) : /* @__PURE__ */ new Set();
|
|
662
696
|
const preservedFilter = previousHandle?.activeFilter ?? null;
|
|
697
|
+
const subnetsMode = opts.subnets ?? previousHandle?.subnets ?? "show";
|
|
663
698
|
if (previousHandle) {
|
|
664
699
|
previousHandle.dispose();
|
|
665
700
|
}
|
|
@@ -673,7 +708,8 @@ async function mount(dotSource, container, opts = {}) {
|
|
|
673
708
|
} else {
|
|
674
709
|
const renderModule = await import("../render-QK57X4TP.js");
|
|
675
710
|
const useElk = (opts.layout ?? "elk") === "elk";
|
|
676
|
-
|
|
711
|
+
const renderedDot = subnetsMode === "hide" ? flattenClusters(dotSource) : dotSource;
|
|
712
|
+
svg = useElk ? await renderModule.renderDotToSvgWithElkLayout(renderedDot) : await renderModule.renderDotToSvg(renderedDot);
|
|
677
713
|
container.innerHTML = "";
|
|
678
714
|
container.appendChild(svg);
|
|
679
715
|
}
|
|
@@ -716,6 +752,19 @@ async function mount(dotSource, container, opts = {}) {
|
|
|
716
752
|
fsBtn.title = "Toggle fullscreen";
|
|
717
753
|
fsBtn.textContent = "Fullscreen";
|
|
718
754
|
fsBtn.addEventListener("click", () => toggleFullscreen(container, fsBtn));
|
|
755
|
+
const subnetsBtn = document.createElement("button");
|
|
756
|
+
subnetsBtn.type = "button";
|
|
757
|
+
subnetsBtn.className = "diagram-btn btn-subnets";
|
|
758
|
+
subnetsBtn.title = subnetsMode === "show" ? "Hide subnet groupings" : "Show subnet groupings";
|
|
759
|
+
subnetsBtn.textContent = subnetsMode === "show" ? "Flat view" : "Subnets view";
|
|
760
|
+
if (dotSource == null) {
|
|
761
|
+
subnetsBtn.disabled = true;
|
|
762
|
+
subnetsBtn.title = "Subnet toggle unavailable for pre-rendered SVG";
|
|
763
|
+
}
|
|
764
|
+
subnetsBtn.addEventListener("click", () => {
|
|
765
|
+
void handle.toggleSubnets();
|
|
766
|
+
});
|
|
767
|
+
controls.appendChild(subnetsBtn);
|
|
719
768
|
controls.appendChild(resetBtn);
|
|
720
769
|
controls.appendChild(fsBtn);
|
|
721
770
|
chromeRoot.appendChild(controls);
|
|
@@ -723,7 +772,7 @@ async function mount(dotSource, container, opts = {}) {
|
|
|
723
772
|
container.style.position = "relative";
|
|
724
773
|
}
|
|
725
774
|
container.appendChild(chromeRoot);
|
|
726
|
-
if (layoutMode === "elk" && clusters.size > 0) {
|
|
775
|
+
if (layoutMode === "elk" && subnetsMode === "show" && clusters.size > 0) {
|
|
727
776
|
sidebarHandle = mountSidebar(container, clusters, {
|
|
728
777
|
onVisibilityChange: (state) => handle.setVisibility(state),
|
|
729
778
|
onHighlightModeChange: (enabled) => {
|
|
@@ -748,6 +797,29 @@ async function mount(dotSource, container, opts = {}) {
|
|
|
748
797
|
get activeFilter() {
|
|
749
798
|
return activeFilter;
|
|
750
799
|
},
|
|
800
|
+
get subnets() {
|
|
801
|
+
return subnetsMode;
|
|
802
|
+
},
|
|
803
|
+
setSubnets(mode) {
|
|
804
|
+
if (dotSource == null) return Promise.resolve(handle);
|
|
805
|
+
if (mode === subnetsMode) return Promise.resolve(handle);
|
|
806
|
+
return mount(dotSource, container, {
|
|
807
|
+
...opts,
|
|
808
|
+
previousHandle: handle,
|
|
809
|
+
subnets: mode
|
|
810
|
+
}).then((next) => {
|
|
811
|
+
container.dispatchEvent(
|
|
812
|
+
new CustomEvent("libpetri-viewer:remount", {
|
|
813
|
+
bubbles: true,
|
|
814
|
+
detail: { handle: next, subnets: mode }
|
|
815
|
+
})
|
|
816
|
+
);
|
|
817
|
+
return next;
|
|
818
|
+
});
|
|
819
|
+
},
|
|
820
|
+
toggleSubnets() {
|
|
821
|
+
return handle.setSubnets(subnetsMode === "show" ? "hide" : "show");
|
|
822
|
+
},
|
|
751
823
|
collapse(prefix) {
|
|
752
824
|
if (disposed) return;
|
|
753
825
|
const cluster = clusters.get(prefix);
|