libpetri 4.1.0 → 5.0.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-GJAHGHGT.js → chunk-4HMFNYTH.js} +2 -2
- package/dist/{chunk-FD3S4TZM.js → chunk-75KEJQGC.js} +126 -27
- package/dist/chunk-75KEJQGC.js.map +1 -0
- package/dist/debug/index.d.ts +2 -2
- package/dist/doclet/index.d.ts +1 -1
- package/dist/doclet/resources/petrinet-diagrams.js +1 -1
- package/dist/{event-store-B3ppVTpO.d.ts → event-store-hlH3-bzJ.d.ts} +1 -1
- package/dist/export/index.d.ts +1 -1
- package/dist/index.d.ts +46 -13
- package/dist/index.js +131 -74
- package/dist/index.js.map +1 -1
- package/dist/{petri-net-p0ONpSAO.d.ts → petri-net-CH7UvjOW.d.ts} +24 -3
- package/dist/render-dom/index.js +1 -1
- package/dist/verification/index.d.ts +2 -2
- package/dist/verification/index.js +3 -1
- package/dist/verification/index.js.map +1 -1
- package/dist/viewer/index.js +1 -1
- package/dist/viewer/viewer.iife.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-FD3S4TZM.js.map +0 -1
- /package/dist/{chunk-GJAHGHGT.js.map → chunk-4HMFNYTH.js.map} +0 -0
|
@@ -762,7 +762,7 @@ function stripClusterEdgeAttrs(line) {
|
|
|
762
762
|
}
|
|
763
763
|
|
|
764
764
|
// src/viewer/version.ts
|
|
765
|
-
var VERSION = true ? "
|
|
765
|
+
var VERSION = true ? "5.0.0" : "0.0.0-dev";
|
|
766
766
|
|
|
767
767
|
// src/viewer/index.ts
|
|
768
768
|
async function mount(dotSource, container, opts = {}) {
|
|
@@ -1013,4 +1013,4 @@ export {
|
|
|
1013
1013
|
VERSION,
|
|
1014
1014
|
mount
|
|
1015
1015
|
};
|
|
1016
|
-
//# sourceMappingURL=chunk-
|
|
1016
|
+
//# sourceMappingURL=chunk-4HMFNYTH.js.map
|
|
@@ -331,6 +331,9 @@ var MarkingStateBuilder = class {
|
|
|
331
331
|
function deadlockFree() {
|
|
332
332
|
return { type: "deadlock-free" };
|
|
333
333
|
}
|
|
334
|
+
function terminatesAtSink() {
|
|
335
|
+
return { type: "terminates-at-sink" };
|
|
336
|
+
}
|
|
334
337
|
function mutualExclusion(p1, p2) {
|
|
335
338
|
return { type: "mutual-exclusion", p1, p2 };
|
|
336
339
|
}
|
|
@@ -350,6 +353,8 @@ function propertyDescription(prop) {
|
|
|
350
353
|
switch (prop.type) {
|
|
351
354
|
case "deadlock-free":
|
|
352
355
|
return "Deadlock-freedom";
|
|
356
|
+
case "terminates-at-sink":
|
|
357
|
+
return "Terminates at a declared sink";
|
|
353
358
|
case "mutual-exclusion":
|
|
354
359
|
return `Mutual exclusion of ${prop.p1.name} and ${prop.p2.name}`;
|
|
355
360
|
case "place-bound":
|
|
@@ -1545,8 +1550,31 @@ function indexOrdered(flatNet, places) {
|
|
|
1545
1550
|
}
|
|
1546
1551
|
function encodePropertyViolation(flatNet, property, mVars, sinkPlaces, envInject) {
|
|
1547
1552
|
switch (property.type) {
|
|
1548
|
-
|
|
1549
|
-
|
|
1553
|
+
// DeadlockFree (VER-002): a quiescent marking that STRANDS a token — holds one
|
|
1554
|
+
// in a place that is not a declared sink. The empty marking strands nothing and
|
|
1555
|
+
// is therefore not a violation (AC4).
|
|
1556
|
+
case "deadlock-free": {
|
|
1557
|
+
const conditions = encodeQuiescent(flatNet, mVars, envInject);
|
|
1558
|
+
if (conditions == null) return "false";
|
|
1559
|
+
const sinks = new Set(indexOrdered(flatNet, sinkPlaces));
|
|
1560
|
+
const stranded = [];
|
|
1561
|
+
for (let pid = 0; pid < flatNet.places.length; pid++) {
|
|
1562
|
+
if (!sinks.has(pid)) stranded.push(`(>= ${mVars[pid]} 1)`);
|
|
1563
|
+
}
|
|
1564
|
+
if (stranded.length === 0) return "false";
|
|
1565
|
+
conditions.push(`(or ${stranded.join(" ")})`);
|
|
1566
|
+
return joinConditions(conditions);
|
|
1567
|
+
}
|
|
1568
|
+
// TerminatesAtSink (VER-002): a quiescent marking that reached NO declared sink.
|
|
1569
|
+
// This is the predicate DeadlockFree carried before the VER-002 split, unchanged.
|
|
1570
|
+
case "terminates-at-sink": {
|
|
1571
|
+
const conditions = encodeQuiescent(flatNet, mVars, envInject);
|
|
1572
|
+
if (conditions == null) return "false";
|
|
1573
|
+
for (const pid of indexOrdered(flatNet, sinkPlaces)) {
|
|
1574
|
+
conditions.push(`(= ${mVars[pid]} 0)`);
|
|
1575
|
+
}
|
|
1576
|
+
return joinConditions(conditions);
|
|
1577
|
+
}
|
|
1550
1578
|
case "mutual-exclusion": {
|
|
1551
1579
|
const conditions = indexOrdered(flatNet, [property.p1, property.p2]).map((i) => `(>= ${mVars[i]} 1)`);
|
|
1552
1580
|
return conditions.length === 0 ? "false" : `(and ${conditions.join(" ")})`;
|
|
@@ -1560,14 +1588,23 @@ function encodePropertyViolation(flatNet, property, mVars, sinkPlaces, envInject
|
|
|
1560
1588
|
const conditions = indexOrdered(flatNet, property.places).map((i) => `(>= ${mVars[i]} 1)`);
|
|
1561
1589
|
return conditions.length === 0 ? "false" : `(and ${conditions.join(" ")})`;
|
|
1562
1590
|
}
|
|
1591
|
+
// JoinedOrDeadLettered (NU-040 AC4): a quiescent state that still holds a
|
|
1592
|
+
// `pending` token is a stranded correlation group. Carries NO sink clause — a
|
|
1593
|
+
// declared sink must not excuse a stranded group.
|
|
1563
1594
|
case "joined-or-dead-lettered": {
|
|
1564
|
-
const deadlock = encodeDeadlock(flatNet, mVars, sinkPlaces, envInject);
|
|
1565
1595
|
const pid = flatNet.placeIndex.get(property.pending.name);
|
|
1566
|
-
|
|
1596
|
+
if (pid == null) return "false";
|
|
1597
|
+
const conditions = encodeQuiescent(flatNet, mVars, envInject);
|
|
1598
|
+
if (conditions == null) return "false";
|
|
1599
|
+
conditions.push(`(>= ${mVars[pid]} 1)`);
|
|
1600
|
+
return joinConditions(conditions);
|
|
1567
1601
|
}
|
|
1568
1602
|
}
|
|
1569
1603
|
}
|
|
1570
|
-
function
|
|
1604
|
+
function joinConditions(conditions) {
|
|
1605
|
+
return conditions.length === 0 ? "true" : `(and ${conditions.join("\n ")})`;
|
|
1606
|
+
}
|
|
1607
|
+
function encodeQuiescent(flatNet, mVars, envInject) {
|
|
1571
1608
|
const envBound = /* @__PURE__ */ new Map();
|
|
1572
1609
|
for (const inj of envInject) envBound.set(inj.pid, inj.bound);
|
|
1573
1610
|
const disabledConditions = [];
|
|
@@ -1597,13 +1634,10 @@ function encodeDeadlock(flatNet, mVars, sinkPlaces, envInject) {
|
|
|
1597
1634
|
disabledConditions.push("true");
|
|
1598
1635
|
continue;
|
|
1599
1636
|
}
|
|
1600
|
-
if (disableReasons.length === 0) return
|
|
1637
|
+
if (disableReasons.length === 0) return null;
|
|
1601
1638
|
disabledConditions.push(`(or ${disableReasons.join(" ")})`);
|
|
1602
1639
|
}
|
|
1603
|
-
|
|
1604
|
-
disabledConditions.push(`(= ${mVars[pid]} 0)`);
|
|
1605
|
-
}
|
|
1606
|
-
return disabledConditions.length === 0 ? "true" : `(and ${disabledConditions.join("\n ")})`;
|
|
1640
|
+
return disabledConditions;
|
|
1607
1641
|
}
|
|
1608
1642
|
function injectionMap(flatNet) {
|
|
1609
1643
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -2511,20 +2545,38 @@ function enabledRelaxEnv(state, ft, envInj) {
|
|
|
2511
2545
|
}
|
|
2512
2546
|
return true;
|
|
2513
2547
|
}
|
|
2514
|
-
function
|
|
2548
|
+
function isQuiescent(index, state) {
|
|
2515
2549
|
for (const ft of index.flatNet.transitions) {
|
|
2516
2550
|
if (enabledRelaxEnv(state, ft, index.envInj)) return false;
|
|
2517
2551
|
}
|
|
2518
2552
|
return true;
|
|
2519
2553
|
}
|
|
2554
|
+
function sinkIndices(flatNet, sinkPlaces) {
|
|
2555
|
+
const idx = /* @__PURE__ */ new Set();
|
|
2556
|
+
for (const sink of sinkPlaces) {
|
|
2557
|
+
const i = flatNetIndexOf(flatNet, sink);
|
|
2558
|
+
if (i >= 0) idx.add(i);
|
|
2559
|
+
}
|
|
2560
|
+
return idx;
|
|
2561
|
+
}
|
|
2520
2562
|
function satisfiesBadIndexed(index, state, property, sinkPlaces) {
|
|
2521
2563
|
const flatNet = index.flatNet;
|
|
2522
2564
|
switch (property.type) {
|
|
2565
|
+
// DeadlockFree (VER-002): quiescent AND some marked place is not a declared
|
|
2566
|
+
// sink. Mirrors the encoder's `stranded` disjunction.
|
|
2523
2567
|
case "deadlock-free": {
|
|
2524
|
-
if (!
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
if (
|
|
2568
|
+
if (!isQuiescent(index, state)) return false;
|
|
2569
|
+
const sinks = sinkIndices(flatNet, sinkPlaces);
|
|
2570
|
+
for (let pid = 0; pid < flatNet.places.length; pid++) {
|
|
2571
|
+
if (!sinks.has(pid) && state[pid] >= 1) return true;
|
|
2572
|
+
}
|
|
2573
|
+
return false;
|
|
2574
|
+
}
|
|
2575
|
+
// TerminatesAtSink (VER-002): quiescent AND no declared sink marked.
|
|
2576
|
+
case "terminates-at-sink": {
|
|
2577
|
+
if (!isQuiescent(index, state)) return false;
|
|
2578
|
+
for (const pid of sinkIndices(flatNet, sinkPlaces)) {
|
|
2579
|
+
if (state[pid] !== 0) return false;
|
|
2528
2580
|
}
|
|
2529
2581
|
return true;
|
|
2530
2582
|
}
|
|
@@ -2540,10 +2592,12 @@ function satisfiesBadIndexed(index, state, property, sinkPlaces) {
|
|
|
2540
2592
|
if (idx < 0) return false;
|
|
2541
2593
|
return state[idx] > property.bound;
|
|
2542
2594
|
}
|
|
2595
|
+
// JoinedOrDeadLettered (NU-040 AC4): quiescent AND `pending` marked. No sink
|
|
2596
|
+
// clause — a marked sink must not excuse a stranded group.
|
|
2543
2597
|
case "joined-or-dead-lettered": {
|
|
2544
2598
|
const idx = flatNetIndexOf(flatNet, property.pending);
|
|
2545
2599
|
if (idx < 0) return false;
|
|
2546
|
-
return
|
|
2600
|
+
return isQuiescent(index, state) && state[idx] >= 1;
|
|
2547
2601
|
}
|
|
2548
2602
|
case "unreachable": {
|
|
2549
2603
|
let resolved = 0;
|
|
@@ -2914,13 +2968,38 @@ function encodeViolation(plan, lay, flat, property, sinkPlaces, envInj) {
|
|
|
2914
2968
|
return anyPlacePresent([property.p1, property.p2]);
|
|
2915
2969
|
case "unreachable":
|
|
2916
2970
|
return anyPlacePresent(property.places);
|
|
2917
|
-
|
|
2918
|
-
|
|
2971
|
+
// DeadlockFree (VER-002): quiescent AND some marked place is not a declared
|
|
2972
|
+
// sink. Mirrors the flat encoder's `stranded` disjunction.
|
|
2973
|
+
case "deadlock-free": {
|
|
2974
|
+
const conds = encodeColouredQuiescent(plan, lay, flat, envInj);
|
|
2975
|
+
if (conds == null) return "false";
|
|
2976
|
+
const sinks = new Set(indexOrdered(flat, sinkPlaces));
|
|
2977
|
+
const stranded = [];
|
|
2978
|
+
for (let pid = 0; pid < flat.places.length; pid++) {
|
|
2979
|
+
if (!sinks.has(pid)) stranded.push(`(>= ${aggregate(plan, lay, pid, lay.cur)} 1)`);
|
|
2980
|
+
}
|
|
2981
|
+
if (stranded.length === 0) return "false";
|
|
2982
|
+
conds.push(`(or ${stranded.join(" ")})`);
|
|
2983
|
+
return joinColoured(conds);
|
|
2984
|
+
}
|
|
2985
|
+
// TerminatesAtSink (VER-002): quiescent AND no declared sink marked.
|
|
2986
|
+
case "terminates-at-sink": {
|
|
2987
|
+
const conds = encodeColouredQuiescent(plan, lay, flat, envInj);
|
|
2988
|
+
if (conds == null) return "false";
|
|
2989
|
+
for (const pid of indexOrdered(flat, sinkPlaces)) {
|
|
2990
|
+
conds.push(`(= ${aggregate(plan, lay, pid, lay.cur)} 0)`);
|
|
2991
|
+
}
|
|
2992
|
+
return joinColoured(conds);
|
|
2993
|
+
}
|
|
2994
|
+
// JoinedOrDeadLettered (NU-040 AC4): quiescent AND `pending` marked, with NO
|
|
2995
|
+
// sink clause.
|
|
2919
2996
|
case "joined-or-dead-lettered": {
|
|
2920
2997
|
const pid = flat.placeIndex.get(property.pending.name);
|
|
2921
2998
|
if (pid == null) return null;
|
|
2922
|
-
const
|
|
2923
|
-
|
|
2999
|
+
const conds = encodeColouredQuiescent(plan, lay, flat, envInj);
|
|
3000
|
+
if (conds == null) return "false";
|
|
3001
|
+
conds.push(`(>= ${aggregate(plan, lay, pid, lay.cur)} 1)`);
|
|
3002
|
+
return joinColoured(conds);
|
|
2924
3003
|
}
|
|
2925
3004
|
}
|
|
2926
3005
|
}
|
|
@@ -2978,7 +3057,10 @@ function colouredDisabledTerm(cls, plan, lay) {
|
|
|
2978
3057
|
}
|
|
2979
3058
|
}
|
|
2980
3059
|
}
|
|
2981
|
-
function
|
|
3060
|
+
function joinColoured(conds) {
|
|
3061
|
+
return conds.length === 0 ? "true" : `(and ${conds.join(" ")})`;
|
|
3062
|
+
}
|
|
3063
|
+
function encodeColouredQuiescent(plan, lay, flat, envInj) {
|
|
2982
3064
|
const disabledConditions = [];
|
|
2983
3065
|
for (let ti = 0; ti < plan.classes.length; ti++) {
|
|
2984
3066
|
const cls = plan.classes[ti];
|
|
@@ -2991,13 +3073,10 @@ function encodeColouredDeadlock(plan, lay, flat, sinkPlaces, envInj) {
|
|
|
2991
3073
|
}
|
|
2992
3074
|
const term = colouredDisabledTerm(cls, plan, lay);
|
|
2993
3075
|
if (term != null) reasons.push(term);
|
|
2994
|
-
if (reasons.length === 0) return
|
|
3076
|
+
if (reasons.length === 0) return null;
|
|
2995
3077
|
disabledConditions.push(reasons.length === 1 ? reasons[0] : `(or ${reasons.join(" ")})`);
|
|
2996
3078
|
}
|
|
2997
|
-
|
|
2998
|
-
disabledConditions.push(`(= ${aggregate(plan, lay, pid, lay.cur)} 0)`);
|
|
2999
|
-
}
|
|
3000
|
-
return disabledConditions.length === 0 ? "true" : `(and ${disabledConditions.join(" ")})`;
|
|
3079
|
+
return disabledConditions;
|
|
3001
3080
|
}
|
|
3002
3081
|
|
|
3003
3082
|
// src/verification/analysis/name-fragment.ts
|
|
@@ -3484,8 +3563,16 @@ function decide(scg, property, sinkPlaces) {
|
|
|
3484
3563
|
const m = scg.markingOf(i);
|
|
3485
3564
|
return m.hasTokens(property.p1) && m.hasTokens(property.p2);
|
|
3486
3565
|
});
|
|
3566
|
+
// DeadlockFree (VER-002): a quiescent class that strands a token — some marked
|
|
3567
|
+
// place is not a declared sink. The empty marking strands nothing (AC4).
|
|
3487
3568
|
case "deadlock-free":
|
|
3488
3569
|
return firstWhere((i) => scg.successorsOf(i).length === 0 && !allTokensInSinks(scg.markingOf(i), sinkPlaces));
|
|
3570
|
+
// TerminatesAtSink (VER-002): a quiescent class that marks NO declared sink.
|
|
3571
|
+
// Inverts with DeadlockFree on the empty marking, by design.
|
|
3572
|
+
case "terminates-at-sink":
|
|
3573
|
+
return firstWhere((i) => scg.successorsOf(i).length === 0 && !anySinkMarked(scg.markingOf(i), sinkPlaces));
|
|
3574
|
+
// JoinedOrDeadLettered (NU-040 AC4): a quiescent class still holding a pending
|
|
3575
|
+
// token. No sink clause.
|
|
3489
3576
|
case "joined-or-dead-lettered":
|
|
3490
3577
|
return firstWhere((i) => scg.successorsOf(i).length === 0 && scg.markingOf(i).hasTokens(property.pending));
|
|
3491
3578
|
}
|
|
@@ -3498,6 +3585,14 @@ function allTokensInSinks(m, sinks) {
|
|
|
3498
3585
|
}
|
|
3499
3586
|
return true;
|
|
3500
3587
|
}
|
|
3588
|
+
function anySinkMarked(m, sinks) {
|
|
3589
|
+
const sinkNames = /* @__PURE__ */ new Set();
|
|
3590
|
+
for (const s of sinks) sinkNames.add(s.name);
|
|
3591
|
+
for (const p of m.placesWithTokens()) {
|
|
3592
|
+
if (sinkNames.has(p.name)) return true;
|
|
3593
|
+
}
|
|
3594
|
+
return false;
|
|
3595
|
+
}
|
|
3501
3596
|
function counterexamplePath(scg, target) {
|
|
3502
3597
|
const n = scg.classCount();
|
|
3503
3598
|
const parent = new Array(n).fill(-1);
|
|
@@ -4206,6 +4301,7 @@ function isReachabilitySafety(property) {
|
|
|
4206
4301
|
case "unreachable":
|
|
4207
4302
|
return true;
|
|
4208
4303
|
case "deadlock-free":
|
|
4304
|
+
case "terminates-at-sink":
|
|
4209
4305
|
case "joined-or-dead-lettered":
|
|
4210
4306
|
return false;
|
|
4211
4307
|
}
|
|
@@ -4282,6 +4378,8 @@ function unresolvedPropertyPlace(flatNet, property) {
|
|
|
4282
4378
|
switch (property.type) {
|
|
4283
4379
|
case "deadlock-free":
|
|
4284
4380
|
return [];
|
|
4381
|
+
case "terminates-at-sink":
|
|
4382
|
+
return [];
|
|
4285
4383
|
case "mutual-exclusion":
|
|
4286
4384
|
return [property.p1, property.p2];
|
|
4287
4385
|
case "place-bound":
|
|
@@ -4350,6 +4448,7 @@ export {
|
|
|
4350
4448
|
MarkingState,
|
|
4351
4449
|
MarkingStateBuilder,
|
|
4352
4450
|
deadlockFree,
|
|
4451
|
+
terminatesAtSink,
|
|
4353
4452
|
mutualExclusion,
|
|
4354
4453
|
placeBound,
|
|
4355
4454
|
unreachable,
|
|
@@ -4403,4 +4502,4 @@ export {
|
|
|
4403
4502
|
isProven,
|
|
4404
4503
|
isViolated
|
|
4405
4504
|
};
|
|
4406
|
-
//# sourceMappingURL=chunk-
|
|
4505
|
+
//# sourceMappingURL=chunk-75KEJQGC.js.map
|