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.
@@ -1,6 +1,6 @@
1
1
  import { Writable } from 'node:stream';
2
- import { P as PetriNet, b as Transition, T as Token } from '../petri-net-p0ONpSAO.js';
3
- import { E as EventStore, N as NetEvent } from '../event-store-B3ppVTpO.js';
2
+ import { P as PetriNet, b as Transition, T as Token } from '../petri-net-CH7UvjOW.js';
3
+ import { E as EventStore, N as NetEvent } from '../event-store-hlH3-bzJ.js';
4
4
 
5
5
  /**
6
6
  * Commands sent from debug UI client to server via WebSocket.
@@ -1,5 +1,5 @@
1
1
  import { Application } from 'typedoc';
2
- import { P as PetriNet, S as SubnetDef, o as Instance } from '../petri-net-p0ONpSAO.js';
2
+ import { P as PetriNet, S as SubnetDef, o as Instance } from '../petri-net-CH7UvjOW.js';
3
3
 
4
4
  /**
5
5
  * TypeDoc plugin for `@petrinet` and `@subnet` tags — auto-generates
@@ -6358,7 +6358,7 @@ textContent??"").replace(/^cluster_/,"");Mn.classList.toggle("is-hidden",!N.has(
6358
6358
  en",!bn.has(v))}if(!Q)for(let Mn of Array.from(P.querySelectorAll("g.node.petri-replica")))Mn.classList.add("is-hidden");let Sn=new Map;for(let Mn of Array.from(P.querySelectorAll("g.node"))){let v=Mn.getAttribute("data-id");v&&Sn.set(v,Mn)}for(let Mn of Array.from(P.querySelectorAll("g.edge"))){let v=Mn.getAttribute("data-src")??"",Vn=Mn.getAttribute("data-dst")??"",te=Sn.get(v)?.classList.contains(
6359
6359
  "is-hidden")??!1,pe=Sn.get(Vn)?.classList.contains("is-hidden")??!1;Mn.classList.toggle("is-hidden",te||pe)}}var Pfe=["--lpv-bg","--lpv-header-bg","--lpv-border","--lpv-text","--lpv-muted","--lpv-cluster-bg","--lpv-cluster-bg-collapsed","--lpv-cluster-stroke-width","--lpv-cluster-stroke-dash","--lpv-cluster-fill-tint","--lpv-dim-opacity","--lpv-active-filter-outline","--lpv-legend-bg","--lpv-chip-bg","--lpv-chip-active-bg","--lpv-sidebar-bg","--lpv-sidebar-border","--lpv-sidebar-text","--lpv-sidebar-mute\
6360
6360
  d","--lpv-sidebar-chip-bg","--lpv-sidebar-chip-hover-bg","--lpv-sidebar-chip-off-opacity","--lpv-shared-glyph-color","--lpv-replica-fill","--lpv-replica-stroke","--lpv-highlight-stroke","--lpv-highlight-glow","--lpv-neighbor-stroke","--lpv-faded-node-opacity","--lpv-faded-edge-opacity","--lpv-faded-cluster-opacity"];var zPe=/^\s*subgraph\s+cluster_[A-Za-z0-9_]+\s*\{\s*$/,KPe=/^\s*\}\s*$/;function Cfe(P){let O=P.split("\n"),N=[],Q=0;for(let bn of O){if(zPe.test(bn)){Q++;continue}if(Q>0){if(KPe.test(bn)){Q--;continue}(bn.includes("[")||bn.includes("->"))&&N.push(_fe(bn));continue}if(bn.includes("->")&&(bn.includes("ltail=")||bn.includes("lhead="))){N.push(_fe(bn));continue}N.push(bn)}return N.join("\n")}function _fe(P){
6361
- return P.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,"[]")}var Ofe="4.1.0";async function ale(P,O,N={}){let Q=N.previousHandle??null,bn=Q?new Set(Q.collapsedPrefixes):new Set,Sn=Q?.activeFilter??null,Mn=N.subnets??Q?.subnets??"show";Q&&Q.dispose();let v=await Promise.resolve().then(()=>(lle(),fle)),Vn=(N.layout??"elk")==="elk",te=Mn==="hide"?Cfe(P):P,pe={clusterLayout:N.clusterLayout,leafPacking:N.leafPacking},$e=Vn?await v.renderDotToSvgWithElkLayout(te,pe):await v.renderDotToSvg(
6361
+ return P.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,"[]")}var Ofe="5.0.0";async function ale(P,O,N={}){let Q=N.previousHandle??null,bn=Q?new Set(Q.collapsedPrefixes):new Set,Sn=Q?.activeFilter??null,Mn=N.subnets??Q?.subnets??"show";Q&&Q.dispose();let v=await Promise.resolve().then(()=>(lle(),fle)),Vn=(N.layout??"elk")==="elk",te=Mn==="hide"?Cfe(P):P,pe={clusterLayout:N.clusterLayout,leafPacking:N.leafPacking},$e=Vn?await v.renderDotToSvgWithElkLayout(te,pe):await v.renderDotToSvg(
6362
6362
  te);O.innerHTML="",O.appendChild($e),O.classList.add("libpetri-viewer");let ft=mfe($e,N.panzoom),gt=z0n($e);Ffe(gt);let $t=N.layout??"elk";$t==="elk"&&(xfe($e),vz($e));let Be=new Set,dt=null,Ie=null,_t=!1,We=null,Pn=null,Bt=!0;function It(){if(!N.chrome||We&&We.parentNode===O)return;We=document.createElement("div"),We.className="libpetri-viewer-chrome",We.style.position="absolute",We.style.inset="\
6363
6363
  0",We.style.pointerEvents="none";let Rt=document.createElement("div");Rt.className="diagram-controls",Rt.style.pointerEvents="auto";let or=document.createElement("button");or.type="button",or.className="diagram-btn btn-reset",or.title="Reset view",or.textContent="Reset",or.addEventListener("click",()=>Ir.fit());let ui=document.createElement("button");ui.type="button",ui.className="diagram-btn btn-\
6364
6364
  fullscreen",ui.title="Toggle fullscreen",ui.textContent="Fullscreen",ui.addEventListener("click",()=>Yr(O,ui));let wr=document.createElement("button");wr.type="button",wr.className="diagram-btn btn-subnets",wr.title=Mn==="show"?"Hide subnet groupings":"Show subnet groupings",wr.textContent=Mn==="show"?"Flat view":"Subnets view",wr.addEventListener("click",()=>{Ir.toggleSubnets()}),Rt.appendChild(wr),
@@ -1,4 +1,4 @@
1
- import { T as Token } from './petri-net-p0ONpSAO.js';
1
+ import { T as Token } from './petri-net-CH7UvjOW.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-p0ONpSAO.js';
1
+ import { P as PetriNet } from '../petri-net-CH7UvjOW.js';
2
2
 
3
3
  /**
4
4
  * Format-agnostic typed graph model.
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-p0ONpSAO.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, K as KeyFn, L as LogFn, M as MAX_DURATION_MS, r as MatchKey, s as MatchSpec, N as NameId, t as OutAnd, u as OutForwardInput, v as OutPlace, w as OutTimeout, x as OutXor, y as OutputEntry, z as PetriNetBuilder, B as Port, D as PortDirection, G as SubnetDefBuilder, H as SubnetInstance, J as Timing, Q as TimingDeadline, R as TimingDelayed, U as TimingExact, V as TimingImmediate, W as TimingWindow, X as TokenInput, Y as TokenOutput, Z as TransitionAction, _ as TransitionBuilder, $ as VerificationHarness, a0 as VerificationResult, a1 as all, a2 as allPlaces, a3 as and, a4 as andPlaces, a5 as arcPlace, a6 as atLeast, a7 as consumptionCount, a8 as deadline, a9 as delayed, aa as earliest, ab as enumerateBranches, ac as environmentPlace, ad as exact, ae as exactly, af as fork, ag as forwardInput, ah as hasDeadline, ai as immediate, aj as inhibitorArc, ak as inputArc, al as isPassthrough, am as isUnit, an as keyForPlace, ao as latest, ap as matchCorrelates, aq as matchKey, ar as matchSpec, as as nameId, at as one, au as outPlace, av as outputArc, aw as passthrough, ax as place, ay as produce, az as readArc, aA as requiredCount, aB as resetArc, aC as timeout, aD as timeoutPlace, aE as tokenAt, aF as tokenOf, aG as transform, aH as transformAsync, aI as transformFrom, aJ as unitToken, aK as window, aL as withTimeout, aM as xor, aN as xorPlaces } from './petri-net-p0ONpSAO.js';
3
- import { E as EventStore } from './event-store-B3ppVTpO.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-B3ppVTpO.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-CH7UvjOW.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, K as KeyFn, L as LogFn, M as MAX_DURATION_MS, r as MatchKey, s as MatchSpec, N as NameId, t as OutAnd, u as OutForwardInput, v as OutPlace, w as OutTimeout, x as OutXor, y as OutputEntry, z as PetriNetBuilder, B as Port, D as PortDirection, G as SubnetDefBuilder, H as SubnetInstance, J as Timing, Q as TimingDeadline, R as TimingDelayed, U as TimingExact, V as TimingImmediate, W as TimingWindow, X as TokenInput, Y as TokenOutput, Z as TransitionAction, _ as TransitionBuilder, $ as VerificationHarness, a0 as VerificationResult, a1 as all, a2 as allPlaces, a3 as and, a4 as andPlaces, a5 as arcPlace, a6 as atLeast, a7 as consumptionCount, a8 as deadline, a9 as delayed, aa as earliest, ab as enumerateBranches, ac as environmentPlace, ad as exact, ae as exactly, af as fork, ag as forwardInput, ah as hasDeadline, ai as immediate, aj as inhibitorArc, ak as inputArc, al as isPassthrough, am as isUnit, an as keyForPlace, ao as latest, ap as matchCorrelates, aq as matchKey, ar as matchSpec, as as nameId, at as one, au as outPlace, av as outputArc, aw as passthrough, ax as place, ay as produce, az as readArc, aA as requiredCount, aB as resetArc, aC as timeout, aD as timeoutPlace, aE as tokenAt, aF as tokenOf, aG as transform, aH as transformAsync, aI as transformFrom, aJ as unitToken, aK as window, aL as withTimeout, aM as xor, aN as xorPlaces } from './petri-net-CH7UvjOW.js';
3
+ import { E as EventStore } from './event-store-hlH3-bzJ.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-hlH3-bzJ.js';
5
5
 
6
6
  /**
7
7
  * Discriminated sum-type abstraction over Petri nets, distinguishing **closed**
@@ -192,12 +192,30 @@ declare function containsAll(snapshot: Uint32Array, mask: Uint32Array): boolean;
192
192
  /** Checks if any bit in mask is set in snapshot. */
193
193
  declare function intersects(snapshot: Uint32Array, mask: Uint32Array): boolean;
194
194
 
195
+ /**
196
+ * What to do with the orchestrator loop when `run(timeoutMs)` hits its timeout.
197
+ *
198
+ * Mirrors Java's `PetriNetExecutor.RunTimeoutPolicy`.
199
+ *
200
+ * - `'abandon'` — reject the returned promise and leave the loop running. The net
201
+ * keeps firing transitions and mutating its marking after the caller has given up.
202
+ * This is what `run(timeoutMs)` has always done, and it stays the default only for
203
+ * compatibility — it is rarely what you want.
204
+ * - `'close'` — reject the returned promise and `close()` the executor. Queued external
205
+ * events are discarded and in-flight actions are allowed to complete, per [ENV-013].
206
+ */
207
+ type RunTimeoutPolicy = 'abandon' | 'close';
195
208
  /**
196
209
  * Interface for Petri net executors.
197
210
  */
198
211
  interface PetriNetExecutor {
199
- /** Run the net until quiescence or timeout. */
200
- run(timeoutMs?: number): Promise<Marking>;
212
+ /**
213
+ * Run the net until quiescence or timeout.
214
+ *
215
+ * @param timeoutMs reject after this many milliseconds; omit to run to quiescence
216
+ * @param onTimeout what happens to the loop when the timeout fires (default `'abandon'`)
217
+ */
218
+ run(timeoutMs?: number, onTimeout?: RunTimeoutPolicy): Promise<Marking>;
201
219
  /** Inject an external token. Returns true if accepted. */
202
220
  inject<T>(place: EnvironmentPlace<T>, token: Token<T>): Promise<boolean>;
203
221
  /** Graceful shutdown: reject new inject() calls, process queued events, terminate at quiescence. */
@@ -327,7 +345,7 @@ declare class BitmapNetExecutor implements PetriNetExecutor {
327
345
  private initMatchCaches;
328
346
  /** Mirror a token added to correlated input `pid` into every fast-path matcher. */
329
347
  private cacheAddToken;
330
- run(timeoutMs?: number): Promise<Marking>;
348
+ run(timeoutMs?: number, onTimeout?: RunTimeoutPolicy): Promise<Marking>;
331
349
  private executeLoop;
332
350
  inject<T>(envPlace: EnvironmentPlace<T>, token: Token<T>): Promise<boolean>;
333
351
  /** Convenience: inject a raw value (creates token with current timestamp). */
@@ -646,7 +664,7 @@ declare class PrecompiledNetExecutor implements PetriNetExecutor {
646
664
  private markDirty;
647
665
  private setMarkingBit;
648
666
  private clearMarkingBit;
649
- run(timeoutMs?: number): Promise<Marking>;
667
+ run(timeoutMs?: number, onTimeout?: RunTimeoutPolicy): Promise<Marking>;
650
668
  private executeLoop;
651
669
  inject<T>(envPlace: EnvironmentPlace<T>, token: Token<T>): Promise<boolean>;
652
670
  injectValue<T>(envPlace: EnvironmentPlace<T>, value: T): Promise<boolean>;
@@ -756,12 +774,27 @@ declare class OutViolationError extends Error {
756
774
  */
757
775
 
758
776
  /**
759
- * Recursively validates that a transition's output satisfies its declared Out spec.
777
+ * [IO-015] output validation as an **exact-explanation search**.
778
+ *
779
+ * An *assignment* picks exactly one child at each `xor` it reaches; subtrees under
780
+ * an unselected child are never evaluated. Each assignment claims a set of places,
781
+ * and validation succeeds iff **exactly one** assignment's claim *equals* the
782
+ * produced set.
783
+ *
784
+ * Returns that claim on success and throws `OutViolationError` otherwise — whether
785
+ * nothing explains the write or two or more branches do.
786
+ *
787
+ * Equality — rather than "every obligation was satisfied" — is what makes a token
788
+ * written to a declared place outside the selected branch a violation instead of a
789
+ * silent deposit. It also removes the need for a subsumption tie-break:
790
+ * `xor(and(A,B,C), and(A,B))` with A, B, C produced has exactly one *exact* claim.
760
791
  *
761
- * @returns the set of claimed place names, or null if not satisfied
762
- * @throws OutViolationError if a structural violation is detected (e.g. XOR with 0 or 2+ branches)
792
+ * Being a search rather than an eager walk is what makes `and` genuinely unordered
793
+ * ([IO-015] AC8): the old version short-circuited on its first unsatisfied child and
794
+ * let an inner `xor` throw before an enclosing one could try a sibling, so the same
795
+ * write set could pass or fail depending on declaration order.
763
796
  */
764
- declare function validateOutSpec(tName: string, spec: Out, producedPlaceNames: Set<string>): Set<string> | null;
797
+ declare function validateOutSpec(tName: string, spec: Out, producedPlaceNames: Set<string>): Set<string>;
765
798
  /**
766
799
  * Produces default tokens to the timeout branch's output places when an action
767
800
  * exceeds its timeout. Walks the Out spec tree recursively:
@@ -780,4 +813,4 @@ declare function validateOutSpec(tName: string, spec: Out, producedPlaceNames: S
780
813
  */
781
814
  declare function produceTimeoutOutput(context: TransitionContext, timeoutChild: Out): void;
782
815
 
783
- export { BitmapNetExecutor, type BitmapNetExecutorOptions, type CardinalityCheck, CompiledNet, EnvironmentPlace, EventStore, Marking, Out, OutViolationError, PetriNet, type PetriNetExecutor, Place, PrecompiledNet, PrecompiledNetExecutor, type PrecompiledNetExecutorOptions, type PredicateSpec, type Subnet, SubnetDef, Token, Transition, TransitionContext, clearBit, closedSubnet, containsAll, intersects, openSubnet, produceTimeoutOutput, setBit, testBit, validateOutSpec };
816
+ export { BitmapNetExecutor, type BitmapNetExecutorOptions, type CardinalityCheck, CompiledNet, EnvironmentPlace, EventStore, Marking, Out, OutViolationError, PetriNet, type PetriNetExecutor, Place, PrecompiledNet, PrecompiledNetExecutor, type PrecompiledNetExecutorOptions, type PredicateSpec, type RunTimeoutPolicy, type Subnet, SubnetDef, Token, Transition, TransitionContext, clearBit, closedSubnet, containsAll, intersects, openSubnet, produceTimeoutOutput, setBit, testBit, validateOutSpec };
package/dist/index.js CHANGED
@@ -28,7 +28,7 @@ import {
28
28
  withTimeout,
29
29
  xor,
30
30
  xorPlaces
31
- } from "./chunk-FD3S4TZM.js";
31
+ } from "./chunk-75KEJQGC.js";
32
32
  import {
33
33
  eventTransitionName,
34
34
  isFailureEvent
@@ -3072,51 +3072,104 @@ function swallowEventStoreFailure(when, err) {
3072
3072
  }
3073
3073
  var DEADLINE_TOLERANCE_MS = 5;
3074
3074
  function validateOutSpec(tName, spec, producedPlaceNames) {
3075
+ const named = specNames(spec);
3076
+ let relevant = 0;
3077
+ for (const name of producedPlaceNames) {
3078
+ if (named.has(name)) relevant++;
3079
+ }
3080
+ const exact2 = [];
3081
+ for (const claim of claimsWithin(spec, producedPlaceNames)) {
3082
+ if (claim.size === relevant) {
3083
+ exact2.push(claim);
3084
+ if (exact2.length > 1) break;
3085
+ }
3086
+ }
3087
+ const wrote = [...producedPlaceNames].filter((n) => named.has(n)).sort();
3088
+ if (exact2.length === 0) {
3089
+ throw new OutViolationError(
3090
+ `'${tName}': output does not match the declared spec - produced {${wrote.join(", ")}}, which no single branch of the spec claims exactly`
3091
+ );
3092
+ }
3093
+ if (exact2.length > 1) {
3094
+ throw new OutViolationError(
3095
+ `'${tName}': ambiguous output - {${wrote.join(", ")}} is claimed by more than one branch`
3096
+ );
3097
+ }
3098
+ return exact2[0];
3099
+ }
3100
+ function capMultiplicity(claims) {
3101
+ if (claims.length < 3) return claims;
3102
+ const seen = /* @__PURE__ */ new Map();
3103
+ const out = [];
3104
+ for (const claim of claims) {
3105
+ const key = [...claim].sort().join("\0");
3106
+ const n = seen.get(key) ?? 0;
3107
+ if (n >= 2) continue;
3108
+ seen.set(key, n + 1);
3109
+ out.push(claim);
3110
+ }
3111
+ return out;
3112
+ }
3113
+ var specNamesCache = /* @__PURE__ */ new WeakMap();
3114
+ function specNames(spec) {
3115
+ const hit = specNamesCache.get(spec);
3116
+ if (hit !== void 0) return hit;
3117
+ const names = /* @__PURE__ */ new Set();
3118
+ const walk = (node) => {
3119
+ switch (node.type) {
3120
+ case "place":
3121
+ names.add(node.place.name);
3122
+ break;
3123
+ case "forward-input":
3124
+ names.add(node.to.name);
3125
+ break;
3126
+ case "timeout":
3127
+ walk(node.child);
3128
+ break;
3129
+ case "xor":
3130
+ case "and":
3131
+ for (const c of node.children) walk(c);
3132
+ break;
3133
+ }
3134
+ };
3135
+ walk(spec);
3136
+ specNamesCache.set(spec, names);
3137
+ return names;
3138
+ }
3139
+ function claimsWithin(spec, produced) {
3075
3140
  switch (spec.type) {
3076
3141
  case "place":
3077
- return producedPlaceNames.has(spec.place.name) ? /* @__PURE__ */ new Set([spec.place.name]) : null;
3142
+ return produced.has(spec.place.name) ? [/* @__PURE__ */ new Set([spec.place.name])] : [];
3078
3143
  case "forward-input":
3079
- return producedPlaceNames.has(spec.to.name) ? /* @__PURE__ */ new Set([spec.to.name]) : null;
3080
- case "and": {
3081
- const claimed = /* @__PURE__ */ new Set();
3144
+ return produced.has(spec.to.name) ? [/* @__PURE__ */ new Set([spec.to.name])] : [];
3145
+ case "timeout":
3146
+ return claimsWithin(spec.child, produced);
3147
+ case "xor": {
3148
+ const out = [];
3082
3149
  for (const child of spec.children) {
3083
- const result = validateOutSpec(tName, child, producedPlaceNames);
3084
- if (result === null) return null;
3085
- for (const p of result) claimed.add(p);
3150
+ for (const claim of claimsWithin(child, produced)) out.push(claim);
3086
3151
  }
3087
- return claimed;
3152
+ return capMultiplicity(out);
3088
3153
  }
3089
- case "xor": {
3090
- const satisfied = [];
3154
+ case "and": {
3155
+ let acc = [/* @__PURE__ */ new Set()];
3091
3156
  for (const child of spec.children) {
3092
- const result = validateOutSpec(tName, child, producedPlaceNames);
3093
- if (result !== null) satisfied.push(result);
3094
- }
3095
- if (satisfied.length === 0) {
3096
- throw new OutViolationError(
3097
- `'${tName}': XOR violation - no branch produced (exactly 1 required)`
3098
- );
3099
- }
3100
- if (satisfied.length > 1) {
3101
- return resolveXorAmbiguity(tName, satisfied);
3157
+ const childClaims = claimsWithin(child, produced);
3158
+ if (childClaims.length === 0) return [];
3159
+ const next = [];
3160
+ for (const partial of acc) {
3161
+ for (const claim of childClaims) {
3162
+ const merged = new Set(partial);
3163
+ for (const name of claim) merged.add(name);
3164
+ next.push(merged);
3165
+ }
3166
+ }
3167
+ acc = capMultiplicity(next);
3102
3168
  }
3103
- return satisfied[0];
3169
+ return acc;
3104
3170
  }
3105
- case "timeout":
3106
- return validateOutSpec(tName, spec.child, producedPlaceNames);
3107
3171
  }
3108
3172
  }
3109
- function resolveXorAmbiguity(tName, satisfied) {
3110
- const subsuming = satisfied.filter(
3111
- (candidate) => satisfied.every(
3112
- (other) => other === candidate || [...other].every((p) => candidate.has(p))
3113
- )
3114
- );
3115
- if (subsuming.length === 1) return subsuming[0];
3116
- throw new OutViolationError(
3117
- `'${tName}': XOR violation - multiple branches produced`
3118
- );
3119
- }
3120
3173
  function produceTimeoutOutput(context, timeoutChild) {
3121
3174
  switch (timeoutChild.type) {
3122
3175
  case "place":
@@ -3342,19 +3395,27 @@ var BitmapNetExecutor = class {
3342
3395
  }
3343
3396
  }
3344
3397
  // ======================== Execution ========================
3345
- async run(timeoutMs) {
3346
- if (timeoutMs !== void 0) {
3347
- let timer;
3348
- const timeoutPromise = new Promise((_, reject) => {
3349
- timer = setTimeout(() => reject(new Error("Execution timed out")), timeoutMs);
3350
- });
3351
- try {
3352
- return await Promise.race([this.executeLoop(), timeoutPromise]);
3353
- } finally {
3354
- if (timer !== void 0) clearTimeout(timer);
3398
+ async run(timeoutMs, onTimeout = "abandon") {
3399
+ if (timeoutMs === void 0) return this.executeLoop();
3400
+ let timer;
3401
+ let timedOut = false;
3402
+ const timeoutPromise = new Promise((_, reject) => {
3403
+ timer = setTimeout(() => {
3404
+ timedOut = true;
3405
+ reject(new Error("Execution timed out"));
3406
+ }, timeoutMs);
3407
+ });
3408
+ const loop = this.executeLoop();
3409
+ try {
3410
+ return await Promise.race([loop, timeoutPromise]);
3411
+ } finally {
3412
+ if (timer !== void 0) clearTimeout(timer);
3413
+ if (timedOut) {
3414
+ if (onTimeout === "close") this.close();
3415
+ loop.catch(() => {
3416
+ });
3355
3417
  }
3356
3418
  }
3357
- return this.executeLoop();
3358
3419
  }
3359
3420
  async executeLoop() {
3360
3421
  this.running = true;
@@ -3852,13 +3913,7 @@ var BitmapNetExecutor = class {
3852
3913
  try {
3853
3914
  const outputs = flight.context.rawOutput();
3854
3915
  if (t.outputSpec !== null) {
3855
- const produced2 = outputs.placesWithTokens();
3856
- const result = validateOutSpec(t.name, t.outputSpec, produced2);
3857
- if (result === null) {
3858
- throw new OutViolationError(
3859
- `'${t.name}': output does not satisfy declared spec`
3860
- );
3861
- }
3916
+ validateOutSpec(t.name, t.outputSpec, outputs.placesWithTokens());
3862
3917
  }
3863
3918
  const produced = [];
3864
3919
  for (const entry of outputs.entries()) {
@@ -4342,13 +4397,13 @@ var PrecompiledNet = class _PrecompiledNet {
4342
4397
  if (needsIdx === SPARSE_EMPTY) {
4343
4398
  } else if (needsIdx >= 0) {
4344
4399
  const m = this.needsSingleWordMask[tid];
4345
- if ((snapshot[needsIdx] & m) !== m) return false;
4400
+ if ((snapshot[needsIdx] & m) >>> 0 !== m) return false;
4346
4401
  } else {
4347
4402
  const indices = this.needsSparseIndices[tid];
4348
4403
  const masks = this.needsSparseMasks[tid];
4349
4404
  for (let i = 0; i < indices.length; i++) {
4350
4405
  const m = masks[i];
4351
- if ((snapshot[indices[i]] & m) !== m) return false;
4406
+ if ((snapshot[indices[i]] & m) >>> 0 !== m) return false;
4352
4407
  }
4353
4408
  }
4354
4409
  const inhIdx = this.inhibitorSingleWordIndex[tid];
@@ -4676,19 +4731,27 @@ var PrecompiledNetExecutor = class {
4676
4731
  this.markingBitmap[pid >>> WORD_SHIFT] &= ~(1 << (pid & BIT_MASK));
4677
4732
  }
4678
4733
  // ======================== Execution ========================
4679
- async run(timeoutMs) {
4680
- if (timeoutMs !== void 0) {
4681
- let timer;
4682
- const timeoutPromise = new Promise((_, reject) => {
4683
- timer = setTimeout(() => reject(new Error("Execution timed out")), timeoutMs);
4684
- });
4685
- try {
4686
- return await Promise.race([this.executeLoop(), timeoutPromise]);
4687
- } finally {
4688
- if (timer !== void 0) clearTimeout(timer);
4734
+ async run(timeoutMs, onTimeout = "abandon") {
4735
+ if (timeoutMs === void 0) return this.executeLoop();
4736
+ let timer;
4737
+ let timedOut = false;
4738
+ const timeoutPromise = new Promise((_, reject) => {
4739
+ timer = setTimeout(() => {
4740
+ timedOut = true;
4741
+ reject(new Error("Execution timed out"));
4742
+ }, timeoutMs);
4743
+ });
4744
+ const loop = this.executeLoop();
4745
+ try {
4746
+ return await Promise.race([loop, timeoutPromise]);
4747
+ } finally {
4748
+ if (timer !== void 0) clearTimeout(timer);
4749
+ if (timedOut) {
4750
+ if (onTimeout === "close") this.close();
4751
+ loop.catch(() => {
4752
+ });
4689
4753
  }
4690
4754
  }
4691
- return this.executeLoop();
4692
4755
  }
4693
4756
  async executeLoop() {
4694
4757
  this.running = true;
@@ -5332,17 +5395,11 @@ var PrecompiledNetExecutor = class {
5332
5395
  const produced2 = outputs.placesWithTokens();
5333
5396
  if (!produced2.has(prog.places[simplePid].name)) {
5334
5397
  throw new OutViolationError(
5335
- `'${t.name}': output does not satisfy declared spec`
5398
+ `'${t.name}': output does not match the declared spec - produced {}, which no single branch of the spec claims exactly`
5336
5399
  );
5337
5400
  }
5338
5401
  } else if (simplePid === -1) {
5339
- const produced2 = outputs.placesWithTokens();
5340
- const result = validateOutSpec(t.name, t.outputSpec, produced2);
5341
- if (result === null) {
5342
- throw new OutViolationError(
5343
- `'${t.name}': output does not satisfy declared spec`
5344
- );
5345
- }
5402
+ validateOutSpec(t.name, t.outputSpec, outputs.placesWithTokens());
5346
5403
  }
5347
5404
  }
5348
5405
  const produced = [];