libpetri 1.4.0 → 1.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/index.d.ts CHANGED
@@ -150,7 +150,9 @@ interface PetriNetExecutor {
150
150
  run(timeoutMs?: number): Promise<Marking>;
151
151
  /** Inject an external token. Returns true if accepted. */
152
152
  inject<T>(place: EnvironmentPlace<T>, token: Token<T>): Promise<boolean>;
153
- /** Shut down the executor. */
153
+ /** Graceful shutdown: reject new inject() calls, process queued events, terminate at quiescence. */
154
+ drain(): void;
155
+ /** Immediate shutdown: discard queued events, wait for in-flight, terminate. */
154
156
  close(): void;
155
157
  }
156
158
 
@@ -181,7 +183,6 @@ interface PetriNetExecutor {
181
183
  interface BitmapNetExecutorOptions {
182
184
  eventStore?: EventStore;
183
185
  environmentPlaces?: Set<EnvironmentPlace<any>>;
184
- longRunning?: boolean;
185
186
  /** Provides execution context data for each transition firing. */
186
187
  executionContextProvider?: (transitionName: string, consumed: Token<any>[]) => Map<string, unknown>;
187
188
  }
@@ -208,7 +209,7 @@ declare class BitmapNetExecutor implements PetriNetExecutor {
208
209
  private readonly marking;
209
210
  private readonly eventStore;
210
211
  private readonly environmentPlaces;
211
- private readonly longRunning;
212
+ private readonly hasEnvironmentPlaces;
212
213
  private readonly executionContextProvider?;
213
214
  private readonly startMs;
214
215
  private readonly hasAnyDeadlines;
@@ -236,6 +237,7 @@ declare class BitmapNetExecutor implements PetriNetExecutor {
236
237
  private readonly pendingResetPlaces;
237
238
  private readonly transitionInputPlaceNames;
238
239
  private running;
240
+ private draining;
239
241
  private closed;
240
242
  constructor(net: PetriNet, initialTokens: Map<Place<any>, Token<any>[]>, options?: BitmapNetExecutorOptions);
241
243
  run(timeoutMs?: number): Promise<Marking>;
@@ -300,6 +302,7 @@ declare class BitmapNetExecutor implements PetriNetExecutor {
300
302
  private snapshotMarking;
301
303
  isQuiescent(): boolean;
302
304
  executionId(): string;
305
+ drain(): void;
303
306
  close(): void;
304
307
  private emitEvent;
305
308
  }
@@ -412,7 +415,6 @@ declare class PrecompiledNet {
412
415
  interface PrecompiledNetExecutorOptions {
413
416
  eventStore?: EventStore;
414
417
  environmentPlaces?: Set<EnvironmentPlace<any>>;
415
- longRunning?: boolean;
416
418
  executionContextProvider?: (transitionName: string, consumed: Token<any>[]) => Map<string, unknown>;
417
419
  /** Skip output spec validation for trusted actions (CONC-026). */
418
420
  skipOutputValidation?: boolean;
@@ -429,7 +431,7 @@ declare class PrecompiledNetExecutor implements PetriNetExecutor {
429
431
  private readonly program;
430
432
  private readonly eventStore;
431
433
  private readonly environmentPlaces;
432
- private readonly longRunning;
434
+ private readonly hasEnvironmentPlaces;
433
435
  private readonly executionContextProvider?;
434
436
  private readonly skipOutputValidation;
435
437
  private readonly startMs;
@@ -462,6 +464,7 @@ declare class PrecompiledNetExecutor implements PetriNetExecutor {
462
464
  private readonly racePromises;
463
465
  private readonly readyBuffer;
464
466
  private running;
467
+ private draining;
465
468
  private closed;
466
469
  private marking;
467
470
  constructor(net: PetriNet, initialTokens: Map<Place<any>, Token<any>[]>, options?: PrecompiledNetExecutorOptions);
@@ -504,6 +507,7 @@ declare class PrecompiledNetExecutor implements PetriNetExecutor {
504
507
  private snapshotMarking;
505
508
  isQuiescent(): boolean;
506
509
  executionId(): string;
510
+ drain(): void;
507
511
  close(): void;
508
512
  private emitEvent;
509
513
  }
package/dist/index.js CHANGED
@@ -1178,7 +1178,7 @@ var BitmapNetExecutor = class {
1178
1178
  marking;
1179
1179
  eventStore;
1180
1180
  environmentPlaces;
1181
- longRunning;
1181
+ hasEnvironmentPlaces;
1182
1182
  executionContextProvider;
1183
1183
  startMs;
1184
1184
  hasAnyDeadlines;
@@ -1213,6 +1213,7 @@ var BitmapNetExecutor = class {
1213
1213
  pendingResetPlaces = /* @__PURE__ */ new Set();
1214
1214
  transitionInputPlaceNames;
1215
1215
  running = false;
1216
+ draining = false;
1216
1217
  closed = false;
1217
1218
  constructor(net, initialTokens, options = {}) {
1218
1219
  this.compiled = CompiledNet.compile(net);
@@ -1221,7 +1222,7 @@ var BitmapNetExecutor = class {
1221
1222
  this.environmentPlaces = new Set(
1222
1223
  [...options.environmentPlaces ?? []].map((ep) => ep.place.name)
1223
1224
  );
1224
- this.longRunning = options.longRunning ?? false;
1225
+ this.hasEnvironmentPlaces = this.environmentPlaces.size > 0;
1225
1226
  this.executionContextProvider = options.executionContextProvider;
1226
1227
  this.startMs = performance.now();
1227
1228
  const wordCount = this.compiled.wordCount;
@@ -1290,7 +1291,7 @@ var BitmapNetExecutor = class {
1290
1291
  timestamp: Date.now(),
1291
1292
  marking: this.snapshotMarking()
1292
1293
  });
1293
- while (this.running && !this.closed) {
1294
+ while (this.running) {
1294
1295
  this.processCompletedTransitions();
1295
1296
  this.processExternalEvents();
1296
1297
  this.updateDirtyTransitions();
@@ -1322,7 +1323,7 @@ var BitmapNetExecutor = class {
1322
1323
  if (!this.environmentPlaces.has(envPlace.place.name)) {
1323
1324
  throw new Error(`Place ${envPlace.place.name} is not registered as an environment place`);
1324
1325
  }
1325
- if (this.closed) return false;
1326
+ if (this.closed || this.draining) return false;
1326
1327
  return new Promise((resolve, reject) => {
1327
1328
  this.externalQueue.push({
1328
1329
  place: envPlace.place,
@@ -1358,7 +1359,12 @@ var BitmapNetExecutor = class {
1358
1359
  }
1359
1360
  }
1360
1361
  shouldTerminate() {
1361
- if (this.longRunning) return this.closed;
1362
+ if (this.closed) {
1363
+ return this.inFlight.size === 0 && this.completionQueue.length === 0;
1364
+ }
1365
+ if (this.hasEnvironmentPlaces) {
1366
+ return this.draining && this.enabledTransitionCount === 0 && this.inFlight.size === 0 && this.completionQueue.length === 0;
1367
+ }
1362
1368
  return this.enabledTransitionCount === 0 && this.inFlight.size === 0 && this.completionQueue.length === 0;
1363
1369
  }
1364
1370
  // ======================== Dirty Set Transitions ========================
@@ -1751,6 +1757,7 @@ var BitmapNetExecutor = class {
1751
1757
  // ======================== External Events ========================
1752
1758
  processExternalEvents() {
1753
1759
  if (this.externalQueue.length === 0) return;
1760
+ if (this.closed) return;
1754
1761
  const len = this.externalQueue.length;
1755
1762
  for (let i = 0; i < len; i++) {
1756
1763
  const event = this.externalQueue[i];
@@ -1790,9 +1797,10 @@ var BitmapNetExecutor = class {
1790
1797
  * After the yield, re-checks queues and `this.closed` for close-during-yield safety.
1791
1798
  */
1792
1799
  async awaitWork() {
1793
- if (this.completionQueue.length > 0 || this.externalQueue.length > 0) return;
1800
+ if (this.completionQueue.length > 0 || !this.closed && this.externalQueue.length > 0) return;
1794
1801
  await Promise.resolve();
1795
- if (this.completionQueue.length > 0 || this.externalQueue.length > 0 || this.closed) return;
1802
+ if (this.completionQueue.length > 0 || !this.closed && this.externalQueue.length > 0) return;
1803
+ if (this.closed && this.inFlight.size === 0) return;
1796
1804
  const promises = this.awaitPromises;
1797
1805
  promises.length = 0;
1798
1806
  if (this.inFlight.size > 0) {
@@ -1801,12 +1809,14 @@ var BitmapNetExecutor = class {
1801
1809
  for (const f of this.inFlight.values()) arr.push(f.promise);
1802
1810
  promises.push(Promise.race(arr));
1803
1811
  }
1804
- promises.push(new Promise((resolve) => {
1805
- this.wakeUpResolve = resolve;
1806
- }));
1807
- const timerMs = this.millisUntilNextTimedTransition();
1808
- if (timerMs > 0 && timerMs < Infinity) {
1809
- promises.push(new Promise((r) => setTimeout(r, timerMs)));
1812
+ if (!this.closed) {
1813
+ promises.push(new Promise((resolve) => {
1814
+ this.wakeUpResolve = resolve;
1815
+ }));
1816
+ const timerMs = this.millisUntilNextTimedTransition();
1817
+ if (timerMs > 0 && timerMs < Infinity) {
1818
+ promises.push(new Promise((r) => setTimeout(r, timerMs)));
1819
+ }
1810
1820
  }
1811
1821
  if (promises.length > 0) {
1812
1822
  await Promise.race(promises);
@@ -1876,8 +1886,12 @@ var BitmapNetExecutor = class {
1876
1886
  executionId() {
1877
1887
  return this.startMs.toString(16);
1878
1888
  }
1889
+ drain() {
1890
+ this.draining = true;
1891
+ this.wakeUp();
1892
+ }
1879
1893
  close() {
1880
- this.running = false;
1894
+ this.draining = true;
1881
1895
  this.closed = true;
1882
1896
  this.wakeUp();
1883
1897
  }
@@ -2235,7 +2249,7 @@ var PrecompiledNetExecutor = class {
2235
2249
  program;
2236
2250
  eventStore;
2237
2251
  environmentPlaces;
2238
- longRunning;
2252
+ hasEnvironmentPlaces;
2239
2253
  executionContextProvider;
2240
2254
  skipOutputValidation;
2241
2255
  startMs;
@@ -2278,6 +2292,7 @@ var PrecompiledNetExecutor = class {
2278
2292
  readyBuffer = [];
2279
2293
  // ==================== Lifecycle ====================
2280
2294
  running = false;
2295
+ draining = false;
2281
2296
  closed = false;
2282
2297
  // ==================== Lazy Marking ====================
2283
2298
  marking = null;
@@ -2287,7 +2302,7 @@ var PrecompiledNetExecutor = class {
2287
2302
  this.environmentPlaces = new Set(
2288
2303
  [...options.environmentPlaces ?? []].map((ep) => ep.place.name)
2289
2304
  );
2290
- this.longRunning = options.longRunning ?? false;
2305
+ this.hasEnvironmentPlaces = this.environmentPlaces.size > 0;
2291
2306
  this.executionContextProvider = options.executionContextProvider;
2292
2307
  this.skipOutputValidation = options.skipOutputValidation ?? false;
2293
2308
  this.startMs = performance.now();
@@ -2372,7 +2387,7 @@ var PrecompiledNetExecutor = class {
2372
2387
  timestamp: Date.now(),
2373
2388
  marking: this.snapshotMarking()
2374
2389
  });
2375
- while (this.running && !this.closed) {
2390
+ while (this.running) {
2376
2391
  this.processCompletedTransitions();
2377
2392
  this.processExternalEvents();
2378
2393
  this.updateDirtyTransitions();
@@ -2404,7 +2419,7 @@ var PrecompiledNetExecutor = class {
2404
2419
  if (!this.environmentPlaces.has(envPlace.place.name)) {
2405
2420
  throw new Error(`Place ${envPlace.place.name} is not registered as an environment place`);
2406
2421
  }
2407
- if (this.closed) return false;
2422
+ if (this.closed || this.draining) return false;
2408
2423
  return new Promise((resolve, reject) => {
2409
2424
  this.externalQueue.push({
2410
2425
  place: envPlace.place,
@@ -2438,7 +2453,12 @@ var PrecompiledNetExecutor = class {
2438
2453
  }
2439
2454
  }
2440
2455
  shouldTerminate() {
2441
- if (this.longRunning) return this.closed;
2456
+ if (this.closed) {
2457
+ return this.inFlightCount === 0 && this.completionQueue.length === 0;
2458
+ }
2459
+ if (this.hasEnvironmentPlaces) {
2460
+ return this.draining && this.enabledTransitionCount === 0 && this.inFlightCount === 0 && this.completionQueue.length === 0;
2461
+ }
2442
2462
  return this.enabledTransitionCount === 0 && this.inFlightCount === 0 && this.completionQueue.length === 0;
2443
2463
  }
2444
2464
  // ======================== Dirty Set Processing ========================
@@ -2960,6 +2980,7 @@ var PrecompiledNetExecutor = class {
2960
2980
  // ======================== External Events ========================
2961
2981
  processExternalEvents() {
2962
2982
  if (this.externalQueue.length === 0) return;
2983
+ if (this.closed) return;
2963
2984
  const prog = this.program;
2964
2985
  const len = this.externalQueue.length;
2965
2986
  for (let i = 0; i < len; i++) {
@@ -2989,9 +3010,10 @@ var PrecompiledNetExecutor = class {
2989
3010
  }
2990
3011
  // ======================== Await Work ========================
2991
3012
  async awaitWork() {
2992
- if (this.completionQueue.length > 0 || this.externalQueue.length > 0) return;
3013
+ if (this.completionQueue.length > 0 || !this.closed && this.externalQueue.length > 0) return;
2993
3014
  await Promise.resolve();
2994
- if (this.completionQueue.length > 0 || this.externalQueue.length > 0 || this.closed) return;
3015
+ if (this.completionQueue.length > 0 || !this.closed && this.externalQueue.length > 0) return;
3016
+ if (this.closed && this.inFlightCount === 0) return;
2995
3017
  const promises = this.awaitPromises;
2996
3018
  promises.length = 0;
2997
3019
  if (this.inFlightCount > 0) {
@@ -3006,12 +3028,14 @@ var PrecompiledNetExecutor = class {
3006
3028
  promises.push(Promise.race(arr));
3007
3029
  }
3008
3030
  }
3009
- promises.push(new Promise((resolve) => {
3010
- this.wakeUpResolve = resolve;
3011
- }));
3012
- const timerMs = this.millisUntilNextTimedTransition();
3013
- if (timerMs > 0 && timerMs < Infinity) {
3014
- promises.push(new Promise((r) => setTimeout(r, timerMs)));
3031
+ if (!this.closed) {
3032
+ promises.push(new Promise((resolve) => {
3033
+ this.wakeUpResolve = resolve;
3034
+ }));
3035
+ const timerMs = this.millisUntilNextTimedTransition();
3036
+ if (timerMs > 0 && timerMs < Infinity) {
3037
+ promises.push(new Promise((r) => setTimeout(r, timerMs)));
3038
+ }
3015
3039
  }
3016
3040
  if (promises.length > 0) {
3017
3041
  await Promise.race(promises);
@@ -3085,8 +3109,12 @@ var PrecompiledNetExecutor = class {
3085
3109
  executionId() {
3086
3110
  return this.startMs.toString(16);
3087
3111
  }
3112
+ drain() {
3113
+ this.draining = true;
3114
+ this.wakeUp();
3115
+ }
3088
3116
  close() {
3089
- this.running = false;
3117
+ this.draining = true;
3090
3118
  this.closed = true;
3091
3119
  this.wakeUp();
3092
3120
  }