libpetri 1.5.1 → 1.8.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 +237 -15
- package/dist/debug/index.js +236 -45
- package/dist/debug/index.js.map +1 -1
- package/dist/doclet/index.d.ts +1 -1
- package/dist/{event-store-Y8q_wapJ.d.ts → event-store-BnyHh3TF.d.ts} +1 -1
- package/dist/export/index.d.ts +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +28 -6
- package/dist/index.js.map +1 -1
- package/dist/{petri-net-C3Jy5HCt.d.ts → petri-net-D-GN9g_D.d.ts} +31 -4
- package/dist/verification/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/debug/index.js
CHANGED
|
@@ -118,7 +118,11 @@ var DebugEventStore = class {
|
|
|
118
118
|
};
|
|
119
119
|
|
|
120
120
|
// src/debug/archive/session-archive.ts
|
|
121
|
-
var CURRENT_VERSION =
|
|
121
|
+
var CURRENT_VERSION = 3;
|
|
122
|
+
var MIN_SUPPORTED_VERSION = 1;
|
|
123
|
+
function emptyMetadata() {
|
|
124
|
+
return { eventTypeHistogram: {}, hasErrors: false };
|
|
125
|
+
}
|
|
122
126
|
|
|
123
127
|
// src/debug/archive/session-archive-reader.ts
|
|
124
128
|
var MAX_EVENT_SIZE = 10 * 1024 * 1024;
|
|
@@ -128,11 +132,7 @@ var SessionArchiveReader = class {
|
|
|
128
132
|
const data = gunzipSync(compressed);
|
|
129
133
|
const metaLen = data.readUInt32BE(0);
|
|
130
134
|
const metaJson = data.subarray(4, 4 + metaLen).toString("utf-8");
|
|
131
|
-
|
|
132
|
-
if (metadata.version !== CURRENT_VERSION) {
|
|
133
|
-
throw new Error(`Unsupported archive version: ${metadata.version} (expected ${CURRENT_VERSION})`);
|
|
134
|
-
}
|
|
135
|
-
return metadata;
|
|
135
|
+
return parseHeader(metaJson);
|
|
136
136
|
}
|
|
137
137
|
/** Reads the full archive: metadata + all events into a DebugEventStore. */
|
|
138
138
|
readFull(compressed) {
|
|
@@ -142,10 +142,7 @@ var SessionArchiveReader = class {
|
|
|
142
142
|
offset += 4;
|
|
143
143
|
const metaJson = data.subarray(offset, offset + metaLen).toString("utf-8");
|
|
144
144
|
offset += metaLen;
|
|
145
|
-
const metadata =
|
|
146
|
-
if (metadata.version !== CURRENT_VERSION) {
|
|
147
|
-
throw new Error(`Unsupported archive version: ${metadata.version} (expected ${CURRENT_VERSION})`);
|
|
148
|
-
}
|
|
145
|
+
const metadata = parseHeader(metaJson);
|
|
149
146
|
const eventStore = new DebugEventStore(metadata.sessionId, Number.MAX_SAFE_INTEGER);
|
|
150
147
|
while (offset < data.length) {
|
|
151
148
|
if (offset + 4 > data.length) break;
|
|
@@ -164,6 +161,33 @@ var SessionArchiveReader = class {
|
|
|
164
161
|
return { metadata, eventStore };
|
|
165
162
|
}
|
|
166
163
|
};
|
|
164
|
+
function parseHeader(metaJson) {
|
|
165
|
+
const raw = JSON.parse(metaJson);
|
|
166
|
+
switch (raw.version) {
|
|
167
|
+
case 1:
|
|
168
|
+
return raw;
|
|
169
|
+
case 2: {
|
|
170
|
+
const v2 = raw;
|
|
171
|
+
return {
|
|
172
|
+
...v2,
|
|
173
|
+
tags: v2.tags ?? {},
|
|
174
|
+
metadata: v2.metadata ?? emptyMetadata()
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
case 3: {
|
|
178
|
+
const v3 = raw;
|
|
179
|
+
return {
|
|
180
|
+
...v3,
|
|
181
|
+
tags: v3.tags ?? {},
|
|
182
|
+
metadata: v3.metadata ?? emptyMetadata()
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
default:
|
|
186
|
+
throw new Error(
|
|
187
|
+
`Unsupported archive version: ${raw.version} (reader supports ${MIN_SUPPORTED_VERSION}..${CURRENT_VERSION})`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
167
191
|
function eventInfoToNetEvent(info) {
|
|
168
192
|
const timestamp = new Date(info.timestamp).getTime();
|
|
169
193
|
const d = info.details;
|
|
@@ -222,10 +246,8 @@ function eventInfoToNetEvent(info) {
|
|
|
222
246
|
}
|
|
223
247
|
}
|
|
224
248
|
function infoToToken(t) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
createdAt: t.timestamp ? new Date(t.timestamp).getTime() : Date.now()
|
|
228
|
-
};
|
|
249
|
+
const createdAt = t.timestamp ? new Date(t.timestamp).getTime() : Date.now();
|
|
250
|
+
return t.structured === void 0 ? { value: t.value, createdAt } : { value: t.value, createdAt, structured: t.structured };
|
|
229
251
|
}
|
|
230
252
|
|
|
231
253
|
// src/debug/place-analysis.ts
|
|
@@ -341,8 +363,12 @@ var DebugSessionRegistry = class {
|
|
|
341
363
|
/**
|
|
342
364
|
* Registers a new debug session for the given Petri net.
|
|
343
365
|
* Generates DOT diagram and extracts net structure.
|
|
366
|
+
*
|
|
367
|
+
* @param sessionId unique session id
|
|
368
|
+
* @param net the Petri net being executed
|
|
369
|
+
* @param tags optional user-defined tags (libpetri 1.6.0+) — e.g. `{channel: 'voice'}`
|
|
344
370
|
*/
|
|
345
|
-
register(sessionId, net) {
|
|
371
|
+
register(sessionId, net, tags) {
|
|
346
372
|
const dotDiagram = dotExport(net);
|
|
347
373
|
const places = PlaceAnalysis.from(net);
|
|
348
374
|
const eventStore = this._eventStoreFactory(sessionId);
|
|
@@ -355,7 +381,8 @@ var DebugSessionRegistry = class {
|
|
|
355
381
|
eventStore,
|
|
356
382
|
startTime: Date.now(),
|
|
357
383
|
active: true,
|
|
358
|
-
importedStructure: null
|
|
384
|
+
importedStructure: null,
|
|
385
|
+
tags: tags ? { ...tags } : {}
|
|
359
386
|
};
|
|
360
387
|
this.evictIfNecessary();
|
|
361
388
|
this._sessions.set(sessionId, session);
|
|
@@ -363,16 +390,20 @@ var DebugSessionRegistry = class {
|
|
|
363
390
|
}
|
|
364
391
|
/**
|
|
365
392
|
* Marks a session as completed (no longer active) and notifies completion listeners.
|
|
393
|
+
*
|
|
394
|
+
* <p>Stamps `endTime = Date.now()` on the first completion. Idempotent: subsequent
|
|
395
|
+
* calls preserve the existing endTime. (libpetri 1.6.0+)
|
|
366
396
|
*/
|
|
367
397
|
complete(sessionId) {
|
|
368
398
|
const session = this._sessions.get(sessionId);
|
|
369
399
|
if (session) {
|
|
370
|
-
const
|
|
400
|
+
const endTime = session.endTime ?? Date.now();
|
|
401
|
+
const completed = { ...session, active: false, endTime };
|
|
371
402
|
this._sessions.set(sessionId, completed);
|
|
372
403
|
this.notifyCompletionListeners(completed);
|
|
373
404
|
}
|
|
374
405
|
}
|
|
375
|
-
/** Removes a session from the registry. */
|
|
406
|
+
/** Removes a session from the registry. Tags die with the session. */
|
|
376
407
|
remove(sessionId) {
|
|
377
408
|
const removed = this._sessions.get(sessionId);
|
|
378
409
|
if (removed) {
|
|
@@ -385,13 +416,36 @@ var DebugSessionRegistry = class {
|
|
|
385
416
|
getSession(sessionId) {
|
|
386
417
|
return this._sessions.get(sessionId);
|
|
387
418
|
}
|
|
419
|
+
/**
|
|
420
|
+
* Sets or overwrites a single tag on a session. (libpetri 1.6.0+)
|
|
421
|
+
*
|
|
422
|
+
* Tags accumulate until the session is removed. Setting a key that already
|
|
423
|
+
* exists replaces its value.
|
|
424
|
+
*
|
|
425
|
+
* If `sessionId` is not a currently-registered session the call is a no-op.
|
|
426
|
+
* A tag write that races with {@link remove} is harmless — the write lands on
|
|
427
|
+
* the now-orphaned session object and is garbage collected along with it.
|
|
428
|
+
*/
|
|
429
|
+
tag(sessionId, key, value) {
|
|
430
|
+
const session = this._sessions.get(sessionId);
|
|
431
|
+
if (!session) return;
|
|
432
|
+
session.tags[key] = value;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Returns a snapshot of the tags attached to a session. Returns an empty
|
|
436
|
+
* object if the session has no tags or does not exist. (libpetri 1.6.0+)
|
|
437
|
+
*/
|
|
438
|
+
tagsFor(sessionId) {
|
|
439
|
+
const session = this._sessions.get(sessionId);
|
|
440
|
+
return session ? { ...session.tags } : {};
|
|
441
|
+
}
|
|
388
442
|
/** Lists sessions, ordered by start time (most recent first). */
|
|
389
|
-
listSessions(limit) {
|
|
390
|
-
return [...this._sessions.values()].sort((a, b) => b.startTime - a.startTime).slice(0, limit);
|
|
443
|
+
listSessions(limit, tagFilter) {
|
|
444
|
+
return [...this._sessions.values()].filter((s) => this.matchesTagFilter(s, tagFilter)).sort((a, b) => b.startTime - a.startTime).slice(0, limit);
|
|
391
445
|
}
|
|
392
446
|
/** Lists only active sessions. */
|
|
393
|
-
listActiveSessions(limit) {
|
|
394
|
-
return [...this._sessions.values()].filter((s) => s.active).sort((a, b) => b.startTime - a.startTime).slice(0, limit);
|
|
447
|
+
listActiveSessions(limit, tagFilter) {
|
|
448
|
+
return [...this._sessions.values()].filter((s) => s.active).filter((s) => this.matchesTagFilter(s, tagFilter)).sort((a, b) => b.startTime - a.startTime).slice(0, limit);
|
|
395
449
|
}
|
|
396
450
|
/** Total number of sessions. */
|
|
397
451
|
get size() {
|
|
@@ -399,8 +453,11 @@ var DebugSessionRegistry = class {
|
|
|
399
453
|
}
|
|
400
454
|
/**
|
|
401
455
|
* Registers an imported (archived) session as an inactive, read-only session.
|
|
456
|
+
*
|
|
457
|
+
* @param endTime when the original session ended (libpetri 1.6.0+)
|
|
458
|
+
* @param tags user-defined tags attached to the imported session (libpetri 1.6.0+)
|
|
402
459
|
*/
|
|
403
|
-
registerImported(sessionId, netName, dotDiagram, structure, eventStore, startTime) {
|
|
460
|
+
registerImported(sessionId, netName, dotDiagram, structure, eventStore, startTime, endTime, tags) {
|
|
404
461
|
this.evictIfNecessary();
|
|
405
462
|
const session = {
|
|
406
463
|
sessionId,
|
|
@@ -411,11 +468,24 @@ var DebugSessionRegistry = class {
|
|
|
411
468
|
eventStore,
|
|
412
469
|
startTime,
|
|
413
470
|
active: false,
|
|
414
|
-
importedStructure: structure
|
|
471
|
+
importedStructure: structure,
|
|
472
|
+
endTime,
|
|
473
|
+
tags: tags ? { ...tags } : {}
|
|
415
474
|
};
|
|
416
475
|
this._sessions.set(sessionId, session);
|
|
417
476
|
return session;
|
|
418
477
|
}
|
|
478
|
+
/** AND-match: all filter entries must exactly match the session's tags. */
|
|
479
|
+
matchesTagFilter(session, filter) {
|
|
480
|
+
if (!filter) return true;
|
|
481
|
+
const keys = Object.keys(filter);
|
|
482
|
+
if (keys.length === 0) return true;
|
|
483
|
+
const tags = session.tags;
|
|
484
|
+
for (const key of keys) {
|
|
485
|
+
if (tags[key] !== filter[key]) return false;
|
|
486
|
+
}
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
419
489
|
/** Notifies all completion listeners. Exceptions are caught and logged. */
|
|
420
490
|
notifyCompletionListeners(session) {
|
|
421
491
|
for (const listener of this._completionListeners) {
|
|
@@ -646,12 +716,30 @@ function tokenInfo(token) {
|
|
|
646
716
|
const value = token.value;
|
|
647
717
|
const type = value != null ? typeof value === "object" ? value.constructor.name : typeof value : "null";
|
|
648
718
|
const fullValue = value != null ? String(value) : "null";
|
|
649
|
-
|
|
719
|
+
const info = {
|
|
650
720
|
id: null,
|
|
651
721
|
type,
|
|
652
722
|
value: fullValue,
|
|
653
723
|
timestamp: new Date(token.createdAt).toISOString()
|
|
654
724
|
};
|
|
725
|
+
const structured = structuredValue(value);
|
|
726
|
+
return structured === void 0 ? info : { ...info, structured };
|
|
727
|
+
}
|
|
728
|
+
function structuredValue(value) {
|
|
729
|
+
if (value == null) return void 0;
|
|
730
|
+
const t = typeof value;
|
|
731
|
+
if (t === "string" || t === "number" || t === "boolean") return value;
|
|
732
|
+
if (t === "bigint") return String(value);
|
|
733
|
+
if (t === "symbol" || t === "function") return void 0;
|
|
734
|
+
try {
|
|
735
|
+
const cloned = JSON.parse(JSON.stringify(value));
|
|
736
|
+
if (cloned && typeof cloned === "object" && !Array.isArray(cloned) && Object.keys(cloned).length === 0) {
|
|
737
|
+
return void 0;
|
|
738
|
+
}
|
|
739
|
+
return cloned;
|
|
740
|
+
} catch {
|
|
741
|
+
return void 0;
|
|
742
|
+
}
|
|
655
743
|
}
|
|
656
744
|
function compactTokenInfo(token) {
|
|
657
745
|
const value = token.value;
|
|
@@ -754,15 +842,22 @@ var DebugProtocolHandler = class {
|
|
|
754
842
|
// ======================== Command Handlers ========================
|
|
755
843
|
handleListSessions(client, cmd) {
|
|
756
844
|
const limit = cmd.limit ?? 50;
|
|
757
|
-
const sessions = cmd.activeOnly ? this._sessionRegistry.listActiveSessions(limit) : this._sessionRegistry.listSessions(limit);
|
|
758
|
-
const summaries = sessions.map((s) => (
|
|
845
|
+
const sessions = cmd.activeOnly ? this._sessionRegistry.listActiveSessions(limit, cmd.tagFilter) : this._sessionRegistry.listSessions(limit, cmd.tagFilter);
|
|
846
|
+
const summaries = sessions.map((s) => this.toProtocolSummary(s));
|
|
847
|
+
this.send(client, { type: "sessionList", sessions: summaries });
|
|
848
|
+
}
|
|
849
|
+
toProtocolSummary(s) {
|
|
850
|
+
const tags = this._sessionRegistry.tagsFor(s.sessionId);
|
|
851
|
+
return {
|
|
759
852
|
sessionId: s.sessionId,
|
|
760
853
|
netName: s.netName,
|
|
761
854
|
startTime: new Date(s.startTime).toISOString(),
|
|
762
855
|
active: s.active,
|
|
763
|
-
eventCount: s.eventStore.eventCount()
|
|
764
|
-
|
|
765
|
-
|
|
856
|
+
eventCount: s.eventStore.eventCount(),
|
|
857
|
+
tags: Object.keys(tags).length > 0 ? tags : void 0,
|
|
858
|
+
endTime: s.endTime !== void 0 ? new Date(s.endTime).toISOString() : void 0,
|
|
859
|
+
durationMs: s.endTime !== void 0 ? s.endTime - s.startTime : void 0
|
|
860
|
+
};
|
|
766
861
|
}
|
|
767
862
|
handleSubscribe(client, cmd) {
|
|
768
863
|
const debugSession = this._sessionRegistry.getSession(cmd.sessionId);
|
|
@@ -1024,13 +1119,7 @@ var DebugProtocolHandler = class {
|
|
|
1024
1119
|
}
|
|
1025
1120
|
broadcastSessionList() {
|
|
1026
1121
|
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
|
-
}));
|
|
1122
|
+
const summaries = sessions.map((s) => this.toProtocolSummary(s));
|
|
1034
1123
|
const response = { type: "sessionList", sessions: summaries };
|
|
1035
1124
|
for (const clientState of this._clients.values()) {
|
|
1036
1125
|
try {
|
|
@@ -1442,23 +1531,126 @@ var FileSessionArchiveStorage = class {
|
|
|
1442
1531
|
|
|
1443
1532
|
// src/debug/archive/session-archive-writer.ts
|
|
1444
1533
|
import { gzipSync } from "zlib";
|
|
1534
|
+
|
|
1535
|
+
// src/debug/archive/session-metadata.ts
|
|
1536
|
+
function computeMetadata(events) {
|
|
1537
|
+
const raw = {};
|
|
1538
|
+
let first;
|
|
1539
|
+
let last;
|
|
1540
|
+
let hasErrors = false;
|
|
1541
|
+
for (const event of events) {
|
|
1542
|
+
const typeName = toEventInfo(event).type;
|
|
1543
|
+
raw[typeName] = (raw[typeName] ?? 0) + 1;
|
|
1544
|
+
if (first === void 0) first = event.timestamp;
|
|
1545
|
+
last = event.timestamp;
|
|
1546
|
+
if (isErrorEvent(event)) hasErrors = true;
|
|
1547
|
+
}
|
|
1548
|
+
const histogram = {};
|
|
1549
|
+
for (const key of Object.keys(raw).sort()) {
|
|
1550
|
+
histogram[key] = raw[key];
|
|
1551
|
+
}
|
|
1552
|
+
return {
|
|
1553
|
+
eventTypeHistogram: histogram,
|
|
1554
|
+
firstEventTime: first !== void 0 ? new Date(first).toISOString() : void 0,
|
|
1555
|
+
lastEventTime: last !== void 0 ? new Date(last).toISOString() : void 0,
|
|
1556
|
+
hasErrors
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
function isErrorEvent(event) {
|
|
1560
|
+
switch (event.type) {
|
|
1561
|
+
case "transition-failed":
|
|
1562
|
+
case "transition-timed-out":
|
|
1563
|
+
case "action-timed-out":
|
|
1564
|
+
return true;
|
|
1565
|
+
case "log-message":
|
|
1566
|
+
return event.level.toUpperCase() === "ERROR";
|
|
1567
|
+
default:
|
|
1568
|
+
return false;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
// src/debug/archive/session-archive-writer.ts
|
|
1445
1573
|
var SessionArchiveWriter = class {
|
|
1446
1574
|
/**
|
|
1447
|
-
* Writes a complete session archive
|
|
1575
|
+
* Writes a complete session archive in the current format (v3 as of 1.8.0)
|
|
1576
|
+
* and returns the compressed bytes.
|
|
1448
1577
|
*/
|
|
1449
1578
|
write(session) {
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1579
|
+
return this.writeV3(session);
|
|
1580
|
+
}
|
|
1581
|
+
/**
|
|
1582
|
+
* Writes a session in the legacy v1 format. Use only for compatibility
|
|
1583
|
+
* testing or when producing archives for consumers pinned to libpetri ≤ 1.6.1.
|
|
1584
|
+
*/
|
|
1585
|
+
writeV1(session) {
|
|
1586
|
+
const header = {
|
|
1587
|
+
version: 1,
|
|
1453
1588
|
sessionId: session.sessionId,
|
|
1454
1589
|
netName: session.netName,
|
|
1455
1590
|
dotDiagram: session.dotDiagram,
|
|
1456
1591
|
startTime: new Date(session.startTime).toISOString(),
|
|
1457
1592
|
eventCount: session.eventStore.eventCount(),
|
|
1458
|
-
structure
|
|
1593
|
+
structure: buildNetStructure(session)
|
|
1459
1594
|
};
|
|
1595
|
+
return this.writeFramed(header, session);
|
|
1596
|
+
}
|
|
1597
|
+
/**
|
|
1598
|
+
* Writes a session in the v2 format — richer header with `endTime`, `tags`,
|
|
1599
|
+
* and pre-computed {@link SessionMetadata}.
|
|
1600
|
+
*
|
|
1601
|
+
* Two passes over the event store: one to compute metadata, one to serialize
|
|
1602
|
+
* events. `DebugEventStore` stores events in a plain readonly array and its
|
|
1603
|
+
* `[Symbol.iterator]()` returns a fresh array iterator each call, so both
|
|
1604
|
+
* passes walk the same sequence from the start.
|
|
1605
|
+
*/
|
|
1606
|
+
writeV2(session) {
|
|
1607
|
+
const metadata = computeMetadata(session.eventStore);
|
|
1608
|
+
const header = {
|
|
1609
|
+
version: 2,
|
|
1610
|
+
sessionId: session.sessionId,
|
|
1611
|
+
netName: session.netName,
|
|
1612
|
+
dotDiagram: session.dotDiagram,
|
|
1613
|
+
startTime: new Date(session.startTime).toISOString(),
|
|
1614
|
+
endTime: session.endTime !== void 0 ? new Date(session.endTime).toISOString() : void 0,
|
|
1615
|
+
eventCount: session.eventStore.eventCount(),
|
|
1616
|
+
// Snapshot of tags at archive-write time — record the state that was
|
|
1617
|
+
// current when the session was archived, not whatever happens on the
|
|
1618
|
+
// live session afterwards.
|
|
1619
|
+
tags: { ...session.tags },
|
|
1620
|
+
metadata,
|
|
1621
|
+
structure: buildNetStructure(session)
|
|
1622
|
+
};
|
|
1623
|
+
return this.writeFramed(header, session);
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* Writes a session in the v3 format — same header shape as v2, with version=3
|
|
1627
|
+
* signalling that token payloads carry a `structured` field alongside the
|
|
1628
|
+
* legacy `value` string (see {@link tokenInfo}).
|
|
1629
|
+
*/
|
|
1630
|
+
writeV3(session) {
|
|
1631
|
+
const metadata = computeMetadata(session.eventStore);
|
|
1632
|
+
const header = {
|
|
1633
|
+
version: 3,
|
|
1634
|
+
sessionId: session.sessionId,
|
|
1635
|
+
netName: session.netName,
|
|
1636
|
+
dotDiagram: session.dotDiagram,
|
|
1637
|
+
startTime: new Date(session.startTime).toISOString(),
|
|
1638
|
+
endTime: session.endTime !== void 0 ? new Date(session.endTime).toISOString() : void 0,
|
|
1639
|
+
eventCount: session.eventStore.eventCount(),
|
|
1640
|
+
tags: { ...session.tags },
|
|
1641
|
+
metadata,
|
|
1642
|
+
structure: buildNetStructure(session)
|
|
1643
|
+
};
|
|
1644
|
+
return this.writeFramed(header, session);
|
|
1645
|
+
}
|
|
1646
|
+
/**
|
|
1647
|
+
* Shared framing logic: length-prefixed header JSON, then length-prefixed
|
|
1648
|
+
* event JSON, then gzip. Both v1 and v2 archives use the identical event
|
|
1649
|
+
* wire format, so the body loop is version-agnostic.
|
|
1650
|
+
*/
|
|
1651
|
+
writeFramed(header, session) {
|
|
1460
1652
|
const parts = [];
|
|
1461
|
-
const metaBytes = Buffer.from(JSON.stringify(
|
|
1653
|
+
const metaBytes = Buffer.from(JSON.stringify(header), "utf-8");
|
|
1462
1654
|
const metaLen = Buffer.alloc(4);
|
|
1463
1655
|
metaLen.writeUInt32BE(metaBytes.length);
|
|
1464
1656
|
parts.push(metaLen, metaBytes);
|
|
@@ -1469,8 +1661,7 @@ var SessionArchiveWriter = class {
|
|
|
1469
1661
|
eventLen.writeUInt32BE(eventBytes.length);
|
|
1470
1662
|
parts.push(eventLen, eventBytes);
|
|
1471
1663
|
}
|
|
1472
|
-
|
|
1473
|
-
return gzipSync(raw);
|
|
1664
|
+
return gzipSync(Buffer.concat(parts));
|
|
1474
1665
|
}
|
|
1475
1666
|
};
|
|
1476
1667
|
|