libpetri 0.5.3 → 0.6.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
+ import { Writable } from 'node:stream';
1
2
  import { a as PetriNet, b as Transition, T as Token } from '../petri-net-C3Jy5HCt.js';
2
3
  import { E as EventStore, N as NetEvent } from '../event-store-Y8q_wapJ.js';
3
- import { Writable } from 'node:stream';
4
4
 
5
5
  /**
6
6
  * Commands sent from debug UI client to server via WebSocket.
@@ -66,6 +66,17 @@ type DebugCommand = {
66
66
  } | {
67
67
  readonly type: 'listBreakpoints';
68
68
  readonly sessionId: string;
69
+ } | {
70
+ readonly type: 'listArchives';
71
+ readonly limit?: number;
72
+ readonly prefix?: string;
73
+ } | {
74
+ readonly type: 'importArchive';
75
+ readonly sessionId: string;
76
+ } | {
77
+ readonly type: 'uploadArchive';
78
+ readonly fileName: string;
79
+ readonly data: string;
69
80
  };
70
81
  declare function eventFilterAll(): EventFilter;
71
82
 
@@ -110,6 +121,12 @@ interface NetStructure {
110
121
  readonly places: readonly PlaceInfo[];
111
122
  readonly transitions: readonly TransitionInfo[];
112
123
  }
124
+ interface ArchiveSummary {
125
+ readonly sessionId: string;
126
+ readonly key: string;
127
+ readonly sizeBytes: number;
128
+ readonly lastModified: string;
129
+ }
113
130
  type DebugResponse = {
114
131
  readonly type: 'sessionList';
115
132
  readonly sessions: readonly SessionSummary[];
@@ -177,8 +194,35 @@ type DebugResponse = {
177
194
  readonly code: string;
178
195
  readonly message: string;
179
196
  readonly sessionId: string | null;
197
+ } | {
198
+ readonly type: 'archiveList';
199
+ readonly archives: readonly ArchiveSummary[];
200
+ readonly storageAvailable: boolean;
201
+ } | {
202
+ readonly type: 'archiveImported';
203
+ readonly sessionId: string;
204
+ readonly netName: string;
205
+ readonly eventCount: number;
180
206
  };
181
207
 
208
+ /**
209
+ * Storage backend interface for session archives.
210
+ */
211
+
212
+ interface ArchivedSessionSummary {
213
+ readonly sessionId: string;
214
+ readonly key: string;
215
+ readonly sizeBytes: number;
216
+ readonly lastModified: number;
217
+ }
218
+ type OutputStreamConsumer = (out: Writable) => Promise<void>;
219
+ interface SessionArchiveStorage {
220
+ storeStreaming(sessionId: string, writer: OutputStreamConsumer): Promise<void>;
221
+ list(limit: number, prefix?: string): Promise<readonly ArchivedSessionSummary[]>;
222
+ retrieve(sessionId: string): Promise<Buffer>;
223
+ isAvailable(): boolean;
224
+ }
225
+
182
226
  /**
183
227
  * Analyze places for start/end/environment classification.
184
228
  * TypeScript port of Java's PlaceAnalysis.
@@ -331,8 +375,9 @@ interface ComputedState {
331
375
  }
332
376
  declare class DebugProtocolHandler {
333
377
  private readonly _sessionRegistry;
378
+ private readonly _archiveStorage;
334
379
  private readonly _clients;
335
- constructor(sessionRegistry: DebugSessionRegistry);
380
+ constructor(sessionRegistry: DebugSessionRegistry, archiveStorage?: SessionArchiveStorage | null);
336
381
  /** Registers a new client connection. */
337
382
  clientConnected(clientId: string, sink: ResponseSink): void;
338
383
  /** Cleans up when a client disconnects. */
@@ -354,6 +399,10 @@ declare class DebugProtocolHandler {
354
399
  private handleSetBreakpoint;
355
400
  private handleClearBreakpoint;
356
401
  private handleListBreakpoints;
402
+ private handleListArchives;
403
+ private handleImportArchive;
404
+ private handleUploadArchive;
405
+ private broadcastSessionList;
357
406
  private send;
358
407
  private sendError;
359
408
  private sendInBatches;
@@ -430,24 +479,6 @@ interface SessionArchive {
430
479
  /** Current archive format version. */
431
480
  declare const CURRENT_VERSION = 1;
432
481
 
433
- /**
434
- * Storage backend interface for session archives.
435
- */
436
-
437
- interface ArchivedSessionSummary {
438
- readonly sessionId: string;
439
- readonly key: string;
440
- readonly sizeBytes: number;
441
- readonly lastModified: number;
442
- }
443
- type OutputStreamConsumer = (out: Writable) => Promise<void>;
444
- interface SessionArchiveStorage {
445
- storeStreaming(sessionId: string, writer: OutputStreamConsumer): Promise<void>;
446
- list(limit: number, prefix?: string): Promise<readonly ArchivedSessionSummary[]>;
447
- retrieve(sessionId: string): Promise<Buffer>;
448
- isAvailable(): boolean;
449
- }
450
-
451
482
  /**
452
483
  * File-system backed storage for session archives.
453
484
  * Uses Node.js fs/promises — no external dependencies.
@@ -510,4 +541,4 @@ declare class SessionArchiveReader {
510
541
  */
511
542
  declare function debugUiAssetPath(): Promise<string>;
512
543
 
513
- export { type ArchivedSessionSummary, type BreakpointConfig, type BreakpointType, CURRENT_VERSION, type ComputedState, DEFAULT_MAX_EVENTS, DebugAwareEventStore, type DebugCommand, DebugEventStore, DebugProtocolHandler, type DebugResponse, type DebugSession, DebugSessionRegistry, type EventFilter, type EventStoreFactory, FileSessionArchiveStorage, type ImportedSession, MarkingCache, type NetEventInfo, type NetStructure, type OutputStreamConsumer, PlaceAnalysis, type PlaceAnalysisInfo, type PlaceInfo, type ResponseSink, SNAPSHOT_INTERVAL, type SessionArchive, SessionArchiveReader, type SessionArchiveStorage, SessionArchiveWriter, type SessionCompletionListener, type SessionSummary, type Subscription, type SubscriptionMode, type TokenInfo, type TransitionInfo, buildNetStructure, compactTokenInfo, convertMarking, debugUiAssetPath, eventFilterAll, toEventInfo, tokenInfo };
544
+ export { type ArchiveSummary, type ArchivedSessionSummary, type BreakpointConfig, type BreakpointType, CURRENT_VERSION, type ComputedState, DEFAULT_MAX_EVENTS, DebugAwareEventStore, type DebugCommand, DebugEventStore, DebugProtocolHandler, type DebugResponse, type DebugSession, DebugSessionRegistry, type EventFilter, type EventStoreFactory, FileSessionArchiveStorage, type ImportedSession, MarkingCache, type NetEventInfo, type NetStructure, type OutputStreamConsumer, PlaceAnalysis, type PlaceAnalysisInfo, type PlaceInfo, type ResponseSink, SNAPSHOT_INTERVAL, type SessionArchive, SessionArchiveReader, type SessionArchiveStorage, SessionArchiveWriter, type SessionCompletionListener, type SessionSummary, type Subscription, type SubscriptionMode, type TokenInfo, type TransitionInfo, buildNetStructure, compactTokenInfo, convertMarking, debugUiAssetPath, eventFilterAll, toEventInfo, tokenInfo };
@@ -9,76 +9,8 @@ function eventFilterAll() {
9
9
  return { eventTypes: null, transitionNames: null, placeNames: null };
10
10
  }
11
11
 
12
- // src/debug/place-analysis.ts
13
- var PlaceAnalysis = class _PlaceAnalysis {
14
- _data;
15
- constructor(data) {
16
- this._data = data;
17
- }
18
- get data() {
19
- return this._data;
20
- }
21
- isStart(placeName) {
22
- const info = this._data.get(placeName);
23
- return info != null && !info.hasIncoming;
24
- }
25
- isEnd(placeName) {
26
- const info = this._data.get(placeName);
27
- return info != null && !info.hasOutgoing;
28
- }
29
- /** Build place analysis from a PetriNet. */
30
- static from(net) {
31
- const data = /* @__PURE__ */ new Map();
32
- function ensure(place) {
33
- let info = data.get(place.name);
34
- if (!info) {
35
- info = { tokenType: "unknown", hasIncoming: false, hasOutgoing: false };
36
- data.set(place.name, info);
37
- }
38
- return info;
39
- }
40
- for (const transition of net.transitions) {
41
- for (const input of transition.inputSpecs) {
42
- const info = ensure(input.place);
43
- info.hasOutgoing = true;
44
- }
45
- if (transition.outputSpec) {
46
- const outputPlaces = collectOutputPlaces(transition.outputSpec);
47
- for (const place of outputPlaces) {
48
- const info = ensure(place);
49
- info.hasIncoming = true;
50
- }
51
- }
52
- for (const inh of transition.inhibitors) {
53
- ensure(inh.place);
54
- }
55
- for (const read of transition.reads) {
56
- const info = ensure(read.place);
57
- info.hasOutgoing = true;
58
- }
59
- for (const reset of transition.resets) {
60
- ensure(reset.place);
61
- }
62
- }
63
- return new _PlaceAnalysis(data);
64
- }
65
- };
66
- function collectOutputPlaces(out) {
67
- const spec = out;
68
- switch (spec.type) {
69
- case "place":
70
- return spec.place ? [spec.place] : [];
71
- case "and":
72
- case "xor":
73
- return (spec.children ?? []).flatMap((c) => collectOutputPlaces(c));
74
- case "timeout":
75
- return spec.child ? collectOutputPlaces(spec.child) : [];
76
- case "forward-input":
77
- return spec.to ? [spec.to] : [];
78
- default:
79
- return [];
80
- }
81
- }
12
+ // src/debug/archive/session-archive-reader.ts
13
+ import { gunzipSync } from "zlib";
82
14
 
83
15
  // src/debug/debug-event-store.ts
84
16
  var DEFAULT_MAX_EVENTS = 1e4;
@@ -185,6 +117,188 @@ var DebugEventStore = class {
185
117
  }
186
118
  };
187
119
 
120
+ // src/debug/archive/session-archive.ts
121
+ var CURRENT_VERSION = 1;
122
+
123
+ // src/debug/archive/session-archive-reader.ts
124
+ var MAX_EVENT_SIZE = 10 * 1024 * 1024;
125
+ var SessionArchiveReader = class {
126
+ /** Reads only the metadata header from an archive. */
127
+ readMetadata(compressed) {
128
+ const data = gunzipSync(compressed);
129
+ const metaLen = data.readUInt32BE(0);
130
+ const metaJson = data.subarray(4, 4 + metaLen).toString("utf-8");
131
+ const metadata = JSON.parse(metaJson);
132
+ if (metadata.version !== CURRENT_VERSION) {
133
+ throw new Error(`Unsupported archive version: ${metadata.version} (expected ${CURRENT_VERSION})`);
134
+ }
135
+ return metadata;
136
+ }
137
+ /** Reads the full archive: metadata + all events into a DebugEventStore. */
138
+ readFull(compressed) {
139
+ const data = gunzipSync(compressed);
140
+ let offset = 0;
141
+ const metaLen = data.readUInt32BE(offset);
142
+ offset += 4;
143
+ const metaJson = data.subarray(offset, offset + metaLen).toString("utf-8");
144
+ offset += metaLen;
145
+ const metadata = JSON.parse(metaJson);
146
+ if (metadata.version !== CURRENT_VERSION) {
147
+ throw new Error(`Unsupported archive version: ${metadata.version} (expected ${CURRENT_VERSION})`);
148
+ }
149
+ const eventStore = new DebugEventStore(metadata.sessionId, Number.MAX_SAFE_INTEGER);
150
+ while (offset < data.length) {
151
+ if (offset + 4 > data.length) break;
152
+ const eventLen = data.readUInt32BE(offset);
153
+ offset += 4;
154
+ if (eventLen <= 0 || eventLen > MAX_EVENT_SIZE) {
155
+ throw new Error(`Invalid event size: ${eventLen}`);
156
+ }
157
+ if (offset + eventLen > data.length) break;
158
+ const eventJson = data.subarray(offset, offset + eventLen).toString("utf-8");
159
+ offset += eventLen;
160
+ const eventInfo = JSON.parse(eventJson);
161
+ const netEvent = eventInfoToNetEvent(eventInfo);
162
+ eventStore.append(netEvent);
163
+ }
164
+ return { metadata, eventStore };
165
+ }
166
+ };
167
+ function eventInfoToNetEvent(info) {
168
+ const timestamp = new Date(info.timestamp).getTime();
169
+ const d = info.details;
170
+ switch (info.type) {
171
+ case "ExecutionStarted":
172
+ return { type: "execution-started", timestamp, netName: d["netName"], executionId: d["executionId"] };
173
+ case "ExecutionCompleted":
174
+ return { type: "execution-completed", timestamp, netName: d["netName"], executionId: d["executionId"], totalDurationMs: d["totalDurationMs"] };
175
+ case "TransitionEnabled":
176
+ return { type: "transition-enabled", timestamp, transitionName: info.transitionName };
177
+ case "TransitionClockRestarted":
178
+ return { type: "transition-clock-restarted", timestamp, transitionName: info.transitionName };
179
+ case "TransitionStarted": {
180
+ const tokens = d["consumedTokens"].map((t) => infoToToken(t));
181
+ return { type: "transition-started", timestamp, transitionName: info.transitionName, consumedTokens: tokens };
182
+ }
183
+ case "TransitionCompleted": {
184
+ const tokens = d["producedTokens"].map((t) => infoToToken(t));
185
+ return { type: "transition-completed", timestamp, transitionName: info.transitionName, producedTokens: tokens, durationMs: d["durationMs"] };
186
+ }
187
+ case "TransitionFailed":
188
+ return { type: "transition-failed", timestamp, transitionName: info.transitionName, errorMessage: d["errorMessage"], exceptionType: d["exceptionType"] };
189
+ case "TransitionTimedOut":
190
+ return { type: "transition-timed-out", timestamp, transitionName: info.transitionName, deadlineMs: d["deadlineMs"], actualDurationMs: d["actualDurationMs"] };
191
+ case "ActionTimedOut":
192
+ return { type: "action-timed-out", timestamp, transitionName: info.transitionName, timeoutMs: d["timeoutMs"] };
193
+ case "TokenAdded": {
194
+ const t = d["token"];
195
+ return { type: "token-added", timestamp, placeName: info.placeName, token: infoToToken(t) };
196
+ }
197
+ case "TokenRemoved": {
198
+ const t = d["token"];
199
+ return { type: "token-removed", timestamp, placeName: info.placeName, token: infoToToken(t) };
200
+ }
201
+ case "MarkingSnapshot": {
202
+ const markingData = d["marking"];
203
+ const marking = /* @__PURE__ */ new Map();
204
+ for (const [place, tokens] of Object.entries(markingData)) {
205
+ marking.set(place, tokens.map((t) => infoToToken(t)));
206
+ }
207
+ return { type: "marking-snapshot", timestamp, marking };
208
+ }
209
+ case "LogMessage":
210
+ return {
211
+ type: "log-message",
212
+ timestamp,
213
+ transitionName: info.transitionName,
214
+ logger: d["loggerName"],
215
+ level: d["level"],
216
+ message: d["message"],
217
+ error: d["throwable"] ?? null,
218
+ errorMessage: d["throwableMessage"] ?? null
219
+ };
220
+ default:
221
+ return { type: "transition-enabled", timestamp, transitionName: info.transitionName ?? "unknown" };
222
+ }
223
+ }
224
+ function infoToToken(t) {
225
+ return {
226
+ value: t.value,
227
+ createdAt: t.timestamp ? new Date(t.timestamp).getTime() : Date.now()
228
+ };
229
+ }
230
+
231
+ // src/debug/place-analysis.ts
232
+ var PlaceAnalysis = class _PlaceAnalysis {
233
+ _data;
234
+ constructor(data) {
235
+ this._data = data;
236
+ }
237
+ get data() {
238
+ return this._data;
239
+ }
240
+ isStart(placeName) {
241
+ const info = this._data.get(placeName);
242
+ return info != null && !info.hasIncoming;
243
+ }
244
+ isEnd(placeName) {
245
+ const info = this._data.get(placeName);
246
+ return info != null && !info.hasOutgoing;
247
+ }
248
+ /** Build place analysis from a PetriNet. */
249
+ static from(net) {
250
+ const data = /* @__PURE__ */ new Map();
251
+ function ensure(place) {
252
+ let info = data.get(place.name);
253
+ if (!info) {
254
+ info = { tokenType: "unknown", hasIncoming: false, hasOutgoing: false };
255
+ data.set(place.name, info);
256
+ }
257
+ return info;
258
+ }
259
+ for (const transition of net.transitions) {
260
+ for (const input of transition.inputSpecs) {
261
+ const info = ensure(input.place);
262
+ info.hasOutgoing = true;
263
+ }
264
+ if (transition.outputSpec) {
265
+ const outputPlaces = collectOutputPlaces(transition.outputSpec);
266
+ for (const place of outputPlaces) {
267
+ const info = ensure(place);
268
+ info.hasIncoming = true;
269
+ }
270
+ }
271
+ for (const inh of transition.inhibitors) {
272
+ ensure(inh.place);
273
+ }
274
+ for (const read of transition.reads) {
275
+ const info = ensure(read.place);
276
+ info.hasOutgoing = true;
277
+ }
278
+ for (const reset of transition.resets) {
279
+ ensure(reset.place);
280
+ }
281
+ }
282
+ return new _PlaceAnalysis(data);
283
+ }
284
+ };
285
+ function collectOutputPlaces(out) {
286
+ const spec = out;
287
+ switch (spec.type) {
288
+ case "place":
289
+ return spec.place ? [spec.place] : [];
290
+ case "and":
291
+ case "xor":
292
+ return (spec.children ?? []).flatMap((c) => collectOutputPlaces(c));
293
+ case "timeout":
294
+ return spec.child ? collectOutputPlaces(spec.child) : [];
295
+ case "forward-input":
296
+ return spec.to ? [spec.to] : [];
297
+ default:
298
+ return [];
299
+ }
300
+ }
301
+
188
302
  // src/debug/debug-session-registry.ts
189
303
  function buildNetStructure(session) {
190
304
  if (session.importedStructure) {
@@ -562,9 +676,11 @@ function convertMarking(marking, compact = false) {
562
676
  var BATCH_SIZE = 500;
563
677
  var DebugProtocolHandler = class {
564
678
  _sessionRegistry;
679
+ _archiveStorage;
565
680
  _clients = /* @__PURE__ */ new Map();
566
- constructor(sessionRegistry) {
681
+ constructor(sessionRegistry, archiveStorage) {
567
682
  this._sessionRegistry = sessionRegistry;
683
+ this._archiveStorage = archiveStorage ?? null;
568
684
  }
569
685
  /** Registers a new client connection. */
570
686
  clientConnected(clientId, sink) {
@@ -621,6 +737,15 @@ var DebugProtocolHandler = class {
621
737
  case "listBreakpoints":
622
738
  this.handleListBreakpoints(clientState, command);
623
739
  break;
740
+ case "listArchives":
741
+ this.handleListArchives(clientState, command);
742
+ break;
743
+ case "importArchive":
744
+ this.handleImportArchive(clientState, command);
745
+ break;
746
+ case "uploadArchive":
747
+ this.handleUploadArchive(clientState, command);
748
+ break;
624
749
  }
625
750
  } catch (e) {
626
751
  this.sendError(clientState, "COMMAND_ERROR", e instanceof Error ? e.message : String(e), null);
@@ -823,6 +948,97 @@ var DebugProtocolHandler = class {
823
948
  const breakpoints = client.subscriptions.getBreakpoints(cmd.sessionId);
824
949
  this.send(client, { type: "breakpointList", sessionId: cmd.sessionId, breakpoints });
825
950
  }
951
+ // ======================== Archive Handlers ========================
952
+ async handleListArchives(client, cmd) {
953
+ if (!this._archiveStorage || !this._archiveStorage.isAvailable()) {
954
+ this.send(client, { type: "archiveList", archives: [], storageAvailable: false });
955
+ return;
956
+ }
957
+ try {
958
+ const limit = cmd.limit ?? 50;
959
+ const archived = await this._archiveStorage.list(limit, cmd.prefix);
960
+ const summaries = archived.map((a) => ({
961
+ sessionId: a.sessionId,
962
+ key: a.key,
963
+ sizeBytes: a.sizeBytes,
964
+ lastModified: new Date(a.lastModified).toISOString()
965
+ }));
966
+ this.send(client, { type: "archiveList", archives: summaries, storageAvailable: true });
967
+ } catch (e) {
968
+ this.sendError(client, "ARCHIVE_LIST_ERROR", e instanceof Error ? e.message : String(e), null);
969
+ }
970
+ }
971
+ async handleImportArchive(client, cmd) {
972
+ if (!this._archiveStorage || !this._archiveStorage.isAvailable()) {
973
+ this.sendError(client, "NO_ARCHIVE_STORAGE", "Archive storage is not configured", null);
974
+ return;
975
+ }
976
+ try {
977
+ const data = await this._archiveStorage.retrieve(cmd.sessionId);
978
+ const reader = new SessionArchiveReader();
979
+ const imported = reader.readFull(data);
980
+ const meta = imported.metadata;
981
+ this._sessionRegistry.registerImported(
982
+ meta.sessionId,
983
+ meta.netName,
984
+ meta.dotDiagram,
985
+ meta.structure,
986
+ imported.eventStore,
987
+ new Date(meta.startTime).getTime()
988
+ );
989
+ this.send(client, {
990
+ type: "archiveImported",
991
+ sessionId: meta.sessionId,
992
+ netName: meta.netName,
993
+ eventCount: meta.eventCount
994
+ });
995
+ this.broadcastSessionList();
996
+ } catch (e) {
997
+ this.sendError(client, "ARCHIVE_IMPORT_ERROR", e instanceof Error ? e.message : String(e), cmd.sessionId);
998
+ }
999
+ }
1000
+ async handleUploadArchive(client, cmd) {
1001
+ try {
1002
+ const decoded = Buffer.from(cmd.data, "base64");
1003
+ const reader = new SessionArchiveReader();
1004
+ const imported = reader.readFull(decoded);
1005
+ const meta = imported.metadata;
1006
+ this._sessionRegistry.registerImported(
1007
+ meta.sessionId,
1008
+ meta.netName,
1009
+ meta.dotDiagram,
1010
+ meta.structure,
1011
+ imported.eventStore,
1012
+ new Date(meta.startTime).getTime()
1013
+ );
1014
+ this.send(client, {
1015
+ type: "archiveImported",
1016
+ sessionId: meta.sessionId,
1017
+ netName: meta.netName,
1018
+ eventCount: meta.eventCount
1019
+ });
1020
+ this.broadcastSessionList();
1021
+ } catch (e) {
1022
+ this.sendError(client, "ARCHIVE_UPLOAD_ERROR", e instanceof Error ? e.message : String(e), null);
1023
+ }
1024
+ }
1025
+ broadcastSessionList() {
1026
+ const sessions = this._sessionRegistry.listSessions(50);
1027
+ const summaries = sessions.map((s) => ({
1028
+ sessionId: s.sessionId,
1029
+ netName: s.netName,
1030
+ startTime: new Date(s.startTime).toISOString(),
1031
+ active: s.active,
1032
+ eventCount: s.eventStore.eventCount()
1033
+ }));
1034
+ const response = { type: "sessionList", sessions: summaries };
1035
+ for (const clientState of this._clients.values()) {
1036
+ try {
1037
+ this.send(clientState, response);
1038
+ } catch {
1039
+ }
1040
+ }
1041
+ }
826
1042
  // ======================== Helper Methods ========================
827
1043
  send(client, response) {
828
1044
  client.sink(response);
@@ -1112,9 +1328,6 @@ var DebugAwareEventStore = class {
1112
1328
  }
1113
1329
  };
1114
1330
 
1115
- // src/debug/archive/session-archive.ts
1116
- var CURRENT_VERSION = 1;
1117
-
1118
1331
  // src/debug/archive/file-session-archive-storage.ts
1119
1332
  import { mkdir, readdir, readFile, stat, writeFile } from "fs/promises";
1120
1333
  import { join, normalize, resolve } from "path";
@@ -1246,115 +1459,6 @@ var SessionArchiveWriter = class {
1246
1459
  }
1247
1460
  };
1248
1461
 
1249
- // src/debug/archive/session-archive-reader.ts
1250
- import { gunzipSync } from "zlib";
1251
- var MAX_EVENT_SIZE = 10 * 1024 * 1024;
1252
- var SessionArchiveReader = class {
1253
- /** Reads only the metadata header from an archive. */
1254
- readMetadata(compressed) {
1255
- const data = gunzipSync(compressed);
1256
- const metaLen = data.readUInt32BE(0);
1257
- const metaJson = data.subarray(4, 4 + metaLen).toString("utf-8");
1258
- const metadata = JSON.parse(metaJson);
1259
- if (metadata.version !== CURRENT_VERSION) {
1260
- throw new Error(`Unsupported archive version: ${metadata.version} (expected ${CURRENT_VERSION})`);
1261
- }
1262
- return metadata;
1263
- }
1264
- /** Reads the full archive: metadata + all events into a DebugEventStore. */
1265
- readFull(compressed) {
1266
- const data = gunzipSync(compressed);
1267
- let offset = 0;
1268
- const metaLen = data.readUInt32BE(offset);
1269
- offset += 4;
1270
- const metaJson = data.subarray(offset, offset + metaLen).toString("utf-8");
1271
- offset += metaLen;
1272
- const metadata = JSON.parse(metaJson);
1273
- if (metadata.version !== CURRENT_VERSION) {
1274
- throw new Error(`Unsupported archive version: ${metadata.version} (expected ${CURRENT_VERSION})`);
1275
- }
1276
- const eventStore = new DebugEventStore(metadata.sessionId, Number.MAX_SAFE_INTEGER);
1277
- while (offset < data.length) {
1278
- if (offset + 4 > data.length) break;
1279
- const eventLen = data.readUInt32BE(offset);
1280
- offset += 4;
1281
- if (eventLen <= 0 || eventLen > MAX_EVENT_SIZE) {
1282
- throw new Error(`Invalid event size: ${eventLen}`);
1283
- }
1284
- if (offset + eventLen > data.length) break;
1285
- const eventJson = data.subarray(offset, offset + eventLen).toString("utf-8");
1286
- offset += eventLen;
1287
- const eventInfo = JSON.parse(eventJson);
1288
- const netEvent = eventInfoToNetEvent(eventInfo);
1289
- eventStore.append(netEvent);
1290
- }
1291
- return { metadata, eventStore };
1292
- }
1293
- };
1294
- function eventInfoToNetEvent(info) {
1295
- const timestamp = new Date(info.timestamp).getTime();
1296
- const d = info.details;
1297
- switch (info.type) {
1298
- case "ExecutionStarted":
1299
- return { type: "execution-started", timestamp, netName: d["netName"], executionId: d["executionId"] };
1300
- case "ExecutionCompleted":
1301
- return { type: "execution-completed", timestamp, netName: d["netName"], executionId: d["executionId"], totalDurationMs: d["totalDurationMs"] };
1302
- case "TransitionEnabled":
1303
- return { type: "transition-enabled", timestamp, transitionName: info.transitionName };
1304
- case "TransitionClockRestarted":
1305
- return { type: "transition-clock-restarted", timestamp, transitionName: info.transitionName };
1306
- case "TransitionStarted": {
1307
- const tokens = d["consumedTokens"].map((t) => infoToToken(t));
1308
- return { type: "transition-started", timestamp, transitionName: info.transitionName, consumedTokens: tokens };
1309
- }
1310
- case "TransitionCompleted": {
1311
- const tokens = d["producedTokens"].map((t) => infoToToken(t));
1312
- return { type: "transition-completed", timestamp, transitionName: info.transitionName, producedTokens: tokens, durationMs: d["durationMs"] };
1313
- }
1314
- case "TransitionFailed":
1315
- return { type: "transition-failed", timestamp, transitionName: info.transitionName, errorMessage: d["errorMessage"], exceptionType: d["exceptionType"] };
1316
- case "TransitionTimedOut":
1317
- return { type: "transition-timed-out", timestamp, transitionName: info.transitionName, deadlineMs: d["deadlineMs"], actualDurationMs: d["actualDurationMs"] };
1318
- case "ActionTimedOut":
1319
- return { type: "action-timed-out", timestamp, transitionName: info.transitionName, timeoutMs: d["timeoutMs"] };
1320
- case "TokenAdded": {
1321
- const t = d["token"];
1322
- return { type: "token-added", timestamp, placeName: info.placeName, token: infoToToken(t) };
1323
- }
1324
- case "TokenRemoved": {
1325
- const t = d["token"];
1326
- return { type: "token-removed", timestamp, placeName: info.placeName, token: infoToToken(t) };
1327
- }
1328
- case "MarkingSnapshot": {
1329
- const markingData = d["marking"];
1330
- const marking = /* @__PURE__ */ new Map();
1331
- for (const [place, tokens] of Object.entries(markingData)) {
1332
- marking.set(place, tokens.map((t) => infoToToken(t)));
1333
- }
1334
- return { type: "marking-snapshot", timestamp, marking };
1335
- }
1336
- case "LogMessage":
1337
- return {
1338
- type: "log-message",
1339
- timestamp,
1340
- transitionName: info.transitionName,
1341
- logger: d["loggerName"],
1342
- level: d["level"],
1343
- message: d["message"],
1344
- error: d["throwable"] ?? null,
1345
- errorMessage: d["throwableMessage"] ?? null
1346
- };
1347
- default:
1348
- return { type: "transition-enabled", timestamp, transitionName: info.transitionName ?? "unknown" };
1349
- }
1350
- }
1351
- function infoToToken(t) {
1352
- return {
1353
- value: t.value,
1354
- createdAt: t.timestamp ? new Date(t.timestamp).getTime() : Date.now()
1355
- };
1356
- }
1357
-
1358
1462
  // src/debug/index.ts
1359
1463
  async function debugUiAssetPath() {
1360
1464
  const dynamicImport = Function("m", "return import(m)");