@lucid-evolution/tx-graph 0.0.1

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.cjs ADDED
@@ -0,0 +1,4292 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ buildSemanticRenderGraph: () => buildSemanticRenderGraph,
34
+ createResolutionCache: () => createResolutionCache,
35
+ createTxGraph: () => createTxGraph,
36
+ describeRedeemerByConstructor: () => describeRedeemerByConstructor,
37
+ describeRedeemerWith: () => describeRedeemerWith,
38
+ genesisUtxo: () => genesisUtxo,
39
+ labelRedeemer: () => labelRedeemer,
40
+ outRefKey: () => outRefKey,
41
+ parseOutRefKey: () => parseOutRefKey,
42
+ parseTransaction: () => parseTransaction,
43
+ parseTransactionCbor: () => parseTransactionCbor,
44
+ producedUtxosFromTransaction: () => producedUtxosFromTransaction,
45
+ resolveVisualRendererOptions: () => resolveVisualRendererOptions,
46
+ semanticSvgForTesting: () => semanticSvgForTesting,
47
+ tagByAddress: () => tagByAddress,
48
+ tagByCredential: () => tagByCredential,
49
+ tagByDatum: () => tagByDatum,
50
+ tagByDatumHash: () => tagByDatumHash,
51
+ tagByPolicyId: () => tagByPolicyId,
52
+ tagByScriptRef: () => tagByScriptRef,
53
+ tagByUnit: () => tagByUnit,
54
+ tagChange: () => tagChange,
55
+ toTraceTxOutput: () => toTraceTxOutput,
56
+ toTraceUtxo: () => toTraceUtxo,
57
+ traceToDot: () => traceToDot,
58
+ traceToHtml: () => traceToHtml,
59
+ traceToJSON: () => traceToJSON,
60
+ traceToMermaid: () => traceToMermaid,
61
+ traceToSvg: () => traceToSvg,
62
+ unresolvedUtxo: () => unresolvedUtxo,
63
+ visualEdgeStyle: () => visualEdgeStyle,
64
+ wrapProvider: () => wrapProvider
65
+ });
66
+ module.exports = __toCommonJS(index_exports);
67
+
68
+ // src/parse.ts
69
+ var import_utils = require("@lucid-evolution/utils");
70
+
71
+ // src/core.ts
72
+ var CML = __toESM(require("@anastasia-labs/cardano-multiplatform-lib-nodejs"), 1);
73
+
74
+ // src/parse.ts
75
+ var parseTransaction = (input, options = {}) => {
76
+ const tx = normalizeTransaction(input);
77
+ const body = tx.body();
78
+ const hash = CML.hash_transaction(body).to_hex();
79
+ const outputs = parseOutputs(hash, body.outputs());
80
+ const mint = parseMint(body.mint());
81
+ const splitMint = splitMintAssets(mint);
82
+ const auxiliaryDataHash = body.auxiliary_data_hash()?.to_hex();
83
+ return {
84
+ hash,
85
+ label: options.label,
86
+ status: options.status ?? "built",
87
+ cbor: tx.to_cbor_hex(),
88
+ sizeBytes: tx.to_cbor_bytes().length,
89
+ fee: body.fee().toString(),
90
+ validityInterval: {
91
+ validFrom: body.validity_interval_start()?.toString(),
92
+ validTo: body.ttl()?.toString()
93
+ },
94
+ inputs: parseInputList(body.inputs()),
95
+ referenceInputs: parseInputList(body.reference_inputs()),
96
+ collateralInputs: parseInputList(body.collateral_inputs()),
97
+ collateralReturn: parseOptionalOutput(body.collateral_return()),
98
+ totalCollateral: body.total_collateral()?.toString(),
99
+ outputs,
100
+ mint,
101
+ mintedAssets: splitMint.mintedAssets,
102
+ burnedAssets: splitMint.burnedAssets,
103
+ withdrawals: parseWithdrawals(body.withdrawals()),
104
+ certificates: parseCertificates(body.certs()),
105
+ redeemers: parseRedeemers(tx.witness_set().redeemers()),
106
+ requiredSigners: parseRequiredSigners(body.required_signers()),
107
+ auxiliaryDataHash
108
+ };
109
+ };
110
+ var parseTransactionCbor = (cbor, options = {}) => parseTransaction(cbor, options);
111
+ var normalizeTransaction = (input) => {
112
+ if (typeof input === "string") {
113
+ return CML.Transaction.from_cbor_hex(input);
114
+ }
115
+ if (hasToTransaction(input)) {
116
+ return input.toTransaction();
117
+ }
118
+ if (isCmlTransaction(input)) {
119
+ return input;
120
+ }
121
+ if (hasToCBOR(input)) {
122
+ return CML.Transaction.from_cbor_hex(input.toCBOR({ canonical: false }));
123
+ }
124
+ throw new TypeError("Unsupported transaction input");
125
+ };
126
+ var hasToTransaction = (input) => typeof input === "object" && input !== null && "toTransaction" in input && typeof input.toTransaction === "function";
127
+ var hasToCBOR = (input) => typeof input === "object" && input !== null && "toCBOR" in input && typeof input.toCBOR === "function";
128
+ var isCmlTransaction = (input) => typeof input === "object" && input !== null && "body" in input && "witness_set" in input && typeof input.body === "function" && typeof input.witness_set === "function";
129
+ var parseInputList = (inputs) => {
130
+ if (!inputs) return [];
131
+ const result = [];
132
+ for (let index = 0; index < inputs.len(); index++) {
133
+ result.push((0, import_utils.coreToOutRef)(inputs.get(index)));
134
+ }
135
+ return result;
136
+ };
137
+ var parseOutputs = (txHash, outputs) => {
138
+ const result = [];
139
+ for (let index = 0; index < outputs.len(); index++) {
140
+ result.push({
141
+ txHash,
142
+ outputIndex: index,
143
+ ...parseTxOutput(outputs.get(index)),
144
+ resolution: "resolved",
145
+ tags: []
146
+ });
147
+ }
148
+ return result;
149
+ };
150
+ var parseOptionalOutput = (output) => output ? parseTxOutput(output) : void 0;
151
+ var parseTxOutput = (output) => {
152
+ const txOutput = (0, import_utils.coreToTxOutput)(output);
153
+ const credentials = credentialsOf(txOutput.address);
154
+ return {
155
+ address: txOutput.address,
156
+ paymentCredential: credentials.paymentCredential,
157
+ stakeCredential: credentials.stakeCredential,
158
+ assets: traceAssets(txOutput.assets),
159
+ datumHash: txOutput.datumHash ?? void 0,
160
+ datum: txOutput.datum ?? void 0,
161
+ scriptRef: txOutput.scriptRef ? traceScriptRef(txOutput.scriptRef) : void 0
162
+ };
163
+ };
164
+ var credentialsOf = (address) => {
165
+ try {
166
+ const { paymentCredential, stakeCredential } = (0, import_utils.getAddressDetails)(address);
167
+ return {
168
+ paymentCredential: paymentCredential ?? void 0,
169
+ stakeCredential: stakeCredential ?? void 0
170
+ };
171
+ } catch {
172
+ return {};
173
+ }
174
+ };
175
+ var traceAssets = (assets) => {
176
+ const result = {};
177
+ for (const unit of Object.keys(assets).sort()) {
178
+ result[unit] = assets[unit].toString();
179
+ }
180
+ return result;
181
+ };
182
+ var traceScriptRef = (script) => ({
183
+ type: script.type,
184
+ hash: scriptHash(script),
185
+ sizeBytes: script.script.length / 2
186
+ });
187
+ var scriptHash = (script) => {
188
+ try {
189
+ switch (script.type) {
190
+ case "Native":
191
+ return CML.NativeScript.from_cbor_hex(script.script).hash().to_hex();
192
+ case "PlutusV1":
193
+ return CML.PlutusScript.from_v1(
194
+ CML.PlutusV1Script.from_cbor_hex(script.script)
195
+ ).hash().to_hex();
196
+ case "PlutusV2":
197
+ return CML.PlutusScript.from_v2(
198
+ CML.PlutusV2Script.from_cbor_hex(script.script)
199
+ ).hash().to_hex();
200
+ case "PlutusV3":
201
+ return CML.PlutusScript.from_v3(
202
+ CML.PlutusV3Script.from_cbor_hex(script.script)
203
+ ).hash().to_hex();
204
+ }
205
+ } catch {
206
+ return void 0;
207
+ }
208
+ };
209
+ var parseMint = (mint) => {
210
+ const assets = {};
211
+ if (!mint) return {};
212
+ const policies = mint.keys();
213
+ for (let policyIndex = 0; policyIndex < policies.len(); policyIndex++) {
214
+ const policy = policies.get(policyIndex);
215
+ const policyId = policy.to_hex();
216
+ const policyAssets = mint.get_assets(policy);
217
+ if (!policyAssets) continue;
218
+ const assetNames = policyAssets.keys();
219
+ for (let assetIndex = 0; assetIndex < assetNames.len(); assetIndex++) {
220
+ const assetName2 = assetNames.get(assetIndex);
221
+ const quantity = policyAssets.get(assetName2);
222
+ if (quantity === void 0 || quantity === 0n) continue;
223
+ assets[policyId + assetName2.to_js_value()] = quantity;
224
+ }
225
+ }
226
+ return traceAssets(assets);
227
+ };
228
+ var splitMintAssets = (mint) => {
229
+ const mintedAssets = {};
230
+ const burnedAssets = {};
231
+ for (const unit of Object.keys(mint).sort()) {
232
+ const quantity = BigInt(mint[unit]);
233
+ if (quantity > 0n) {
234
+ mintedAssets[unit] = quantity.toString();
235
+ } else if (quantity < 0n) {
236
+ burnedAssets[unit] = (-quantity).toString();
237
+ }
238
+ }
239
+ return { mintedAssets, burnedAssets };
240
+ };
241
+ var parseWithdrawals = (withdrawals) => {
242
+ if (!withdrawals) return [];
243
+ const result = [];
244
+ const rewardAccounts = withdrawals.keys();
245
+ for (let index = 0; index < rewardAccounts.len(); index++) {
246
+ const rewardAccount = rewardAccounts.get(index);
247
+ const amount = withdrawals.get(rewardAccount);
248
+ if (amount === void 0) continue;
249
+ result.push({
250
+ rewardAddress: rewardAccount.to_address().to_bech32(void 0),
251
+ amount: amount.toString()
252
+ });
253
+ }
254
+ return result;
255
+ };
256
+ var parseCertificates = (certificates) => {
257
+ if (!certificates) return [];
258
+ const result = [];
259
+ for (let index = 0; index < certificates.len(); index++) {
260
+ const certificate = certificates.get(index);
261
+ const kind = certificate.kind();
262
+ result.push({
263
+ index,
264
+ kind,
265
+ kindName: certificateKindName(kind),
266
+ cbor: cborHex(certificate)
267
+ });
268
+ }
269
+ return result;
270
+ };
271
+ var certificateKindName = (kind) => {
272
+ for (const [name, value] of Object.entries(CML.CertificateKind)) {
273
+ if (typeof value === "number" && value === kind) return name;
274
+ }
275
+ return `Unknown(${kind})`;
276
+ };
277
+ var parseRequiredSigners = (signers) => {
278
+ if (!signers) return [];
279
+ const result = [];
280
+ for (let index = 0; index < signers.len(); index++) {
281
+ result.push(signers.get(index).to_hex());
282
+ }
283
+ return result;
284
+ };
285
+ var parseRedeemers = (redeemers) => {
286
+ if (!redeemers) return [];
287
+ return canonicalRedeemerEntries(redeemers).map(
288
+ (entry, redeemerListIndex) => ({
289
+ tag: (0, import_utils.fromCMLRedeemerTag)(entry.tag),
290
+ index: entry.index.toString(),
291
+ redeemerListIndex,
292
+ data: entry.data.to_cbor_hex(),
293
+ exUnits: {
294
+ mem: entry.exUnits.mem().toString(),
295
+ steps: entry.exUnits.steps().toString()
296
+ }
297
+ })
298
+ );
299
+ };
300
+ var canonicalRedeemerEntries = (redeemers) => {
301
+ const entries = [];
302
+ const legacyRedeemers = redeemers.as_arr_legacy_redeemer();
303
+ if (legacyRedeemers) {
304
+ for (let index = 0; index < legacyRedeemers.len(); index++) {
305
+ const redeemer = legacyRedeemers.get(index);
306
+ const tag = redeemer.tag();
307
+ const redeemerIndex = redeemer.index();
308
+ entries.push({
309
+ tag,
310
+ index: redeemerIndex,
311
+ data: redeemer.data(),
312
+ exUnits: redeemer.ex_units(),
313
+ sortKey: redeemerKeyBytes(tag, redeemerIndex)
314
+ });
315
+ }
316
+ }
317
+ const mapRedeemers = redeemers.as_map_redeemer_key_to_redeemer_val();
318
+ if (mapRedeemers) {
319
+ const keys = mapRedeemers.keys();
320
+ for (let index = 0; index < keys.len(); index++) {
321
+ const key = keys.get(index);
322
+ const value = mapRedeemers.get(key);
323
+ if (!value) continue;
324
+ const tag = key.tag();
325
+ const redeemerIndex = key.index();
326
+ entries.push({
327
+ tag,
328
+ index: redeemerIndex,
329
+ data: value.data(),
330
+ exUnits: value.ex_units(),
331
+ sortKey: redeemerKeyBytes(tag, redeemerIndex)
332
+ });
333
+ }
334
+ }
335
+ return entries.sort(
336
+ (left, right) => compareBytes(left.sortKey, right.sortKey)
337
+ );
338
+ };
339
+ var redeemerKeyBytes = (tag, index) => CML.RedeemerKey.new(tag, index).to_canonical_cbor_bytes();
340
+ var compareBytes = (left, right) => {
341
+ if (left.length !== right.length) return left.length - right.length;
342
+ for (let index = 0; index < left.length; index++) {
343
+ if (left[index] !== right[index]) return left[index] - right[index];
344
+ }
345
+ return 0;
346
+ };
347
+ var cborHex = (value) => {
348
+ try {
349
+ return value.to_cbor_hex?.();
350
+ } catch {
351
+ return void 0;
352
+ }
353
+ };
354
+
355
+ // src/resolve.ts
356
+ var import_utils2 = require("@lucid-evolution/utils");
357
+ var createResolutionCache = () => {
358
+ const producedUtxos = /* @__PURE__ */ new Map();
359
+ const knownUtxos = /* @__PURE__ */ new Map();
360
+ const addProducedUtxos = (utxos) => {
361
+ for (const utxo of utxos) {
362
+ producedUtxos.set(outRefKey(utxo), normalizeTraceUtxo(utxo));
363
+ }
364
+ };
365
+ const addResolvedUtxos = (utxos) => {
366
+ for (const utxo of utxos) {
367
+ const traceUtxo = normalizeResolvedUtxo(utxo);
368
+ knownUtxos.set(outRefKey(traceUtxo), traceUtxo);
369
+ }
370
+ };
371
+ return {
372
+ addProducedUtxos,
373
+ addTransactionOutputs: (transaction) => addProducedUtxos(producedUtxosFromTransaction(transaction)),
374
+ addResolvedUtxos,
375
+ getProducedUtxo: (outRef) => producedUtxos.get(outRefKey(outRef)),
376
+ getKnownUtxo: (outRef) => knownUtxos.get(outRefKey(outRef)),
377
+ resolveOutRefs: (outRefs, options = {}) => resolveOutRefs(outRefs, {
378
+ ...options,
379
+ producedUtxos,
380
+ knownUtxos
381
+ })
382
+ };
383
+ };
384
+ var outRefKey = (outRef) => `${outRef.txHash}#${outRef.outputIndex}`;
385
+ var parseOutRefKey = (key) => {
386
+ const separator = key.lastIndexOf("#");
387
+ if (separator < 1) throw new Error(`Invalid out ref key: ${key}`);
388
+ const outputIndex = Number(key.slice(separator + 1));
389
+ if (!Number.isInteger(outputIndex) || outputIndex < 0) {
390
+ throw new Error(`Invalid out ref key: ${key}`);
391
+ }
392
+ return {
393
+ txHash: key.slice(0, separator),
394
+ outputIndex
395
+ };
396
+ };
397
+ var producedUtxosFromTransaction = (transaction) => transaction.outputs.map((utxo) => normalizeTraceUtxo(utxo));
398
+ var toTraceUtxo = (utxo, options = {}) => ({
399
+ txHash: utxo.txHash,
400
+ outputIndex: utxo.outputIndex,
401
+ ...toTraceTxOutput(utxo),
402
+ resolution: options.resolution ?? "resolved",
403
+ unresolvedReason: options.unresolvedReason,
404
+ tags: [...options.tags ?? []]
405
+ });
406
+ var toTraceTxOutput = (output) => {
407
+ const credentials = credentialsOf2(output.address);
408
+ return {
409
+ address: output.address,
410
+ paymentCredential: credentials.paymentCredential,
411
+ stakeCredential: credentials.stakeCredential,
412
+ assets: traceAssets2(output.assets),
413
+ datumHash: output.datumHash ?? void 0,
414
+ datum: output.datum ?? void 0,
415
+ scriptRef: output.scriptRef ? traceScriptRef2(output.scriptRef) : void 0
416
+ };
417
+ };
418
+ var unresolvedUtxo = (outRef, reason) => ({
419
+ txHash: outRef.txHash,
420
+ outputIndex: outRef.outputIndex,
421
+ address: "unresolved",
422
+ assets: {},
423
+ resolution: "unresolved",
424
+ unresolvedReason: reason,
425
+ tags: []
426
+ });
427
+ var genesisUtxo = (outRef) => ({
428
+ txHash: outRef.txHash,
429
+ outputIndex: outRef.outputIndex,
430
+ address: "genesis",
431
+ assets: {},
432
+ resolution: "genesis",
433
+ tags: ["genesis"]
434
+ });
435
+ var resolveOutRefs = async (outRefs, options) => {
436
+ const requested = uniqueOutRefs(outRefs);
437
+ const requestedKeys = new Set(requested.map(outRefKey));
438
+ const resolved = /* @__PURE__ */ new Map();
439
+ const sources = {};
440
+ const missReasons = /* @__PURE__ */ new Map();
441
+ const warnings = [];
442
+ const recordResolved = (utxo, source) => {
443
+ const traceUtxo = source === "genesis" ? normalizeGenesisUtxo(utxo) : normalizeResolvedUtxo(utxo);
444
+ const key = outRefKey(traceUtxo);
445
+ if (!requestedKeys.has(key) || resolved.has(key)) return;
446
+ resolved.set(key, traceUtxo);
447
+ sources[key] = source;
448
+ missReasons.delete(key);
449
+ };
450
+ for (const outRef of requested) {
451
+ if (!isGenesisOutRef(outRef)) continue;
452
+ const knownGenesisUtxo = options.knownUtxos?.get(outRefKey(outRef));
453
+ recordResolved(knownGenesisUtxo ?? genesisUtxo(outRef), "genesis");
454
+ }
455
+ for (const outRef of requested) {
456
+ const utxo = options.producedUtxos?.get(outRefKey(outRef));
457
+ if (utxo) recordResolved(utxo, "scenario-cache");
458
+ }
459
+ let missing = unresolvedOutRefs(requested, resolved);
460
+ if (missing.length > 0) {
461
+ if (options.provider) {
462
+ try {
463
+ const providerUtxos = await options.provider.getUtxosByOutRef(
464
+ missing.map(toProviderOutRef)
465
+ );
466
+ for (const utxo of providerUtxos) {
467
+ recordResolved(utxo, "provider");
468
+ }
469
+ for (const outRef of unresolvedOutRefs(missing, resolved)) {
470
+ missReasons.set(outRefKey(outRef), "provider-miss");
471
+ }
472
+ } catch (error) {
473
+ const message = errorMessage(error);
474
+ warnings.push({
475
+ code: "provider-resolution-error",
476
+ message
477
+ });
478
+ for (const outRef of missing) {
479
+ missReasons.set(outRefKey(outRef), "provider-error");
480
+ }
481
+ }
482
+ } else {
483
+ for (const outRef of missing) {
484
+ missReasons.set(outRefKey(outRef), "missing-provider");
485
+ }
486
+ }
487
+ }
488
+ missing = unresolvedOutRefs(requested, resolved);
489
+ for (const outRef of missing) {
490
+ const utxo = options.knownUtxos?.get(outRefKey(outRef));
491
+ if (utxo) recordResolved(utxo, "known-utxos");
492
+ }
493
+ missing = unresolvedOutRefs(requested, resolved);
494
+ if (missing.length > 0 && options.resolver) {
495
+ try {
496
+ const resolverUtxos = await options.resolver([...missing]);
497
+ for (const utxo of resolverUtxos) {
498
+ recordResolved(utxo, "resolver");
499
+ }
500
+ for (const outRef of unresolvedOutRefs(missing, resolved)) {
501
+ missReasons.set(outRefKey(outRef), "resolver-miss");
502
+ }
503
+ } catch (error) {
504
+ const message = errorMessage(error);
505
+ warnings.push({
506
+ code: "resolver-resolution-error",
507
+ message
508
+ });
509
+ for (const outRef of missing) {
510
+ missReasons.set(outRefKey(outRef), "resolver-error");
511
+ }
512
+ }
513
+ }
514
+ const utxos = outRefs.map((outRef) => {
515
+ const key = outRefKey(outRef);
516
+ const utxo = resolved.get(key);
517
+ if (utxo) return utxo;
518
+ sources[key] = "unresolved";
519
+ return unresolvedUtxo(outRef, missReasons.get(key) ?? "unresolved");
520
+ });
521
+ return { utxos, sources, warnings };
522
+ };
523
+ var uniqueOutRefs = (outRefs) => {
524
+ const seen = /* @__PURE__ */ new Set();
525
+ const result = [];
526
+ for (const outRef of outRefs) {
527
+ const key = outRefKey(outRef);
528
+ if (seen.has(key)) continue;
529
+ seen.add(key);
530
+ result.push(outRef);
531
+ }
532
+ return result;
533
+ };
534
+ var unresolvedOutRefs = (outRefs, resolved) => outRefs.filter((outRef) => !resolved.has(outRefKey(outRef)));
535
+ var toProviderOutRef = (outRef) => ({
536
+ txHash: outRef.txHash,
537
+ outputIndex: outRef.outputIndex
538
+ });
539
+ var isGenesisOutRef = (outRef) => /^0{64}$/.test(outRef.txHash);
540
+ var normalizeResolvedUtxo = (utxo) => {
541
+ const traceUtxo = isTraceUtxo(utxo) ? normalizeTraceUtxo({ ...utxo, resolution: "resolved" }) : toTraceUtxo(utxo);
542
+ return isGenesisOutRef(traceUtxo) ? normalizeGenesisUtxo(traceUtxo) : traceUtxo;
543
+ };
544
+ var normalizeGenesisUtxo = (utxo) => {
545
+ const traceUtxo = isTraceUtxo(utxo) ? normalizeTraceUtxo(utxo) : toTraceUtxo(utxo);
546
+ return normalizeTraceUtxo({
547
+ ...traceUtxo,
548
+ resolution: "genesis",
549
+ unresolvedReason: void 0,
550
+ tags: [.../* @__PURE__ */ new Set(["genesis", ...traceUtxo.tags])]
551
+ });
552
+ };
553
+ var normalizeTraceUtxo = (utxo) => ({
554
+ ...utxo,
555
+ assets: sortedTraceAssets(utxo.assets),
556
+ tags: [...utxo.tags].sort()
557
+ });
558
+ var isTraceUtxo = (utxo) => "resolution" in utxo && "tags" in utxo;
559
+ var credentialsOf2 = (address) => {
560
+ try {
561
+ const { paymentCredential, stakeCredential } = (0, import_utils2.getAddressDetails)(address);
562
+ return {
563
+ paymentCredential: paymentCredential ?? void 0,
564
+ stakeCredential: stakeCredential ?? void 0
565
+ };
566
+ } catch {
567
+ return {};
568
+ }
569
+ };
570
+ var traceAssets2 = (assets) => {
571
+ const result = {};
572
+ for (const unit of Object.keys(assets).sort()) {
573
+ result[unit] = assets[unit].toString();
574
+ }
575
+ return result;
576
+ };
577
+ var sortedTraceAssets = (assets) => {
578
+ const result = {};
579
+ for (const unit of Object.keys(assets).sort()) {
580
+ result[unit] = assets[unit];
581
+ }
582
+ return result;
583
+ };
584
+ var traceScriptRef2 = (script) => ({
585
+ type: script.type,
586
+ hash: scriptHash2(script),
587
+ sizeBytes: script.script.length / 2
588
+ });
589
+ var scriptHash2 = (script) => {
590
+ try {
591
+ switch (script.type) {
592
+ case "Native":
593
+ return CML.NativeScript.from_cbor_hex(script.script).hash().to_hex();
594
+ case "PlutusV1":
595
+ return CML.PlutusScript.from_v1(
596
+ CML.PlutusV1Script.from_cbor_hex(script.script)
597
+ ).hash().to_hex();
598
+ case "PlutusV2":
599
+ return CML.PlutusScript.from_v2(
600
+ CML.PlutusV2Script.from_cbor_hex(script.script)
601
+ ).hash().to_hex();
602
+ case "PlutusV3":
603
+ return CML.PlutusScript.from_v3(
604
+ CML.PlutusV3Script.from_cbor_hex(script.script)
605
+ ).hash().to_hex();
606
+ }
607
+ } catch {
608
+ return void 0;
609
+ }
610
+ };
611
+ var errorMessage = (error) => error instanceof Error ? error.message : String(error);
612
+
613
+ // src/render/common.ts
614
+ var buildRenderGraph = (trace) => {
615
+ const nodes = /* @__PURE__ */ new Map();
616
+ for (const transaction of trace.transactions) {
617
+ nodes.set(txNodeId(transaction.hash), {
618
+ id: txNodeId(transaction.hash),
619
+ kind: "transaction",
620
+ label: transactionLabel(transaction, trace)
621
+ });
622
+ }
623
+ for (const [key, utxo] of Object.entries(trace.utxos).sort(
624
+ ([a], [b]) => a.localeCompare(b)
625
+ )) {
626
+ nodes.set(utxoNodeId(utxo), {
627
+ id: utxoNodeId(utxo),
628
+ kind: "utxo",
629
+ label: utxoLabel(key, utxo, trace),
630
+ unresolved: utxo.resolution === "unresolved",
631
+ genesis: utxo.resolution === "genesis"
632
+ });
633
+ }
634
+ for (const edge of sortedEdges(trace.edges)) {
635
+ ensureEndpointNode(nodes, edge.from, trace);
636
+ ensureEndpointNode(nodes, edge.to, trace);
637
+ }
638
+ return {
639
+ nodes: [...nodes.values()].sort(
640
+ (left, right) => left.id.localeCompare(right.id)
641
+ ),
642
+ edges: sortedEdges(trace.edges)
643
+ };
644
+ };
645
+ var sortedEdges = (edges) => [...edges].sort(
646
+ (left, right) => edgeSortKey(left).localeCompare(edgeSortKey(right))
647
+ );
648
+ var edgeStyle = (kind) => {
649
+ switch (kind) {
650
+ case "spends":
651
+ return { label: "spends", color: "#374151", style: "solid" };
652
+ case "reads":
653
+ return { label: "reads", color: "#2563eb", style: "dashed" };
654
+ case "collateral":
655
+ return { label: "collateral", color: "#d97706", style: "dashed" };
656
+ case "collateralReturn":
657
+ return { label: "collateral return", color: "#f59e0b", style: "solid" };
658
+ case "produces":
659
+ return { label: "produces", color: "#15803d", style: "solid" };
660
+ case "mints":
661
+ return { label: "mints", color: "#7c3aed", style: "solid" };
662
+ case "burns":
663
+ return { label: "burns", color: "#dc2626", style: "solid" };
664
+ case "withdraws":
665
+ return { label: "withdraws", color: "#0891b2", style: "solid" };
666
+ case "certifies":
667
+ return { label: "certifies", color: "#4f46e5", style: "solid" };
668
+ case "requiresSigner":
669
+ return { label: "requires signer", color: "#6b7280", style: "dotted" };
670
+ }
671
+ };
672
+ var txNodeId = (txHash) => `tx:${txHash}`;
673
+ var utxoNodeId = (outRef) => `utxo:${outRefKey(outRef)}`;
674
+ var shortHash = (value, size = 8) => value.length <= size * 2 ? value : `${value.slice(0, size)}...${value.slice(-size)}`;
675
+ var assetName = (unit, trace) => {
676
+ const alias = trace.aliases.assets[unit];
677
+ if (alias) return alias;
678
+ if (unit === "lovelace") return "lovelace";
679
+ return shortHash(unit, 6);
680
+ };
681
+ var addressName = (address, trace) => trace.aliases.addresses[address] ?? shortHash(address, 10);
682
+ var formatAssets = (assets, trace, options = {}) => {
683
+ const units = Object.keys(assets).sort(assetUnitSort);
684
+ const maxAssets = options.maxAssets ?? 4;
685
+ const visible = units.slice(0, maxAssets);
686
+ const rendered = visible.map(
687
+ (unit) => `${assets[unit]} ${assetName(unit, trace)}`
688
+ );
689
+ if (units.length > visible.length) {
690
+ rendered.push(`+${units.length - visible.length} more`);
691
+ }
692
+ return rendered.length > 0 ? rendered.join("\\n") : "no assets";
693
+ };
694
+ var transactionLabel = (transaction, trace) => {
695
+ const lines = [
696
+ transaction.label ?? `tx ${shortHash(transaction.hash)}`,
697
+ `status: ${transaction.status}`,
698
+ `fee: ${transaction.fee}`
699
+ ];
700
+ if (Object.keys(transaction.mintedAssets).length > 0) {
701
+ lines.push(`mint: ${formatAssets(transaction.mintedAssets, trace)}`);
702
+ }
703
+ if (Object.keys(transaction.burnedAssets).length > 0) {
704
+ lines.push(`burn: ${formatAssets(transaction.burnedAssets, trace)}`);
705
+ }
706
+ const warningCount = trace.warnings.filter(
707
+ (warning) => warning.txHash === transaction.hash
708
+ ).length;
709
+ if (warningCount > 0) lines.push(`warnings: ${warningCount}`);
710
+ return lines.join("\\n");
711
+ };
712
+ var utxoLabel = (key, utxo, trace) => {
713
+ if (utxo.resolution === "genesis") {
714
+ const lines2 = ["genesis UTxO", key];
715
+ if (Object.keys(utxo.assets).length > 0) {
716
+ lines2.push(formatAssets(utxo.assets, trace));
717
+ }
718
+ lines2.push("external funding source");
719
+ return lines2.join("\\n");
720
+ }
721
+ const lines = [
722
+ utxo.tags.length > 0 ? `[${utxo.tags.join(", ")}]` : key,
723
+ formatAssets(utxo.assets, trace),
724
+ addressName(utxo.address, trace)
725
+ ];
726
+ if (utxo.datum) {
727
+ lines.push("inline datum");
728
+ } else if (utxo.datumHash) {
729
+ lines.push(`datum ${shortHash(utxo.datumHash)}`);
730
+ }
731
+ if (utxo.scriptRef) {
732
+ lines.push(
733
+ `script ref ${utxo.scriptRef.type}${utxo.scriptRef.hash ? ` ${shortHash(utxo.scriptRef.hash)}` : ""}`
734
+ );
735
+ }
736
+ if (utxo.resolution === "unresolved") {
737
+ lines.push(`unresolved: ${utxo.unresolvedReason ?? "unknown"}`);
738
+ }
739
+ return lines.join("\\n");
740
+ };
741
+ var externalNodeLabel = (nodeId, trace) => {
742
+ if (nodeId.startsWith("asset:")) {
743
+ return assetName(nodeId.slice("asset:".length), trace);
744
+ }
745
+ if (nodeId.startsWith("withdrawal:")) {
746
+ return `withdrawal\\n${addressName(nodeId.slice("withdrawal:".length), trace)}`;
747
+ }
748
+ if (nodeId.startsWith("signer:")) {
749
+ return `signer\\n${shortHash(nodeId.slice("signer:".length))}`;
750
+ }
751
+ if (nodeId.startsWith("collateral-return:")) {
752
+ return `collateral return\\n${shortHash(
753
+ nodeId.slice("collateral-return:".length)
754
+ )}`;
755
+ }
756
+ if (nodeId.startsWith("certificate:")) {
757
+ const { txHash, index } = parseIndexedNode(
758
+ nodeId.slice("certificate:".length)
759
+ );
760
+ const certificate = trace.transactions.find((transaction) => transaction.hash === txHash)?.certificates.find((candidate) => candidate.index === index);
761
+ return certificate ? `certificate\\n${certificate.kindName}` : `certificate\\n${shortHash(txHash)}#${index}`;
762
+ }
763
+ return nodeId;
764
+ };
765
+ var ensureEndpointNode = (nodes, nodeId, trace) => {
766
+ if (nodes.has(nodeId)) return;
767
+ nodes.set(nodeId, {
768
+ id: nodeId,
769
+ kind: externalNodeKind(nodeId),
770
+ label: externalNodeLabel(nodeId, trace)
771
+ });
772
+ };
773
+ var externalNodeKind = (nodeId) => {
774
+ if (nodeId.startsWith("asset:")) return "asset";
775
+ if (nodeId.startsWith("withdrawal:")) return "withdrawal";
776
+ if (nodeId.startsWith("certificate:")) return "certificate";
777
+ if (nodeId.startsWith("signer:")) return "signer";
778
+ if (nodeId.startsWith("collateral-return:")) return "collateralReturn";
779
+ return "external";
780
+ };
781
+ var edgeSortKey = (edge) => `${edge.from}\0${edge.kind}\0${edge.to}`;
782
+ var assetUnitSort = (left, right) => {
783
+ if (left === "lovelace") return -1;
784
+ if (right === "lovelace") return 1;
785
+ return left.localeCompare(right);
786
+ };
787
+ var parseIndexedNode = (value) => {
788
+ const separator = value.lastIndexOf("#");
789
+ return {
790
+ txHash: separator >= 0 ? value.slice(0, separator) : value,
791
+ index: separator >= 0 ? Number(value.slice(separator + 1)) : 0
792
+ };
793
+ };
794
+
795
+ // src/render/options.ts
796
+ var resolveVisualRendererOptions = (options = {}) => {
797
+ const mode = options.mode ?? "overview";
798
+ return {
799
+ mode,
800
+ view: options.view ?? "flow",
801
+ privacy: { ...privacyByMode[mode], ...options.privacy },
802
+ budget: { ...budgetByMode[mode], ...options.budget }
803
+ };
804
+ };
805
+ var visualEdgeStyle = (kind) => {
806
+ switch (kind) {
807
+ case "spend":
808
+ return { color: "#374151", style: "solid" };
809
+ case "read":
810
+ return { color: "#2563eb", style: "dashed" };
811
+ case "collateral":
812
+ return { color: "#d97706", style: "dashed" };
813
+ case "collateralReturn":
814
+ return { color: "#f59e0b", style: "solid" };
815
+ case "produce":
816
+ return { color: "#15803d", style: "solid" };
817
+ case "mint":
818
+ return { color: "#7c3aed", style: "solid" };
819
+ case "burn":
820
+ return { color: "#dc2626", style: "solid" };
821
+ case "withdraw":
822
+ return { color: "#0891b2", style: "solid" };
823
+ case "certify":
824
+ return { color: "#4f46e5", style: "solid" };
825
+ case "sign":
826
+ return { color: "#6b7280", style: "dotted" };
827
+ case "diagnostic":
828
+ return { color: "#dc2626", style: "dashed" };
829
+ case "summary":
830
+ return { color: "#6b7280", style: "dashed" };
831
+ default:
832
+ return { color: "#6b7280", style: "solid" };
833
+ }
834
+ };
835
+ var privacyByMode = {
836
+ overview: {
837
+ hash: "short",
838
+ address: "alias",
839
+ datum: "marker",
840
+ redeemer: "label",
841
+ cbor: "hidden"
842
+ },
843
+ audit: {
844
+ hash: "short",
845
+ address: "alias",
846
+ datum: "hash",
847
+ redeemer: "constructor",
848
+ cbor: "prefix"
849
+ },
850
+ debug: {
851
+ hash: "full",
852
+ address: "full",
853
+ datum: "full",
854
+ redeemer: "full",
855
+ cbor: "prefix"
856
+ }
857
+ };
858
+ var budgetByMode = {
859
+ overview: {
860
+ maxVisibleInputs: 4,
861
+ maxVisibleOutputs: 4,
862
+ maxVisibleAssetsPerUtxo: 4,
863
+ maxVisibleSigners: 3,
864
+ maxVisibleWarnings: 3,
865
+ maxVisibleRedeemerFields: 2
866
+ },
867
+ audit: {
868
+ maxVisibleInputs: 12,
869
+ maxVisibleOutputs: 12,
870
+ maxVisibleAssetsPerUtxo: 12,
871
+ maxVisibleSigners: 8,
872
+ maxVisibleWarnings: 8,
873
+ maxVisibleRedeemerFields: 6
874
+ },
875
+ debug: {
876
+ maxVisibleInputs: Number.POSITIVE_INFINITY,
877
+ maxVisibleOutputs: Number.POSITIVE_INFINITY,
878
+ maxVisibleAssetsPerUtxo: Number.POSITIVE_INFINITY,
879
+ maxVisibleSigners: Number.POSITIVE_INFINITY,
880
+ maxVisibleWarnings: Number.POSITIVE_INFINITY,
881
+ maxVisibleRedeemerFields: Number.POSITIVE_INFINITY
882
+ }
883
+ };
884
+
885
+ // src/render/semantic-format.ts
886
+ var formatSemanticGraph = (graph, options) => {
887
+ const nodes = new Map(graph.nodes.map((node) => [node.id, node]));
888
+ const edges = [...graph.edges];
889
+ applyViewAnnotations(options, nodes);
890
+ addDiagnosticNodes(graph, options, nodes, edges);
891
+ addBudgetSummaries(options, nodes, edges);
892
+ const connectedNodeIds = /* @__PURE__ */ new Set();
893
+ for (const edge of edges) {
894
+ connectedNodeIds.add(edge.from);
895
+ connectedNodeIds.add(edge.to);
896
+ }
897
+ return {
898
+ nodes: [...nodes.values()].filter(
899
+ (node) => node.kind === "transaction" || node.kind === "diagnostic" || connectedNodeIds.has(node.id)
900
+ ).sort((left, right) => left.id.localeCompare(right.id)),
901
+ edges: edges.sort((left, right) => left.id.localeCompare(right.id))
902
+ };
903
+ };
904
+ var semanticEdgeLabel = (trace, edge, options) => {
905
+ if (!edge.redeemerKey || !edge.action) return edge.label;
906
+ const base = `${edge.redeemerKey.tag} #${edge.redeemerKey.index}`;
907
+ if (options.privacy.redeemer === "hidden") return base;
908
+ if (options.mode === "overview" || options.privacy.redeemer === "label") {
909
+ return edge.action.label;
910
+ }
911
+ const redeemer = findRedeemer(trace, edge);
912
+ const parts = [base];
913
+ if (edge.action.label !== base) parts.push(edge.action.label);
914
+ parts.push(`redeemer list #${edge.redeemerKey.redeemerListIndex}`);
915
+ if (redeemer)
916
+ parts.push(`ex ${redeemer.exUnits.mem}/${redeemer.exUnits.steps}`);
917
+ if (options.mode === "debug" || options.privacy.redeemer === "constructor" || options.privacy.redeemer === "full") {
918
+ parts.push(
919
+ ...Object.entries(edge.action.fields ?? {}).slice(0, options.budget.maxVisibleRedeemerFields).map(([label, value]) => `${label}: ${value}`)
920
+ );
921
+ }
922
+ if (options.mode === "debug") {
923
+ parts.push(`${edge.action.source}/${edge.action.confidence}`);
924
+ }
925
+ return parts.join(" \xB7 ");
926
+ };
927
+ var semanticChipLabels = (node, options) => {
928
+ if (node.rawRef.type === "assetPolicy") {
929
+ return [formatHash(node.rawRef.policyId, options)];
930
+ }
931
+ return node.chips.map((chip) => chip.label).filter((label) => node.kind !== "utxo" || label !== "resolved");
932
+ };
933
+ var visibleSemanticSections = (node, options) => {
934
+ if (options.mode !== "overview") return node.sections;
935
+ if (node.kind === "transaction") {
936
+ return node.sections.filter(
937
+ (section2) => ["facts", "inputs", "assets", "redeemers"].includes(section2.id)
938
+ );
939
+ }
940
+ return node.sections;
941
+ };
942
+ var visibleSemanticRows = (section2, _node, options) => {
943
+ const maxRows = section2.id === "assets" ? options.budget.maxVisibleAssetsPerUtxo : section2.id === "redeemers" ? options.budget.maxVisibleRedeemerFields : Number.POSITIVE_INFINITY;
944
+ const rows = section2.rows.slice(0, maxRows);
945
+ if (section2.rows.length <= rows.length) return rows;
946
+ return [
947
+ ...rows,
948
+ {
949
+ id: `${section2.id}:more`,
950
+ label: "More",
951
+ value: `+${section2.rows.length - rows.length} more`,
952
+ tone: "neutral"
953
+ }
954
+ ];
955
+ };
956
+ var semanticFieldValue = (row, options) => {
957
+ if (row.id.startsWith("asset:") && row.rawValue) {
958
+ const defaultShort = shortHash(row.rawValue, 6);
959
+ if (options.privacy.hash === "hidden" && (row.value === defaultShort || row.value === row.rawValue)) {
960
+ return "asset";
961
+ }
962
+ if (options.privacy.hash === "full" && row.value === defaultShort) {
963
+ return row.rawValue;
964
+ }
965
+ }
966
+ if (row.id === "address") {
967
+ switch (options.privacy.address) {
968
+ case "hidden":
969
+ return "hidden";
970
+ case "full":
971
+ return row.rawValue ?? row.value;
972
+ case "short":
973
+ return row.rawValue ? shortHash(row.rawValue, 10) : row.value;
974
+ case "alias":
975
+ return row.value;
976
+ }
977
+ }
978
+ if (row.id.startsWith("datum")) {
979
+ switch (options.privacy.datum) {
980
+ case "hidden":
981
+ return "hidden";
982
+ case "marker":
983
+ return "present";
984
+ case "full":
985
+ return row.rawValue ?? row.value;
986
+ default:
987
+ return row.value;
988
+ }
989
+ }
990
+ if (row.id === "keyHash" && row.rawValue) {
991
+ return formatHash(row.rawValue, options);
992
+ }
993
+ if (row.id === "scriptRef" && row.rawValue) {
994
+ if (options.privacy.hash === "hidden") {
995
+ return row.rawValue.split(" ")[0] ?? "script ref";
996
+ }
997
+ return options.privacy.hash === "full" ? row.rawValue : row.value;
998
+ }
999
+ return row.value;
1000
+ };
1001
+ var semanticFieldLabel = (row, options) => {
1002
+ if (row.id.startsWith("asset:") && row.rawLabel) {
1003
+ const defaultShort = shortHash(row.rawLabel, 6);
1004
+ if (options.privacy.hash === "hidden" && (row.label === defaultShort || row.label === row.rawLabel)) {
1005
+ return "asset";
1006
+ }
1007
+ if (options.privacy.hash === "full" && row.label === defaultShort) {
1008
+ return row.rawLabel;
1009
+ }
1010
+ }
1011
+ return row.label;
1012
+ };
1013
+ var semanticNodeTitle = (node, options) => {
1014
+ if (node.rawRef.type === "transaction" && node.title === `tx ${shortHash(node.rawRef.txHash)}`) {
1015
+ return `tx ${formatHash(node.rawRef.txHash, options)}`;
1016
+ }
1017
+ if (node.rawRef.type === "utxo" && node.title === outRefKey(node.rawRef.outRef)) {
1018
+ return `UTxO ${formatOutRef(node.rawRef.outRef, options)}`;
1019
+ }
1020
+ if (node.rawRef.type === "assetPolicy" && node.title.startsWith("policy ")) {
1021
+ return `policy ${formatHash(node.rawRef.policyId, options)}`;
1022
+ }
1023
+ return node.title;
1024
+ };
1025
+ var semanticNodeSubtitle = (node, options) => {
1026
+ if (options.privacy.hash === "hidden") return void 0;
1027
+ switch (node.rawRef.type) {
1028
+ case "transaction":
1029
+ return formatHash(node.rawRef.txHash, options);
1030
+ case "utxo":
1031
+ return formatOutRef(node.rawRef.outRef, options);
1032
+ case "assetPolicy":
1033
+ return formatHash(node.rawRef.policyId, options);
1034
+ case "signer":
1035
+ return formatHash(node.rawRef.keyHash, options);
1036
+ case "withdrawal":
1037
+ return formatAddress(node.rawRef.rewardAddress, node.subtitle, options);
1038
+ case "certificate":
1039
+ return `${formatHash(node.rawRef.txHash, options)}#${node.rawRef.index}`;
1040
+ case "collateralReturn":
1041
+ return formatHash(node.rawRef.txHash, options);
1042
+ default:
1043
+ return node.subtitle;
1044
+ }
1045
+ };
1046
+ var isGenesisNode = (node) => node.kind === "utxo" && node.chips.some((chip) => chip.label === "genesis");
1047
+ var isScriptNode = (node) => node.kind === "utxo" && (node.sections.some(
1048
+ (section2) => section2.rows.some(
1049
+ (row) => ["datum", "datumHash", "scriptRef"].includes(row.id)
1050
+ )
1051
+ ) || node.chips.some((chip) => /script|datum|state/i.test(chip.label)));
1052
+ var isScriptInteractionEdge = (edge, nodes) => ["spend", "read", "produce", "mint", "burn"].includes(edge.kind) && [edge.from, edge.to].some((id) => {
1053
+ const node = nodes.get(id);
1054
+ return node ? isScriptNode(node) : false;
1055
+ }) || edge.kind === "collateral";
1056
+ var addDiagnosticNodes = (graph, options, nodes, edges) => {
1057
+ const byTx = /* @__PURE__ */ new Map();
1058
+ for (const diagnostic of graph.diagnostics) {
1059
+ const key = diagnostic.txHash ?? "global";
1060
+ byTx.set(key, [...byTx.get(key) ?? [], diagnostic]);
1061
+ }
1062
+ for (const [txHash, diagnostics] of byTx) {
1063
+ const visible = diagnostics.slice(0, options.budget.maxVisibleWarnings);
1064
+ for (const diagnostic of visible) {
1065
+ addDiagnosticNode(options, diagnostic, nodes, edges);
1066
+ }
1067
+ if (diagnostics.length > visible.length) {
1068
+ const hiddenCount = diagnostics.length - visible.length;
1069
+ const node = summaryNode(
1070
+ `summary:${txHash}:diagnostics`,
1071
+ `+${hiddenCount} diagnostics`,
1072
+ "warnings",
1073
+ hiddenCount
1074
+ );
1075
+ nodes.set(node.id, node);
1076
+ if (txHash !== "global") {
1077
+ edges.push(summaryEdge(node.id, `tx:${txHash}`, txHash, "warnings"));
1078
+ }
1079
+ }
1080
+ }
1081
+ };
1082
+ var applyViewAnnotations = (options, nodes) => {
1083
+ if (options.view !== "scriptInteraction") return;
1084
+ for (const [id, node] of nodes) {
1085
+ if (node.kind !== "utxo" || !isScriptNode(node)) continue;
1086
+ nodes.set(id, {
1087
+ ...node,
1088
+ chips: [
1089
+ { label: "script interaction", tone: "accent" },
1090
+ ...node.chips.filter((chip) => chip.label !== "script interaction")
1091
+ ]
1092
+ });
1093
+ }
1094
+ };
1095
+ var addDiagnosticNode = (options, diagnostic, nodes, edges) => {
1096
+ const node = {
1097
+ id: `diagnostic:${diagnostic.id}`,
1098
+ kind: "diagnostic",
1099
+ title: diagnostic.code,
1100
+ sections: [
1101
+ {
1102
+ id: "diagnostic",
1103
+ title: "Diagnostic",
1104
+ rows: [
1105
+ {
1106
+ id: "message",
1107
+ label: "Message",
1108
+ value: diagnosticMessage(diagnostic, options)
1109
+ }
1110
+ ]
1111
+ }
1112
+ ],
1113
+ ports: [],
1114
+ chips: [{ label: diagnostic.severity, tone: "danger" }],
1115
+ severity: diagnostic.severity === "error" ? "error" : "warning",
1116
+ rawRef: { type: "diagnostic", code: diagnostic.code }
1117
+ };
1118
+ nodes.set(node.id, node);
1119
+ if (diagnostic.txHash) {
1120
+ edges.push({
1121
+ id: `diagnostic-edge:${diagnostic.id}`,
1122
+ kind: "diagnostic",
1123
+ from: node.id,
1124
+ to: `tx:${diagnostic.txHash}`,
1125
+ label: diagnostic.severity,
1126
+ targetRef: {
1127
+ type: "diagnostic",
1128
+ txHash: diagnostic.txHash,
1129
+ code: diagnostic.code
1130
+ },
1131
+ rawRef: { type: "diagnostic", code: diagnostic.code }
1132
+ });
1133
+ }
1134
+ };
1135
+ var addBudgetSummaries = (options, nodes, edges) => {
1136
+ const hidden = /* @__PURE__ */ new Set();
1137
+ for (const txHash of transactionHashes(nodes)) {
1138
+ hideExcessEdges(
1139
+ edges,
1140
+ txHash,
1141
+ "inputs",
1142
+ options.budget.maxVisibleInputs,
1143
+ (edge) => edge.targetRef.txHash === txHash && ["spend", "read", "collateral"].includes(edge.kind),
1144
+ hidden,
1145
+ nodes
1146
+ );
1147
+ hideExcessEdges(
1148
+ edges,
1149
+ txHash,
1150
+ "outputs",
1151
+ options.budget.maxVisibleOutputs,
1152
+ (edge) => edge.targetRef.txHash === txHash && edge.kind === "produce",
1153
+ hidden,
1154
+ nodes
1155
+ );
1156
+ hideExcessEdges(
1157
+ edges,
1158
+ txHash,
1159
+ "signers",
1160
+ options.budget.maxVisibleSigners,
1161
+ (edge) => edge.targetRef.txHash === txHash && edge.kind === "sign",
1162
+ hidden,
1163
+ nodes
1164
+ );
1165
+ }
1166
+ for (let index = edges.length - 1; index >= 0; index--) {
1167
+ if (hidden.has(edges[index].id)) edges.splice(index, 1);
1168
+ }
1169
+ };
1170
+ var hideExcessEdges = (edges, txHash, group, maxVisible, matches, hidden, nodes) => {
1171
+ const visibleLimit = Number.isFinite(maxVisible) ? maxVisible : edges.length;
1172
+ const candidates = edges.filter(matches).sort((left, right) => left.id.localeCompare(right.id));
1173
+ const extra = candidates.slice(visibleLimit);
1174
+ for (const edge of extra) hidden.add(edge.id);
1175
+ if (extra.length === 0) return;
1176
+ const node = summaryNode(
1177
+ `summary:${txHash}:${group}`,
1178
+ `+${extra.length} ${group}`,
1179
+ group,
1180
+ extra.length
1181
+ );
1182
+ nodes.set(node.id, node);
1183
+ edges.push(
1184
+ group === "outputs" ? summaryEdge(`tx:${txHash}`, node.id, txHash, group) : summaryEdge(node.id, `tx:${txHash}`, txHash, group)
1185
+ );
1186
+ };
1187
+ var summaryNode = (id, title, group, count) => ({
1188
+ id,
1189
+ kind: "diagnostic",
1190
+ title,
1191
+ sections: [
1192
+ {
1193
+ id: "summary",
1194
+ title: "Collapsed",
1195
+ rows: [{ id: "count", label: "Count", value: String(count) }]
1196
+ }
1197
+ ],
1198
+ ports: [],
1199
+ chips: [{ label: "collapsed", tone: "neutral" }],
1200
+ rawRef: { type: "diagnostic", code: "collapsed", count, group }
1201
+ });
1202
+ var summaryEdge = (from, to, txHash, group) => ({
1203
+ id: `summary-edge:${txHash}:${group}:${from}->${to}`,
1204
+ kind: "summary",
1205
+ from,
1206
+ to,
1207
+ label: group,
1208
+ targetRef: {
1209
+ type: "diagnostic",
1210
+ txHash,
1211
+ code: "collapsed",
1212
+ group
1213
+ },
1214
+ rawRef: { type: "diagnostic", code: "collapsed", group }
1215
+ });
1216
+ var transactionHashes = (nodes) => [...nodes.values()].filter(
1217
+ (node) => node.rawRef.type === "transaction"
1218
+ ).map((node) => node.rawRef.txHash).sort();
1219
+ var formatOutRef = (outRef, options) => {
1220
+ if (options.privacy.hash === "hidden") return `#${outRef.outputIndex}`;
1221
+ return `${formatHash(outRef.txHash, options)}#${outRef.outputIndex}`;
1222
+ };
1223
+ var formatHash = (hash, options) => options.privacy.hash === "full" ? hash : options.privacy.hash === "hidden" ? "hidden" : shortHash(hash);
1224
+ var formatAddress = (rawAddress, aliasOrShort, options) => {
1225
+ switch (options.privacy.address) {
1226
+ case "hidden":
1227
+ return void 0;
1228
+ case "full":
1229
+ return rawAddress;
1230
+ case "short":
1231
+ return shortHash(rawAddress, 10);
1232
+ case "alias":
1233
+ return aliasOrShort ?? shortHash(rawAddress, 10);
1234
+ }
1235
+ };
1236
+ var diagnosticMessage = (diagnostic, options) => {
1237
+ let message = diagnostic.message;
1238
+ if (diagnostic.outRef) {
1239
+ message = message.replace(
1240
+ outRefKey(diagnostic.outRef),
1241
+ formatOutRef(diagnostic.outRef, options)
1242
+ );
1243
+ }
1244
+ if (diagnostic.txHash) {
1245
+ message = message.replaceAll(
1246
+ diagnostic.txHash,
1247
+ formatHash(diagnostic.txHash, options)
1248
+ );
1249
+ }
1250
+ return message;
1251
+ };
1252
+ var findRedeemer = (trace, edge) => {
1253
+ const key = edge.redeemerKey;
1254
+ if (!key) return void 0;
1255
+ return trace.transactions.find((transaction) => transaction.hash === edge.targetRef.txHash)?.redeemers.find(
1256
+ (redeemer) => redeemer.tag === key.tag && redeemer.index === key.index && redeemer.redeemerListIndex === key.redeemerListIndex
1257
+ );
1258
+ };
1259
+
1260
+ // src/render/semantic.ts
1261
+ var buildSemanticRenderGraph = (trace, options = {}) => {
1262
+ const nodes = /* @__PURE__ */ new Map();
1263
+ const edges = [];
1264
+ const mappedRedeemers = /* @__PURE__ */ new Set();
1265
+ for (const [key, utxo] of Object.entries(trace.utxos).sort(
1266
+ ([left], [right]) => left.localeCompare(right)
1267
+ )) {
1268
+ nodes.set(utxoNodeId2(utxo), utxoNode(key, utxo, trace));
1269
+ }
1270
+ for (const transaction of trace.transactions) {
1271
+ nodes.set(
1272
+ transactionNodeId(transaction.hash),
1273
+ transactionNode(transaction, trace)
1274
+ );
1275
+ addTransactionEdges(trace, transaction, nodes, edges, mappedRedeemers);
1276
+ }
1277
+ const diagnostics = [
1278
+ ...trace.warnings.map(visualDiagnostic),
1279
+ ...unmappedRedeemerDiagnostics(trace, mappedRedeemers)
1280
+ ];
1281
+ return {
1282
+ version: 1,
1283
+ nodes: [...nodes.values()].sort(
1284
+ (left, right) => left.id.localeCompare(right.id)
1285
+ ),
1286
+ edges: edges.map((edge) => applyAction(trace, edge, options.redeemers ?? [])).sort((left, right) => left.id.localeCompare(right.id)),
1287
+ diagnostics,
1288
+ legend: defaultLegend,
1289
+ aliases: trace.aliases
1290
+ };
1291
+ };
1292
+ var labelRedeemer = (selector, label, intent) => (context) => {
1293
+ if (!matchesRedeemer(selector, context)) return void 0;
1294
+ return {
1295
+ label,
1296
+ ...intent ? { intent } : {},
1297
+ source: "user",
1298
+ confidence: "high"
1299
+ };
1300
+ };
1301
+ var describeRedeemerByConstructor = (selector, label, intent) => (context) => {
1302
+ const { redeemer } = context;
1303
+ if (!matchesRedeemer(selector, context)) return void 0;
1304
+ if (constructorOf(redeemer.data) !== selector.constructor) return void 0;
1305
+ return {
1306
+ label,
1307
+ ...intent ? { intent } : {},
1308
+ fields: { constructor: String(selector.constructor) },
1309
+ source: "constructor",
1310
+ confidence: "medium"
1311
+ };
1312
+ };
1313
+ var describeRedeemerWith = (describe) => (context) => describe(context);
1314
+ var addTransactionEdges = (trace, transaction, nodes, edges, mappedRedeemers) => {
1315
+ addInputEdges(
1316
+ trace,
1317
+ transaction,
1318
+ nodes,
1319
+ edges,
1320
+ mappedRedeemers,
1321
+ "spend",
1322
+ transaction.inputs
1323
+ );
1324
+ addInputEdges(
1325
+ trace,
1326
+ transaction,
1327
+ nodes,
1328
+ edges,
1329
+ mappedRedeemers,
1330
+ "read",
1331
+ transaction.referenceInputs
1332
+ );
1333
+ addInputEdges(
1334
+ trace,
1335
+ transaction,
1336
+ nodes,
1337
+ edges,
1338
+ mappedRedeemers,
1339
+ "collateral",
1340
+ transaction.collateralInputs
1341
+ );
1342
+ addOutputEdges(transaction, edges);
1343
+ addAssetPolicyEdges(
1344
+ trace,
1345
+ transaction,
1346
+ nodes,
1347
+ edges,
1348
+ mappedRedeemers,
1349
+ "mint"
1350
+ );
1351
+ addAssetPolicyEdges(
1352
+ trace,
1353
+ transaction,
1354
+ nodes,
1355
+ edges,
1356
+ mappedRedeemers,
1357
+ "burn"
1358
+ );
1359
+ addWithdrawalEdges(trace, transaction, nodes, edges, mappedRedeemers);
1360
+ addCertificateEdges(transaction, nodes, edges, mappedRedeemers);
1361
+ addSignerEdges(transaction, nodes, edges);
1362
+ addCollateralReturnEdge(transaction, nodes, edges);
1363
+ };
1364
+ var addInputEdges = (trace, transaction, nodes, edges, mappedRedeemers, kind, inputs) => {
1365
+ inputs.forEach((outRef, index) => {
1366
+ const inputKey = outRefKey(outRef);
1367
+ const from = utxoNodeId2(outRef);
1368
+ const to = transactionNodeId(transaction.hash);
1369
+ const redeemerKey = kind === "spend" ? redeemerKeyFor(transaction, "spend", index, mappedRedeemers) : void 0;
1370
+ ensureUtxoPlaceholder(trace, nodes, outRef);
1371
+ edges.push({
1372
+ id: `${to}:${kind}:${index}:${inputKey}`,
1373
+ kind,
1374
+ from,
1375
+ to,
1376
+ fromPort: utxoPortId(inputKey, "out"),
1377
+ toPort: transactionPortId(transaction.hash, kind, index),
1378
+ label: `${kind} #${index}`,
1379
+ ...redeemerKey ? { redeemerKey } : {},
1380
+ targetRef: {
1381
+ type: "input",
1382
+ inputKind: kind,
1383
+ txHash: transaction.hash,
1384
+ index,
1385
+ outRef
1386
+ },
1387
+ rawRef: { type: "utxo", outRef }
1388
+ });
1389
+ });
1390
+ };
1391
+ var addOutputEdges = (transaction, edges) => {
1392
+ transaction.outputs.forEach((output, index) => {
1393
+ const outputKey = outRefKey(output);
1394
+ edges.push({
1395
+ id: `${transactionNodeId(transaction.hash)}:produce:${index}`,
1396
+ kind: "produce",
1397
+ from: transactionNodeId(transaction.hash),
1398
+ to: utxoNodeId2(output),
1399
+ fromPort: transactionPortId(transaction.hash, "produce", index),
1400
+ toPort: utxoPortId(outputKey, "in"),
1401
+ label: `output #${index}`,
1402
+ targetRef: {
1403
+ type: "output",
1404
+ txHash: transaction.hash,
1405
+ index,
1406
+ outRef: {
1407
+ txHash: output.txHash,
1408
+ outputIndex: output.outputIndex
1409
+ }
1410
+ },
1411
+ rawRef: {
1412
+ type: "utxo",
1413
+ outRef: {
1414
+ txHash: output.txHash,
1415
+ outputIndex: output.outputIndex
1416
+ }
1417
+ }
1418
+ });
1419
+ });
1420
+ };
1421
+ var addAssetPolicyEdges = (trace, transaction, nodes, edges, mappedRedeemers, kind) => {
1422
+ const assets = kind === "mint" ? transaction.mintedAssets : transaction.burnedAssets;
1423
+ const byPolicy = assetsByPolicy(assets);
1424
+ const policyIds = sortedPolicyIds(transaction.mint);
1425
+ for (const [policyId, policyAssets] of Object.entries(byPolicy).sort(
1426
+ ([a], [b]) => a.localeCompare(b)
1427
+ )) {
1428
+ const policyIndex = policyIds.indexOf(policyId);
1429
+ const index = policyIndex >= 0 ? policyIndex : 0;
1430
+ const redeemerKey = redeemerKeyFor(
1431
+ transaction,
1432
+ "mint",
1433
+ index,
1434
+ mappedRedeemers
1435
+ );
1436
+ const assetNode = assetPolicyNode(policyId, policyAssets, trace);
1437
+ nodes.set(
1438
+ assetNode.id,
1439
+ mergeAssetPolicyNode(nodes.get(assetNode.id), assetNode)
1440
+ );
1441
+ const edge = {
1442
+ id: `${transactionNodeId(transaction.hash)}:${kind}:${index}:${policyId}`,
1443
+ kind,
1444
+ from: kind === "mint" ? transactionNodeId(transaction.hash) : assetPolicyNodeId(policyId),
1445
+ to: kind === "mint" ? assetPolicyNodeId(policyId) : transactionNodeId(transaction.hash),
1446
+ fromPort: kind === "mint" ? transactionPortId(transaction.hash, "mint", index) : assetPolicyPortId(policyId),
1447
+ toPort: kind === "mint" ? assetPolicyPortId(policyId) : transactionPortId(transaction.hash, "burn", index),
1448
+ label: `${kind} #${index}`,
1449
+ ...redeemerKey ? { redeemerKey } : {},
1450
+ targetRef: {
1451
+ type: "assetPolicy",
1452
+ txHash: transaction.hash,
1453
+ policyId,
1454
+ policyIndex: index,
1455
+ assets: policyAssets
1456
+ },
1457
+ rawRef: { type: "assetPolicy", policyId }
1458
+ };
1459
+ edges.push(edge);
1460
+ }
1461
+ };
1462
+ var addWithdrawalEdges = (trace, transaction, nodes, edges, mappedRedeemers) => {
1463
+ transaction.withdrawals.forEach((withdrawal, index) => {
1464
+ const node = withdrawalNode(withdrawal.rewardAddress, trace);
1465
+ const redeemerKey = redeemerKeyFor(
1466
+ transaction,
1467
+ "withdraw",
1468
+ index,
1469
+ mappedRedeemers
1470
+ );
1471
+ nodes.set(node.id, node);
1472
+ edges.push({
1473
+ id: `${transactionNodeId(transaction.hash)}:withdraw:${index}:${withdrawal.rewardAddress}`,
1474
+ kind: "withdraw",
1475
+ from: node.id,
1476
+ to: transactionNodeId(transaction.hash),
1477
+ fromPort: withdrawalPortId(withdrawal.rewardAddress),
1478
+ toPort: transactionPortId(transaction.hash, "withdraw", index),
1479
+ label: `withdraw #${index}`,
1480
+ ...redeemerKey ? { redeemerKey } : {},
1481
+ targetRef: {
1482
+ type: "withdrawal",
1483
+ txHash: transaction.hash,
1484
+ index,
1485
+ rewardAddress: withdrawal.rewardAddress
1486
+ },
1487
+ rawRef: { type: "withdrawal", rewardAddress: withdrawal.rewardAddress }
1488
+ });
1489
+ });
1490
+ };
1491
+ var addCertificateEdges = (transaction, nodes, edges, mappedRedeemers) => {
1492
+ transaction.certificates.forEach((certificate) => {
1493
+ const node = certificateNode(
1494
+ transaction.hash,
1495
+ certificate.index,
1496
+ certificate.kindName
1497
+ );
1498
+ const redeemerKey = redeemerKeyFor(
1499
+ transaction,
1500
+ "publish",
1501
+ certificate.index,
1502
+ mappedRedeemers
1503
+ );
1504
+ nodes.set(node.id, node);
1505
+ edges.push({
1506
+ id: `${transactionNodeId(transaction.hash)}:cert:${certificate.index}`,
1507
+ kind: "certify",
1508
+ from: transactionNodeId(transaction.hash),
1509
+ to: node.id,
1510
+ fromPort: transactionPortId(
1511
+ transaction.hash,
1512
+ "certify",
1513
+ certificate.index
1514
+ ),
1515
+ toPort: certificatePortId(transaction.hash, certificate.index),
1516
+ label: `cert #${certificate.index}`,
1517
+ ...redeemerKey ? { redeemerKey } : {},
1518
+ targetRef: {
1519
+ type: "certificate",
1520
+ txHash: transaction.hash,
1521
+ index: certificate.index
1522
+ },
1523
+ rawRef: {
1524
+ type: "certificate",
1525
+ txHash: transaction.hash,
1526
+ index: certificate.index
1527
+ }
1528
+ });
1529
+ });
1530
+ };
1531
+ var addSignerEdges = (transaction, nodes, edges) => {
1532
+ transaction.requiredSigners.forEach((keyHash, index) => {
1533
+ const node = signerNode(keyHash);
1534
+ nodes.set(node.id, node);
1535
+ edges.push({
1536
+ id: `${transactionNodeId(transaction.hash)}:signer:${keyHash}`,
1537
+ kind: "sign",
1538
+ from: node.id,
1539
+ to: transactionNodeId(transaction.hash),
1540
+ fromPort: signerPortId(keyHash),
1541
+ toPort: transactionPortId(transaction.hash, "sign", index),
1542
+ label: "requires signer",
1543
+ targetRef: {
1544
+ type: "signer",
1545
+ txHash: transaction.hash,
1546
+ keyHash
1547
+ },
1548
+ rawRef: { type: "signer", keyHash }
1549
+ });
1550
+ });
1551
+ };
1552
+ var addCollateralReturnEdge = (transaction, nodes, edges) => {
1553
+ if (!transaction.collateralReturn) return;
1554
+ const node = collateralReturnNode(transaction);
1555
+ nodes.set(node.id, node);
1556
+ edges.push({
1557
+ id: `${transactionNodeId(transaction.hash)}:collateral-return`,
1558
+ kind: "collateralReturn",
1559
+ from: transactionNodeId(transaction.hash),
1560
+ to: node.id,
1561
+ fromPort: transactionPortId(transaction.hash, "collateralReturn", 0),
1562
+ toPort: collateralReturnPortId(transaction.hash),
1563
+ label: "collateral return",
1564
+ targetRef: {
1565
+ type: "collateralReturn",
1566
+ txHash: transaction.hash
1567
+ },
1568
+ rawRef: { type: "collateralReturn", txHash: transaction.hash }
1569
+ });
1570
+ };
1571
+ var transactionNode = (transaction, trace) => {
1572
+ const warningCount = trace.warnings.filter(
1573
+ (warning) => warning.txHash === transaction.hash
1574
+ ).length;
1575
+ return {
1576
+ id: transactionNodeId(transaction.hash),
1577
+ kind: "transaction",
1578
+ title: transaction.label ?? `tx ${shortHash(transaction.hash)}`,
1579
+ subtitle: shortHash(transaction.hash),
1580
+ chips: [
1581
+ { label: transaction.status, tone: statusTone(transaction.status) },
1582
+ ...warningCount > 0 ? [{ label: `${warningCount} warnings`, tone: "warning" }] : []
1583
+ ],
1584
+ sections: [
1585
+ section("facts", "Facts", [
1586
+ field("fee", "Fee", transaction.fee),
1587
+ field("size", "Size", `${transaction.sizeBytes} bytes`),
1588
+ ...transaction.validityInterval.validFrom ? [
1589
+ field(
1590
+ "validFrom",
1591
+ "Valid from",
1592
+ transaction.validityInterval.validFrom
1593
+ )
1594
+ ] : [],
1595
+ ...transaction.validityInterval.validTo ? [field("validTo", "Valid to", transaction.validityInterval.validTo)] : []
1596
+ ]),
1597
+ section("inputs", "Inputs", [
1598
+ field("spend", "Spend", String(transaction.inputs.length)),
1599
+ field("read", "Read", String(transaction.referenceInputs.length)),
1600
+ field(
1601
+ "collateral",
1602
+ "Collateral",
1603
+ String(transaction.collateralInputs.length)
1604
+ )
1605
+ ]),
1606
+ section("assets", "Assets", [
1607
+ field(
1608
+ "mint",
1609
+ "Mint policies",
1610
+ String(sortedPolicyIds(transaction.mintedAssets).length)
1611
+ ),
1612
+ field(
1613
+ "burn",
1614
+ "Burn policies",
1615
+ String(sortedPolicyIds(transaction.burnedAssets).length)
1616
+ )
1617
+ ]),
1618
+ section("authority", "Authority", [
1619
+ field("signers", "Signers", String(transaction.requiredSigners.length)),
1620
+ field(
1621
+ "certificates",
1622
+ "Certificates",
1623
+ String(transaction.certificates.length)
1624
+ ),
1625
+ field(
1626
+ "withdrawals",
1627
+ "Withdrawals",
1628
+ String(transaction.withdrawals.length)
1629
+ )
1630
+ ]),
1631
+ section("redeemers", "Redeemers", [
1632
+ field("redeemers", "Redeemers", String(transaction.redeemers.length))
1633
+ ])
1634
+ ],
1635
+ ports: transactionPorts(transaction),
1636
+ ...transaction.status === "failed" ? { severity: "error" } : {},
1637
+ rawRef: { type: "transaction", txHash: transaction.hash }
1638
+ };
1639
+ };
1640
+ var utxoNode = (key, utxo, trace) => {
1641
+ const title = utxo.resolution === "genesis" ? "genesis UTxO" : utxo.tags.length > 0 ? utxo.tags.join(", ") : key;
1642
+ return {
1643
+ id: utxoNodeId2(utxo),
1644
+ kind: "utxo",
1645
+ title,
1646
+ subtitle: key,
1647
+ chips: [
1648
+ {
1649
+ label: utxo.resolution,
1650
+ tone: utxo.resolution === "unresolved" ? "danger" : utxo.resolution === "genesis" ? "warning" : "success"
1651
+ },
1652
+ ...utxo.tags.map((tag) => ({ label: tag, tone: "neutral" }))
1653
+ ],
1654
+ sections: [
1655
+ section("owner", "Owner", [
1656
+ field(
1657
+ "address",
1658
+ "Address",
1659
+ addressName(utxo.address, trace),
1660
+ true,
1661
+ void 0,
1662
+ utxo.address
1663
+ )
1664
+ ]),
1665
+ section(
1666
+ "assets",
1667
+ "Assets",
1668
+ Object.entries(utxo.assets).sort(([left], [right]) => assetSort(left, right)).map(
1669
+ ([unit, amount]) => field(
1670
+ `asset:${unit}`,
1671
+ assetName(unit, trace),
1672
+ amount,
1673
+ false,
1674
+ void 0,
1675
+ void 0,
1676
+ unit
1677
+ )
1678
+ )
1679
+ ),
1680
+ section("state", "State", [
1681
+ ...utxo.datum ? [field("datum", "Datum", "inline datum")] : [],
1682
+ ...utxo.datumHash ? [
1683
+ field(
1684
+ "datumHash",
1685
+ "Datum hash",
1686
+ shortHash(utxo.datumHash),
1687
+ true,
1688
+ void 0,
1689
+ utxo.datumHash
1690
+ )
1691
+ ] : [],
1692
+ ...utxo.scriptRef ? [
1693
+ field(
1694
+ "scriptRef",
1695
+ "Script ref",
1696
+ `${utxo.scriptRef.type}${utxo.scriptRef.hash ? ` ${shortHash(utxo.scriptRef.hash)}` : ""}`,
1697
+ false,
1698
+ void 0,
1699
+ utxo.scriptRef.hash ? `${utxo.scriptRef.type} ${utxo.scriptRef.hash}` : utxo.scriptRef.type
1700
+ )
1701
+ ] : [],
1702
+ ...utxo.unresolvedReason ? [
1703
+ field(
1704
+ "unresolved",
1705
+ "Unresolved",
1706
+ utxo.unresolvedReason,
1707
+ false,
1708
+ "danger"
1709
+ )
1710
+ ] : []
1711
+ ])
1712
+ ],
1713
+ ports: [
1714
+ port(utxoPortId(key, "in"), utxoNodeId2(utxo), "left", "in"),
1715
+ port(utxoPortId(key, "out"), utxoNodeId2(utxo), "right", "out")
1716
+ ],
1717
+ ...utxo.resolution === "unresolved" ? { severity: "error" } : {},
1718
+ rawRef: {
1719
+ type: "utxo",
1720
+ outRef: { txHash: utxo.txHash, outputIndex: utxo.outputIndex }
1721
+ }
1722
+ };
1723
+ };
1724
+ var assetPolicyNode = (policyId, assets, trace) => ({
1725
+ id: assetPolicyNodeId(policyId),
1726
+ kind: "asset",
1727
+ title: `policy ${shortHash(policyId, 6)}`,
1728
+ sections: [
1729
+ section(
1730
+ "assets",
1731
+ "Assets",
1732
+ Object.entries(assets).sort(([left], [right]) => assetSort(left, right)).map(
1733
+ ([unit]) => field(
1734
+ `asset:${unit}`,
1735
+ "Asset",
1736
+ assetName(unit, trace),
1737
+ false,
1738
+ void 0,
1739
+ unit
1740
+ )
1741
+ )
1742
+ )
1743
+ ],
1744
+ ports: [
1745
+ port(
1746
+ assetPolicyPortId(policyId),
1747
+ assetPolicyNodeId(policyId),
1748
+ "left",
1749
+ "asset"
1750
+ )
1751
+ ],
1752
+ chips: [{ label: shortHash(policyId, 6), tone: "accent" }],
1753
+ rawRef: { type: "assetPolicy", policyId }
1754
+ });
1755
+ var mergeAssetPolicyNode = (existing, next) => {
1756
+ if (!existing) return next;
1757
+ const rows = /* @__PURE__ */ new Map();
1758
+ for (const node of [existing, next]) {
1759
+ const assetRows = node.sections.find(
1760
+ (section2) => section2.id === "assets"
1761
+ )?.rows;
1762
+ for (const row of assetRows ?? []) {
1763
+ rows.set(row.id, row);
1764
+ }
1765
+ }
1766
+ return {
1767
+ ...existing,
1768
+ sections: [
1769
+ section(
1770
+ "assets",
1771
+ "Assets",
1772
+ [...rows.values()].sort(
1773
+ (left, right) => left.id.localeCompare(right.id)
1774
+ )
1775
+ )
1776
+ ]
1777
+ };
1778
+ };
1779
+ var withdrawalNode = (rewardAddress, trace) => ({
1780
+ id: withdrawalNodeId(rewardAddress),
1781
+ kind: "withdrawal",
1782
+ title: "withdrawal",
1783
+ subtitle: addressName(rewardAddress, trace),
1784
+ sections: [
1785
+ section("reward", "Reward address", [
1786
+ field(
1787
+ "address",
1788
+ "Address",
1789
+ addressName(rewardAddress, trace),
1790
+ true,
1791
+ void 0,
1792
+ rewardAddress
1793
+ )
1794
+ ])
1795
+ ],
1796
+ ports: [
1797
+ port(
1798
+ withdrawalPortId(rewardAddress),
1799
+ withdrawalNodeId(rewardAddress),
1800
+ "right",
1801
+ "withdraw"
1802
+ )
1803
+ ],
1804
+ chips: [],
1805
+ rawRef: { type: "withdrawal", rewardAddress }
1806
+ });
1807
+ var certificateNode = (txHash, index, kindName) => ({
1808
+ id: certificateNodeId(txHash, index),
1809
+ kind: "certificate",
1810
+ title: kindName,
1811
+ subtitle: `certificate #${index}`,
1812
+ sections: [
1813
+ section("certificate", "Certificate", [field("kind", "Kind", kindName)])
1814
+ ],
1815
+ ports: [
1816
+ port(
1817
+ certificatePortId(txHash, index),
1818
+ certificateNodeId(txHash, index),
1819
+ "left",
1820
+ "cert"
1821
+ )
1822
+ ],
1823
+ chips: [],
1824
+ rawRef: { type: "certificate", txHash, index }
1825
+ });
1826
+ var signerNode = (keyHash) => ({
1827
+ id: signerNodeId(keyHash),
1828
+ kind: "signer",
1829
+ title: "signer",
1830
+ subtitle: shortHash(keyHash),
1831
+ sections: [
1832
+ section("signer", "Signer", [
1833
+ field(
1834
+ "keyHash",
1835
+ "Key hash",
1836
+ shortHash(keyHash),
1837
+ true,
1838
+ void 0,
1839
+ keyHash
1840
+ )
1841
+ ])
1842
+ ],
1843
+ ports: [port(signerPortId(keyHash), signerNodeId(keyHash), "right", "sign")],
1844
+ chips: [],
1845
+ rawRef: { type: "signer", keyHash }
1846
+ });
1847
+ var collateralReturnNode = (transaction) => ({
1848
+ id: collateralReturnNodeId(transaction.hash),
1849
+ kind: "collateralReturn",
1850
+ title: "collateral return",
1851
+ subtitle: shortHash(transaction.hash),
1852
+ sections: [
1853
+ section("assets", "Assets", [
1854
+ ...Object.entries(transaction.collateralReturn?.assets ?? {}).sort(([left], [right]) => assetSort(left, right)).map(
1855
+ ([unit, amount]) => field(
1856
+ `asset:${unit}`,
1857
+ unit,
1858
+ amount,
1859
+ false,
1860
+ void 0,
1861
+ void 0,
1862
+ unit
1863
+ )
1864
+ )
1865
+ ])
1866
+ ],
1867
+ ports: [
1868
+ port(
1869
+ collateralReturnPortId(transaction.hash),
1870
+ collateralReturnNodeId(transaction.hash),
1871
+ "left",
1872
+ "collateral return"
1873
+ )
1874
+ ],
1875
+ chips: [],
1876
+ rawRef: { type: "collateralReturn", txHash: transaction.hash }
1877
+ });
1878
+ var ensureUtxoPlaceholder = (trace, nodes, outRef) => {
1879
+ const id = utxoNodeId2(outRef);
1880
+ if (nodes.has(id)) return;
1881
+ const key = outRefKey(outRef);
1882
+ nodes.set(
1883
+ id,
1884
+ utxoNode(
1885
+ key,
1886
+ {
1887
+ ...outRef,
1888
+ address: "unresolved",
1889
+ assets: {},
1890
+ resolution: "unresolved",
1891
+ unresolvedReason: "missing-from-trace",
1892
+ tags: []
1893
+ },
1894
+ trace
1895
+ )
1896
+ );
1897
+ };
1898
+ var transactionPorts = (transaction) => [
1899
+ ...transaction.inputs.map(
1900
+ (_input, index) => port(
1901
+ transactionPortId(transaction.hash, "spend", index),
1902
+ transactionNodeId(transaction.hash),
1903
+ "left",
1904
+ `spend #${index}`,
1905
+ "spend"
1906
+ )
1907
+ ),
1908
+ ...transaction.referenceInputs.map(
1909
+ (_input, index) => port(
1910
+ transactionPortId(transaction.hash, "read", index),
1911
+ transactionNodeId(transaction.hash),
1912
+ "left",
1913
+ `read #${index}`,
1914
+ "read"
1915
+ )
1916
+ ),
1917
+ ...transaction.collateralInputs.map(
1918
+ (_input, index) => port(
1919
+ transactionPortId(transaction.hash, "collateral", index),
1920
+ transactionNodeId(transaction.hash),
1921
+ "left",
1922
+ `collateral #${index}`,
1923
+ "collateral"
1924
+ )
1925
+ ),
1926
+ ...transaction.outputs.map(
1927
+ (_output, index) => port(
1928
+ transactionPortId(transaction.hash, "produce", index),
1929
+ transactionNodeId(transaction.hash),
1930
+ "right",
1931
+ `output #${index}`,
1932
+ "produce"
1933
+ )
1934
+ ),
1935
+ ...sortedPolicyIds(transaction.mintedAssets).map((policyId) => {
1936
+ const index = sortedPolicyIds(transaction.mint).indexOf(policyId);
1937
+ return port(
1938
+ transactionPortId(transaction.hash, "mint", index),
1939
+ transactionNodeId(transaction.hash),
1940
+ "bottom",
1941
+ `mint #${index}`,
1942
+ "mint"
1943
+ );
1944
+ }),
1945
+ ...sortedPolicyIds(transaction.burnedAssets).map((policyId) => {
1946
+ const index = sortedPolicyIds(transaction.mint).indexOf(policyId);
1947
+ return port(
1948
+ transactionPortId(transaction.hash, "burn", index),
1949
+ transactionNodeId(transaction.hash),
1950
+ "bottom",
1951
+ `burn #${index}`,
1952
+ "burn"
1953
+ );
1954
+ }),
1955
+ ...transaction.withdrawals.map(
1956
+ (_withdrawal, index) => port(
1957
+ transactionPortId(transaction.hash, "withdraw", index),
1958
+ transactionNodeId(transaction.hash),
1959
+ "left",
1960
+ `withdraw #${index}`,
1961
+ "withdraw"
1962
+ )
1963
+ ),
1964
+ ...transaction.certificates.map(
1965
+ (certificate) => port(
1966
+ transactionPortId(transaction.hash, "certify", certificate.index),
1967
+ transactionNodeId(transaction.hash),
1968
+ "right",
1969
+ `cert #${certificate.index}`,
1970
+ "certify"
1971
+ )
1972
+ ),
1973
+ ...transaction.requiredSigners.map(
1974
+ (_signer, index) => port(
1975
+ transactionPortId(transaction.hash, "sign", index),
1976
+ transactionNodeId(transaction.hash),
1977
+ "left",
1978
+ `signer #${index}`,
1979
+ "sign"
1980
+ )
1981
+ ),
1982
+ ...transaction.collateralReturn ? [
1983
+ port(
1984
+ transactionPortId(transaction.hash, "collateralReturn", 0),
1985
+ transactionNodeId(transaction.hash),
1986
+ "right",
1987
+ "collateral return",
1988
+ "collateralReturn"
1989
+ )
1990
+ ] : []
1991
+ ];
1992
+ var redeemerKeyFor = (transaction, tag, index, mappedRedeemers) => {
1993
+ const redeemer = transaction.redeemers.find(
1994
+ (candidate) => candidate.tag === tag && candidate.index === String(index)
1995
+ );
1996
+ if (!redeemer) return void 0;
1997
+ mappedRedeemers?.add(redeemerMapKey(transaction.hash, redeemer));
1998
+ return {
1999
+ tag: redeemer.tag,
2000
+ index: redeemer.index,
2001
+ redeemerListIndex: redeemer.redeemerListIndex
2002
+ };
2003
+ };
2004
+ var applyAction = (trace, edge, describers) => {
2005
+ const context = redeemerContext(trace, edge);
2006
+ if (!context) return edge;
2007
+ for (const describer of describers) {
2008
+ const action = describer(context);
2009
+ if (action) {
2010
+ return { ...edge, action, label: action.label };
2011
+ }
2012
+ }
2013
+ const fallback = genericAction(context.redeemer);
2014
+ return {
2015
+ ...edge,
2016
+ action: fallback,
2017
+ label: fallback.label
2018
+ };
2019
+ };
2020
+ var redeemerContext = (trace, edge) => {
2021
+ if (!edge.redeemerKey) return void 0;
2022
+ const transaction = trace.transactions.find(
2023
+ (candidate) => candidate.hash === edge.targetRef.txHash
2024
+ );
2025
+ const redeemer = transaction?.redeemers.find(
2026
+ (candidate) => candidate.tag === edge.redeemerKey?.tag && candidate.index === edge.redeemerKey.index && candidate.redeemerListIndex === edge.redeemerKey.redeemerListIndex
2027
+ );
2028
+ return transaction && redeemer ? {
2029
+ trace,
2030
+ transaction,
2031
+ redeemer,
2032
+ target: edge.targetRef,
2033
+ edge
2034
+ } : void 0;
2035
+ };
2036
+ var genericAction = (redeemer) => ({
2037
+ label: `${redeemer.tag} #${redeemer.index}`,
2038
+ source: "generic",
2039
+ confidence: "fallback"
2040
+ });
2041
+ var matchesRedeemer = (selector, { transaction, redeemer, target }) => redeemer.tag === selector.tag && redeemer.index === String(selector.index) && (!selector.txHash || transaction.hash === selector.txHash) && (!selector.target || selector.target(target));
2042
+ var constructorOf = (data) => {
2043
+ try {
2044
+ const alternative = CML.PlutusData.from_cbor_hex(data).as_constr_plutus_data()?.alternative();
2045
+ if (alternative === void 0) return void 0;
2046
+ const value = Number(alternative);
2047
+ return Number.isSafeInteger(value) ? value : void 0;
2048
+ } catch {
2049
+ return void 0;
2050
+ }
2051
+ };
2052
+ var visualDiagnostic = (warning, index) => ({
2053
+ id: `diagnostic:${index}:${warning.code}`,
2054
+ severity: warning.code.includes("failed") ? "error" : "warning",
2055
+ code: warning.code,
2056
+ message: warning.message,
2057
+ ...warning.txHash ? { txHash: warning.txHash } : {},
2058
+ ...warning.outRef ? { outRef: warning.outRef } : {}
2059
+ });
2060
+ var unmappedRedeemerDiagnostics = (trace, mappedRedeemers) => trace.transactions.flatMap(
2061
+ (transaction) => transaction.redeemers.filter(
2062
+ (redeemer) => !mappedRedeemers.has(redeemerMapKey(transaction.hash, redeemer))
2063
+ ).map((redeemer) => ({
2064
+ id: `diagnostic:${transaction.hash}:unmapped-redeemer:${redeemer.tag}:${redeemer.index}`,
2065
+ severity: "warning",
2066
+ code: "unmapped-redeemer",
2067
+ message: `Redeemer ${redeemer.tag} #${redeemer.index} in transaction ${transaction.hash} has no matching semantic edge`,
2068
+ txHash: transaction.hash,
2069
+ redeemerKey: {
2070
+ tag: redeemer.tag,
2071
+ index: redeemer.index,
2072
+ redeemerListIndex: redeemer.redeemerListIndex
2073
+ }
2074
+ }))
2075
+ );
2076
+ var redeemerMapKey = (txHash, redeemer) => `${txHash}\0${redeemer.tag}\0${redeemer.index}\0${redeemer.redeemerListIndex}`;
2077
+ var assetsByPolicy = (assets) => {
2078
+ const result = {};
2079
+ for (const [unit, amount] of Object.entries(assets)) {
2080
+ const policyId = policyIdOfUnit(unit);
2081
+ result[policyId] = {
2082
+ ...result[policyId] ?? {},
2083
+ [unit]: amount
2084
+ };
2085
+ }
2086
+ return result;
2087
+ };
2088
+ var sortedPolicyIds = (assets) => [...new Set(Object.keys(assets).map(policyIdOfUnit))].sort();
2089
+ var policyIdOfUnit = (unit) => unit === "lovelace" ? "lovelace" : unit.slice(0, 56);
2090
+ var section = (id, title, rows) => ({
2091
+ id,
2092
+ title,
2093
+ rows: [...rows],
2094
+ collapsed: rows.length === 0
2095
+ });
2096
+ var field = (id, label, value, mono = false, tone, rawValue, rawLabel) => ({
2097
+ id,
2098
+ label,
2099
+ ...rawLabel ? { rawLabel } : {},
2100
+ value,
2101
+ ...rawValue ? { rawValue } : {},
2102
+ ...mono ? { mono } : {},
2103
+ ...tone ? { tone } : {}
2104
+ });
2105
+ var port = (id, nodeId, side, label, edgeKind) => ({
2106
+ id,
2107
+ nodeId,
2108
+ side,
2109
+ label,
2110
+ ...edgeKind ? { edgeKind } : {}
2111
+ });
2112
+ var statusTone = (status) => {
2113
+ switch (status) {
2114
+ case "submitted":
2115
+ case "confirmed":
2116
+ return "success";
2117
+ case "failed":
2118
+ return "danger";
2119
+ case "built":
2120
+ case "signed":
2121
+ return "info";
2122
+ }
2123
+ };
2124
+ var assetSort = (left, right) => {
2125
+ if (left === "lovelace") return -1;
2126
+ if (right === "lovelace") return 1;
2127
+ return left.localeCompare(right);
2128
+ };
2129
+ var transactionNodeId = (txHash) => `tx:${txHash}`;
2130
+ var utxoNodeId2 = (outRef) => `utxo:${outRefKey(outRef)}`;
2131
+ var assetPolicyNodeId = (policyId) => `asset-policy:${policyId}`;
2132
+ var signerNodeId = (keyHash) => `signer:${keyHash}`;
2133
+ var withdrawalNodeId = (rewardAddress) => `withdrawal:${rewardAddress}`;
2134
+ var certificateNodeId = (txHash, index) => `certificate:${txHash}#${index}`;
2135
+ var collateralReturnNodeId = (txHash) => `collateral-return:${txHash}`;
2136
+ var transactionPortId = (txHash, kind, index) => `tx:${txHash}:port:${kind}:${index}`;
2137
+ var utxoPortId = (key, direction) => `utxo:${key}:port:${direction}`;
2138
+ var assetPolicyPortId = (policyId) => `asset-policy:${policyId}:port`;
2139
+ var signerPortId = (keyHash) => `signer:${keyHash}:port`;
2140
+ var withdrawalPortId = (rewardAddress) => `withdrawal:${rewardAddress}:port`;
2141
+ var certificatePortId = (txHash, index) => `certificate:${txHash}#${index}:port`;
2142
+ var collateralReturnPortId = (txHash) => `collateral-return:${txHash}:port`;
2143
+ var defaultLegend = {
2144
+ nodes: [
2145
+ { kind: "transaction", label: "Transaction" },
2146
+ { kind: "utxo", label: "UTxO" },
2147
+ { kind: "asset", label: "Asset policy" },
2148
+ { kind: "diagnostic", label: "Diagnostic" }
2149
+ ],
2150
+ edges: [
2151
+ { kind: "spend", label: "Spend input" },
2152
+ { kind: "read", label: "Reference input" },
2153
+ { kind: "collateral", label: "Collateral input" },
2154
+ { kind: "produce", label: "Produced output" },
2155
+ { kind: "mint", label: "Mint policy" },
2156
+ { kind: "burn", label: "Burn policy" },
2157
+ { kind: "withdraw", label: "Withdrawal" },
2158
+ { kind: "certify", label: "Certificate" },
2159
+ { kind: "sign", label: "Required signer" }
2160
+ ]
2161
+ };
2162
+
2163
+ // src/render/dot.ts
2164
+ var traceToDot = (trace, options = {}) => {
2165
+ if (options.legacy) return traceToLegacyDot(trace, options);
2166
+ const resolved = resolveVisualRendererOptions(options);
2167
+ const graph = formatSemanticGraph(
2168
+ buildSemanticRenderGraph(trace, {
2169
+ redeemers: options.redeemers
2170
+ }),
2171
+ resolved
2172
+ );
2173
+ const semanticNodes = new Map(
2174
+ graph.nodes.map((node) => [node.id, node])
2175
+ );
2176
+ const nodeIds = new Map(
2177
+ graph.nodes.map((node, index) => [node.id, `n${index}`])
2178
+ );
2179
+ const lines = [
2180
+ `digraph ${dotString(options.graphName ?? "lucid-evolution-tx-graph")} {`,
2181
+ " rankdir=LR;",
2182
+ " splines=ortho;",
2183
+ " nodesep=0.45;",
2184
+ " ranksep=0.75;",
2185
+ ' graph [fontname="Inter, Arial", bgcolor="white", pad=0.2];',
2186
+ ' node [fontname="Inter, Arial", fontsize=10, shape=plain, margin=0];',
2187
+ ' edge [fontname="Inter, Arial", fontsize=9, arrowsize=0.7];'
2188
+ ];
2189
+ for (const node of graph.nodes) {
2190
+ lines.push(
2191
+ ` ${dotString(nodeIds.get(node.id))} [${semanticNodeAttributes(
2192
+ node,
2193
+ resolved
2194
+ )}];`
2195
+ );
2196
+ }
2197
+ for (const edge of graph.edges) {
2198
+ const style = visualEdgeStyle(edge.kind);
2199
+ const scriptFocus = resolved.view === "scriptInteraction" && isScriptInteractionEdge(edge, semanticNodes);
2200
+ lines.push(
2201
+ ` ${dotString(nodeIds.get(edge.from))} -> ${dotString(
2202
+ nodeIds.get(edge.to)
2203
+ )} [` + [
2204
+ `label=${dotString(semanticEdgeLabel(trace, edge, resolved))}`,
2205
+ `color=${dotString(style.color)}`,
2206
+ `fontcolor=${dotString(style.color)}`,
2207
+ `style=${dotString(style.style)}`,
2208
+ `penwidth=${scriptFocus ? "2.8" : edge.kind === "spend" || edge.kind === "produce" ? "1.6" : "1.1"}`
2209
+ ].join(", ") + "];"
2210
+ );
2211
+ }
2212
+ lines.push("}");
2213
+ return `${lines.join("\n")}
2214
+ `;
2215
+ };
2216
+ var semanticNodeAttributes = (node, options) => [
2217
+ `label=<${semanticNodeLabel(node, options)}>`,
2218
+ `tooltip=${dotString(nodeTooltip(node, options))}`
2219
+ ].join(", ");
2220
+ var semanticNodeLabel = (node, options) => {
2221
+ const palette = nodePalette(node);
2222
+ const subtitle = semanticNodeSubtitle(node, options);
2223
+ const chips = semanticChipLabels(node, options);
2224
+ const sections = visibleSemanticSections(node, options);
2225
+ return [
2226
+ `<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0" CELLPADDING="5" COLOR="${palette.border}">`,
2227
+ `<TR><TD COLSPAN="2" BGCOLOR="${palette.header}"><B>${htmlText(semanticNodeTitle(node, options))}</B>${subtitle ? `<BR/><FONT POINT-SIZE="9">${htmlText(subtitle)}</FONT>` : ""}</TD></TR>`,
2228
+ ...chips.length > 0 ? [
2229
+ `<TR><TD COLSPAN="2" BGCOLOR="${palette.chip}"><FONT POINT-SIZE="9">${htmlText(
2230
+ chips.join(" | ")
2231
+ )}</FONT></TD></TR>`
2232
+ ] : [],
2233
+ ...sections.flatMap((section2) => sectionRows(section2, node, options)),
2234
+ "</TABLE>"
2235
+ ].join("");
2236
+ };
2237
+ var sectionRows = (section2, node, options) => {
2238
+ const rows = visibleSemanticRows(section2, node, options);
2239
+ if (rows.length === 0) return [];
2240
+ return [
2241
+ `<TR><TD COLSPAN="2" ALIGN="LEFT" BGCOLOR="#f9fafb"><B>${htmlText(
2242
+ section2.title
2243
+ )}</B></TD></TR>`,
2244
+ ...rows.map((row) => {
2245
+ const value = semanticFieldValue(row, options);
2246
+ const valueFace = row.mono ? ' FACE="monospace"' : "";
2247
+ return `<TR><TD ALIGN="LEFT"><FONT POINT-SIZE="9">${htmlText(
2248
+ semanticFieldLabel(row, options)
2249
+ )}</FONT></TD><TD ALIGN="LEFT"><FONT${valueFace}>${htmlText(
2250
+ value
2251
+ )}</FONT></TD></TR>`;
2252
+ })
2253
+ ];
2254
+ };
2255
+ var nodeTooltip = (node, options) => [
2256
+ semanticNodeTitle(node, options),
2257
+ ...semanticNodeSubtitle(node, options) ? [semanticNodeSubtitle(node, options)] : [],
2258
+ ...node.sections.flatMap(
2259
+ (section2) => section2.rows.map(
2260
+ (row) => `${section2.title}.${semanticFieldLabel(row, options)}: ${semanticFieldValue(
2261
+ row,
2262
+ options
2263
+ )}`
2264
+ )
2265
+ )
2266
+ ].join("\n");
2267
+ var nodePalette = (node) => {
2268
+ if (node.severity === "error") {
2269
+ return { header: "#fee2e2", chip: "#fef2f2", border: "#dc2626" };
2270
+ }
2271
+ if (isGenesisNode(node)) {
2272
+ return { header: "#fef3c7", chip: "#fffbeb", border: "#d97706" };
2273
+ }
2274
+ if (node.rawRef.type === "diagnostic" && node.rawRef.code === "collapsed") {
2275
+ return { header: "#f3f4f6", chip: "#f9fafb", border: "#6b7280" };
2276
+ }
2277
+ switch (node.kind) {
2278
+ case "transaction":
2279
+ return { header: "#eef2ff", chip: "#e0e7ff", border: "#6366f1" };
2280
+ case "utxo":
2281
+ return { header: "#ecfdf5", chip: "#d1fae5", border: "#10b981" };
2282
+ case "asset":
2283
+ return { header: "#f5f3ff", chip: "#ede9fe", border: "#7c3aed" };
2284
+ case "signer":
2285
+ return { header: "#f3f4f6", chip: "#f9fafb", border: "#6b7280" };
2286
+ case "withdrawal":
2287
+ return { header: "#ecfeff", chip: "#cffafe", border: "#0891b2" };
2288
+ case "certificate":
2289
+ return { header: "#eef2ff", chip: "#e0e7ff", border: "#4f46e5" };
2290
+ case "collateralReturn":
2291
+ return { header: "#fffbeb", chip: "#fef3c7", border: "#f59e0b" };
2292
+ case "diagnostic":
2293
+ return { header: "#fef2f2", chip: "#fee2e2", border: "#dc2626" };
2294
+ }
2295
+ };
2296
+ var htmlText = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/\\n/g, "<BR/>").replace(/\n/g, "<BR/>");
2297
+ var traceToLegacyDot = (trace, options = {}) => {
2298
+ const graph = buildRenderGraph(trace);
2299
+ const lines = [
2300
+ `digraph ${dotString(options.graphName ?? "lucid-evolution-tx-graph")} {`,
2301
+ " rankdir=LR;",
2302
+ ' graph [fontname="Inter, Arial", bgcolor="white"];',
2303
+ ' node [fontname="Inter, Arial", fontsize=10, margin="0.08,0.06"];',
2304
+ ' edge [fontname="Inter, Arial", fontsize=9, arrowsize=0.7];'
2305
+ ];
2306
+ for (const node of graph.nodes) {
2307
+ lines.push(` ${dotString(node.id)} [${legacyNodeAttributes(node)}];`);
2308
+ }
2309
+ for (const edge of graph.edges) {
2310
+ const style = edgeStyle(edge.kind);
2311
+ lines.push(
2312
+ ` ${dotString(edge.from)} -> ${dotString(edge.to)} [` + [
2313
+ `label=${dotString(style.label)}`,
2314
+ `color=${dotString(style.color)}`,
2315
+ `fontcolor=${dotString(style.color)}`,
2316
+ `style=${dotString(style.style)}`
2317
+ ].join(", ") + "];"
2318
+ );
2319
+ }
2320
+ lines.push("}");
2321
+ return `${lines.join("\n")}
2322
+ `;
2323
+ };
2324
+ var legacyNodeAttributes = (node) => {
2325
+ const attrs = [
2326
+ `label=${dotString(node.label)}`,
2327
+ `shape=${dotString(legacyNodeShape(node))}`,
2328
+ `style=${dotString(legacyNodeStyle(node))}`,
2329
+ `fillcolor=${dotString(legacyNodeFill(node))}`,
2330
+ `color=${dotString(legacyNodeColor(node))}`
2331
+ ];
2332
+ return attrs.join(", ");
2333
+ };
2334
+ var legacyNodeShape = (node) => {
2335
+ switch (node.kind) {
2336
+ case "transaction":
2337
+ return "box";
2338
+ case "utxo":
2339
+ return "box";
2340
+ case "asset":
2341
+ return "component";
2342
+ case "signer":
2343
+ return "octagon";
2344
+ case "withdrawal":
2345
+ case "certificate":
2346
+ case "collateralReturn":
2347
+ case "external":
2348
+ return "note";
2349
+ }
2350
+ };
2351
+ var legacyNodeStyle = (node) => node.kind === "utxo" ? "rounded,filled" : "filled";
2352
+ var legacyNodeFill = (node) => {
2353
+ if (node.unresolved) return "#fee2e2";
2354
+ if (node.genesis) return "#fef3c7";
2355
+ switch (node.kind) {
2356
+ case "transaction":
2357
+ return "#eef2ff";
2358
+ case "utxo":
2359
+ return "#ecfdf5";
2360
+ case "asset":
2361
+ return "#f5f3ff";
2362
+ case "signer":
2363
+ return "#f3f4f6";
2364
+ case "withdrawal":
2365
+ return "#ecfeff";
2366
+ case "certificate":
2367
+ return "#eef2ff";
2368
+ case "collateralReturn":
2369
+ return "#fffbeb";
2370
+ case "external":
2371
+ return "#f9fafb";
2372
+ }
2373
+ };
2374
+ var legacyNodeColor = (node) => node.unresolved ? "#dc2626" : node.genesis ? "#d97706" : "#9ca3af";
2375
+ var dotString = (value) => JSON.stringify(value.replace(/\\n/g, "\n"));
2376
+
2377
+ // src/render/measure.ts
2378
+ var MARGIN = 40;
2379
+ var TITLE_HEIGHT = 74;
2380
+ var LEGEND_HEIGHT = 132;
2381
+ var MIN_CANVAS_WIDTH = 940;
2382
+ var NODE_GAP_Y = 34;
2383
+ var RANK_GAP_X = 170;
2384
+ var LABEL_GAP = 8;
2385
+ var measureNode = (node, options) => {
2386
+ const width = nodeWidth(node);
2387
+ if (node.kind === "transaction") {
2388
+ return { width, height: measureTransactionNode(node, options) };
2389
+ }
2390
+ const sections = visibleSemanticSections(node, options);
2391
+ const rowCount = sections.reduce(
2392
+ (sum, section2) => sum + visibleSemanticRows(section2, node, options).length,
2393
+ 0
2394
+ );
2395
+ const populatedSections = sections.filter(
2396
+ (section2) => visibleSemanticRows(section2, node, options).length > 0
2397
+ ).length;
2398
+ const chips = semanticChipLabels(node, options);
2399
+ const chipRows = estimateChipRows(chips, width - 28);
2400
+ return {
2401
+ width,
2402
+ height: 58 + chipRows * 24 + populatedSections * 24 + rowCount * 23 + 22
2403
+ };
2404
+ };
2405
+ var measureLabel = (label) => ({
2406
+ width: Math.min(260, Math.max(54, label.length * 6.4 + 18)),
2407
+ height: 22
2408
+ });
2409
+ var visibleEdgeLabel = (trace, edge, options) => {
2410
+ if (edge.action || edge.redeemerKey) {
2411
+ return edge.action?.label ?? semanticEdgeLabel(trace, edge, options);
2412
+ }
2413
+ if (edge.kind === "diagnostic")
2414
+ return semanticEdgeLabel(trace, edge, options);
2415
+ if (edge.kind === "mint" || edge.kind === "burn") {
2416
+ return options.mode === "overview" ? void 0 : edge.label;
2417
+ }
2418
+ if (edge.kind === "withdraw" || edge.kind === "certify") return edge.label;
2419
+ if (edge.kind === "collateral" || edge.kind === "collateralReturn") {
2420
+ return options.mode === "debug" ? edge.label : void 0;
2421
+ }
2422
+ return void 0;
2423
+ };
2424
+ var nodeWidth = (node) => {
2425
+ switch (node.kind) {
2426
+ case "transaction":
2427
+ return 330;
2428
+ case "utxo":
2429
+ return 306;
2430
+ case "diagnostic":
2431
+ return 286;
2432
+ case "asset":
2433
+ return 270;
2434
+ case "withdrawal":
2435
+ case "certificate":
2436
+ case "collateralReturn":
2437
+ return 270;
2438
+ case "signer":
2439
+ return 240;
2440
+ }
2441
+ };
2442
+ var metricRowCount = (node, options) => {
2443
+ const metricRows = visibleSemanticSections(node, options).filter(
2444
+ (section2) => ["inputs", "assets", "authority", "redeemers"].includes(section2.id)
2445
+ ).flatMap((section2) => visibleSemanticRows(section2, node, options)).filter((row) => row.value !== "0").length;
2446
+ return metricRows === 0 ? 0 : Math.ceil(metricRows / 4);
2447
+ };
2448
+ var measureTransactionNode = (node, options) => {
2449
+ const metricRows = metricRowCount(node, options);
2450
+ const facts = rowsForSection(node, options, "facts").slice(0, 4);
2451
+ const redeemers = rowsForSection(node, options, "redeemers").slice(0, 1);
2452
+ const detailRows = facts.length + redeemers.length;
2453
+ const metricsHeight = metricRows === 0 ? 0 : metricRows * 34 + 8;
2454
+ const detailsHeight = detailRows === 0 ? 0 : 27 + detailRows * 21;
2455
+ return 64 + metricsHeight + detailsHeight + 12;
2456
+ };
2457
+ var rowsForSection = (node, options, sectionId) => visibleSemanticSections(node, options).filter((section2) => section2.id === sectionId).flatMap((section2) => visibleSemanticRows(section2, node, options)).filter((row) => row.value !== "0");
2458
+ var estimateChipRows = (chips, availableWidth) => {
2459
+ if (chips.length === 0) return 0;
2460
+ let rows = 1;
2461
+ let cursor = 0;
2462
+ for (const chip of chips) {
2463
+ const width = Math.max(42, Math.min(18, chip.length) * 6 + 18) + 6;
2464
+ if (cursor > 0 && cursor + width > availableWidth) {
2465
+ rows += 1;
2466
+ cursor = 0;
2467
+ }
2468
+ cursor += width;
2469
+ }
2470
+ return rows;
2471
+ };
2472
+
2473
+ // src/render/layout.ts
2474
+ var layoutGraph = (trace, graph, options) => {
2475
+ const sizes = new Map(
2476
+ graph.nodes.map((node) => [node.id, measureNode(node, options)])
2477
+ );
2478
+ const ranks = rankNodes(trace, graph);
2479
+ const rankedNodes = /* @__PURE__ */ new Map();
2480
+ for (const node of graph.nodes) {
2481
+ const rank = ranks.get(node.id) ?? 0;
2482
+ rankedNodes.set(rank, [...rankedNodes.get(rank) ?? [], node]);
2483
+ }
2484
+ const orderedRanks = [...rankedNodes.keys()].sort(
2485
+ (left, right) => left - right
2486
+ );
2487
+ const rankWidths = new Map(
2488
+ orderedRanks.map((rank) => [
2489
+ rank,
2490
+ Math.max(
2491
+ ...rankedNodes.get(rank).map((node) => sizes.get(node.id).width)
2492
+ )
2493
+ ])
2494
+ );
2495
+ const rankHeights = new Map(
2496
+ orderedRanks.map((rank) => [
2497
+ rank,
2498
+ rankedNodes.get(rank).reduce((sum, node) => sum + sizes.get(node.id).height, 0) + Math.max(0, rankedNodes.get(rank).length - 1) * NODE_GAP_Y
2499
+ ])
2500
+ );
2501
+ const maxRankHeight = Math.max(0, ...rankHeights.values());
2502
+ let x = MARGIN;
2503
+ const rankX = /* @__PURE__ */ new Map();
2504
+ for (const rank of orderedRanks) {
2505
+ rankX.set(rank, x);
2506
+ x += (rankWidths.get(rank) ?? 0) + RANK_GAP_X;
2507
+ }
2508
+ const positioned = /* @__PURE__ */ new Map();
2509
+ for (const rank of orderedRanks) {
2510
+ const nodes = [...rankedNodes.get(rank)].sort(
2511
+ (left, right) => nodeSortKey(left, graph).localeCompare(nodeSortKey(right, graph))
2512
+ );
2513
+ let y = MARGIN + TITLE_HEIGHT + 32 + Math.max(0, (maxRankHeight - (rankHeights.get(rank) ?? 0)) / 2);
2514
+ for (const node of nodes) {
2515
+ const size = sizes.get(node.id);
2516
+ positioned.set(node.id, {
2517
+ node,
2518
+ ...size,
2519
+ x: rankX.get(rank) + ((rankWidths.get(rank) ?? size.width) - size.width) / 2,
2520
+ y
2521
+ });
2522
+ y += size.height + NODE_GAP_Y;
2523
+ }
2524
+ }
2525
+ const ports = portOffsets(graph, positioned);
2526
+ const provisional = {
2527
+ nodes: positioned,
2528
+ portOffsets: ports,
2529
+ edgeLabels: /* @__PURE__ */ new Map(),
2530
+ width: MIN_CANVAS_WIDTH,
2531
+ height: 260,
2532
+ offsetX: 0,
2533
+ offsetY: 0
2534
+ };
2535
+ const edgeLabels = placeEdgeLabels(trace, graph, options, provisional);
2536
+ const boxes = [
2537
+ ...[...positioned.values()].map(nodeBox),
2538
+ ...edgeLabels.values()
2539
+ ];
2540
+ const maxX = Math.max(0, ...boxes.map((box) => box.x + box.width));
2541
+ const maxY = Math.max(0, ...boxes.map((box) => box.y + box.height));
2542
+ return {
2543
+ nodes: positioned,
2544
+ portOffsets: ports,
2545
+ edgeLabels,
2546
+ width: Math.max(MIN_CANVAS_WIDTH, Math.ceil(maxX + MARGIN)),
2547
+ height: Math.ceil(maxY + MARGIN + LEGEND_HEIGHT),
2548
+ offsetX: 0,
2549
+ offsetY: 0
2550
+ };
2551
+ };
2552
+ var nodeBox = (node) => ({
2553
+ x: node.x,
2554
+ y: node.y,
2555
+ width: node.width,
2556
+ height: node.height
2557
+ });
2558
+ var edgeAnchors = (edge, graph) => {
2559
+ const from = graph.nodes.get(edge.from);
2560
+ const to = graph.nodes.get(edge.to);
2561
+ if (!from || !to) return void 0;
2562
+ return {
2563
+ start: anchor(edge, "from", from, to, graph),
2564
+ end: anchor(edge, "to", to, from, graph)
2565
+ };
2566
+ };
2567
+ var edgePath = (edge, graph) => {
2568
+ const anchors = edgeAnchors(edge, graph);
2569
+ if (!anchors) return "";
2570
+ const { start, end } = anchors;
2571
+ const dx = end.x - start.x;
2572
+ const dy = end.y - start.y;
2573
+ if (Math.abs(dx) < 80) {
2574
+ const curve = Math.max(50, Math.abs(dy) / 2);
2575
+ const direction = dy >= 0 ? 1 : -1;
2576
+ return `M ${start.x} ${start.y} C ${start.x} ${start.y + curve * direction}, ${end.x} ${end.y - curve * direction}, ${end.x} ${end.y}`;
2577
+ }
2578
+ const midX = start.x + dx / 2;
2579
+ return `M ${start.x} ${start.y} L ${midX} ${start.y} L ${midX} ${end.y} L ${end.x} ${end.y}`;
2580
+ };
2581
+ var edgeLabelPoint = (edge, graph) => {
2582
+ const anchors = edgeAnchors(edge, graph);
2583
+ if (!anchors) return { x: 0, y: 0 };
2584
+ const { start, end } = anchors;
2585
+ return {
2586
+ x: (start.x + end.x) / 2,
2587
+ y: (start.y + end.y) / 2
2588
+ };
2589
+ };
2590
+ var rankNodes = (trace, graph) => {
2591
+ const txIndex = new Map(
2592
+ trace.transactions.map(
2593
+ (transaction, index) => [transaction.hash, index]
2594
+ )
2595
+ );
2596
+ const edgeTxIndices = /* @__PURE__ */ new Map();
2597
+ for (const edge of graph.edges) {
2598
+ const txHash = edge.targetRef.txHash;
2599
+ if (!txHash) continue;
2600
+ const index = txIndex.get(txHash);
2601
+ if (index === void 0) continue;
2602
+ for (const nodeId of [edge.from, edge.to]) {
2603
+ edgeTxIndices.set(nodeId, [...edgeTxIndices.get(nodeId) ?? [], index]);
2604
+ }
2605
+ }
2606
+ const ranks = /* @__PURE__ */ new Map();
2607
+ for (const node of graph.nodes) {
2608
+ const attachedTxs = edgeTxIndices.get(node.id) ?? [0];
2609
+ const attachedTx = Math.min(...attachedTxs);
2610
+ const latestAttachedTx = Math.max(...attachedTxs);
2611
+ switch (node.rawRef.type) {
2612
+ case "transaction": {
2613
+ ranks.set(
2614
+ node.id,
2615
+ (txIndex.get(node.rawRef.txHash) ?? attachedTx) * 2 + 1
2616
+ );
2617
+ break;
2618
+ }
2619
+ case "utxo": {
2620
+ const producer = txIndex.get(node.rawRef.outRef.txHash);
2621
+ ranks.set(
2622
+ node.id,
2623
+ producer === void 0 ? attachedTx * 2 : producer * 2 + 2
2624
+ );
2625
+ break;
2626
+ }
2627
+ case "assetPolicy":
2628
+ ranks.set(node.id, latestAttachedTx * 2 + 2);
2629
+ break;
2630
+ case "certificate":
2631
+ case "collateralReturn":
2632
+ ranks.set(node.id, attachedTx * 2 + 2);
2633
+ break;
2634
+ case "signer":
2635
+ case "withdrawal":
2636
+ ranks.set(node.id, attachedTx * 2);
2637
+ break;
2638
+ case "diagnostic":
2639
+ ranks.set(node.id, attachedTx * 2 + 1);
2640
+ break;
2641
+ }
2642
+ }
2643
+ return ranks;
2644
+ };
2645
+ var nodeSortKey = (node, graph) => {
2646
+ const attached = graph.edges.filter((edge) => edge.from === node.id || edge.to === node.id).map((edge) => targetRefSortIndex(edge.targetRef));
2647
+ const attachedIndex = attached.length === 0 ? 0 : Math.min(...attached);
2648
+ return [
2649
+ String(kindPriority(node)).padStart(2, "0"),
2650
+ String(attachedIndex).padStart(4, "0"),
2651
+ rawRefSortKey(node),
2652
+ node.id
2653
+ ].join(":");
2654
+ };
2655
+ var targetRefSortIndex = (targetRef) => {
2656
+ switch (targetRef.type) {
2657
+ case "input":
2658
+ case "output":
2659
+ case "withdrawal":
2660
+ case "certificate":
2661
+ return targetRef.index;
2662
+ case "assetPolicy":
2663
+ return targetRef.policyIndex;
2664
+ case "signer":
2665
+ case "collateralReturn":
2666
+ return 0;
2667
+ case "diagnostic":
2668
+ return targetRef.count ?? 0;
2669
+ }
2670
+ };
2671
+ var kindPriority = (node) => {
2672
+ switch (node.kind) {
2673
+ case "diagnostic":
2674
+ return 0;
2675
+ case "withdrawal":
2676
+ return 1;
2677
+ case "signer":
2678
+ return 2;
2679
+ case "transaction":
2680
+ return 3;
2681
+ case "utxo":
2682
+ return 4;
2683
+ case "asset":
2684
+ return 5;
2685
+ case "certificate":
2686
+ return 6;
2687
+ case "collateralReturn":
2688
+ return 7;
2689
+ }
2690
+ };
2691
+ var rawRefSortKey = (node) => {
2692
+ switch (node.rawRef.type) {
2693
+ case "utxo":
2694
+ return outRefKey(node.rawRef.outRef);
2695
+ case "transaction":
2696
+ return node.rawRef.txHash;
2697
+ case "assetPolicy":
2698
+ return node.rawRef.policyId;
2699
+ case "signer":
2700
+ return node.rawRef.keyHash;
2701
+ case "withdrawal":
2702
+ return node.rawRef.rewardAddress;
2703
+ case "certificate":
2704
+ return `${node.rawRef.txHash}#${node.rawRef.index}`;
2705
+ case "collateralReturn":
2706
+ return node.rawRef.txHash;
2707
+ case "diagnostic":
2708
+ return node.rawRef.code;
2709
+ }
2710
+ };
2711
+ var placeEdgeLabels = (trace, formatted, options, graph) => {
2712
+ const nodeBoxes = [...graph.nodes.values()].map(
2713
+ (node) => inflate(nodeBox(node), LABEL_GAP)
2714
+ );
2715
+ const placed = [];
2716
+ const result = /* @__PURE__ */ new Map();
2717
+ for (const edge of formatted.edges) {
2718
+ const text = visibleEdgeLabel(trace, edge, options);
2719
+ if (!text) continue;
2720
+ const labelText = fit(text, options.mode === "debug" ? 42 : 28);
2721
+ const size = measureLabel(labelText);
2722
+ const origin = edgeLabelPoint(edge, graph);
2723
+ const candidates = labelCandidates(origin, size);
2724
+ const box = candidates.find(
2725
+ (candidate) => !nodeBoxes.some((node) => intersects(candidate, node)) && !placed.some(
2726
+ (label2) => intersects(candidate, inflate(label2, LABEL_GAP))
2727
+ )
2728
+ );
2729
+ if (!box) continue;
2730
+ const label = { ...box, edgeId: edge.id, text: labelText };
2731
+ placed.push(label);
2732
+ result.set(edge.id, label);
2733
+ }
2734
+ return result;
2735
+ };
2736
+ var labelCandidates = (origin, size) => {
2737
+ const x = origin.x - size.width / 2;
2738
+ const y = origin.y - size.height / 2;
2739
+ const offsets = [
2740
+ [0, -28],
2741
+ [0, 28],
2742
+ [0, -56],
2743
+ [0, 56],
2744
+ [-44, -28],
2745
+ [44, -28],
2746
+ [-44, 28],
2747
+ [44, 28],
2748
+ [0, -84],
2749
+ [0, 84]
2750
+ ];
2751
+ return offsets.map(([dx, dy]) => ({
2752
+ x: x + dx,
2753
+ y: y + dy,
2754
+ width: size.width,
2755
+ height: size.height
2756
+ }));
2757
+ };
2758
+ var portOffsets = (graph, nodes) => {
2759
+ const edgeCountByPort = /* @__PURE__ */ new Map();
2760
+ for (const edge of graph.edges) {
2761
+ for (const portId of [edge.fromPort, edge.toPort]) {
2762
+ if (portId)
2763
+ edgeCountByPort.set(portId, (edgeCountByPort.get(portId) ?? 0) + 1);
2764
+ }
2765
+ }
2766
+ const offsets = /* @__PURE__ */ new Map();
2767
+ for (const layoutNode of nodes.values()) {
2768
+ const ports = layoutNode.node.ports.filter((port2) => edgeCountByPort.has(port2.id)).sort(
2769
+ (left, right) => portSortKey(left, layoutNode.node).localeCompare(
2770
+ portSortKey(right, layoutNode.node)
2771
+ )
2772
+ );
2773
+ const bySide = groupPortsBySide(ports);
2774
+ for (const sidePorts of bySide.values()) {
2775
+ sidePorts.forEach((port2, index) => {
2776
+ if (port2.side === "top" || port2.side === "bottom") {
2777
+ offsets.set(
2778
+ port2.id,
2779
+ (index + 1) * layoutNode.width / (sidePorts.length + 1)
2780
+ );
2781
+ } else {
2782
+ offsets.set(
2783
+ port2.id,
2784
+ 60 + (index + 1) * Math.max(24, layoutNode.height - 92) / (sidePorts.length + 1)
2785
+ );
2786
+ }
2787
+ });
2788
+ }
2789
+ }
2790
+ return offsets;
2791
+ };
2792
+ var groupPortsBySide = (ports) => {
2793
+ const groups = /* @__PURE__ */ new Map();
2794
+ for (const port2 of ports) {
2795
+ groups.set(port2.side, [...groups.get(port2.side) ?? [], port2]);
2796
+ }
2797
+ return groups;
2798
+ };
2799
+ var portSortKey = (port2, node) => [
2800
+ String(portSidePriority(port2.side)).padStart(2, "0"),
2801
+ String(edgeKindPriority(port2.edgeKind)).padStart(2, "0"),
2802
+ port2.id.replace(
2803
+ /:(\d+)(?::|$)/g,
2804
+ (_match, index) => `:${index.padStart(4, "0")}:`
2805
+ ),
2806
+ node.id
2807
+ ].join(":");
2808
+ var portSidePriority = (side) => {
2809
+ switch (side) {
2810
+ case "left":
2811
+ return 0;
2812
+ case "right":
2813
+ return 1;
2814
+ case "top":
2815
+ return 2;
2816
+ case "bottom":
2817
+ return 3;
2818
+ default:
2819
+ return 4;
2820
+ }
2821
+ };
2822
+ var edgeKindPriority = (kind) => {
2823
+ switch (kind) {
2824
+ case "spend":
2825
+ return 0;
2826
+ case "read":
2827
+ return 1;
2828
+ case "collateral":
2829
+ return 2;
2830
+ case "produce":
2831
+ return 3;
2832
+ case "mint":
2833
+ return 4;
2834
+ case "withdraw":
2835
+ return 5;
2836
+ case "certify":
2837
+ return 6;
2838
+ case "sign":
2839
+ return 7;
2840
+ default:
2841
+ return 8;
2842
+ }
2843
+ };
2844
+ var anchor = (edge, endpoint, node, other, graph) => {
2845
+ const portId = endpoint === "from" ? edge.fromPort : edge.toPort;
2846
+ const port2 = portId ? node.node.ports.find((candidate) => candidate.id === portId) : void 0;
2847
+ const offset = portId ? graph.portOffsets.get(portId) : void 0;
2848
+ if (port2?.side === "top") {
2849
+ return { x: node.x + (offset ?? node.width / 2), y: node.y };
2850
+ }
2851
+ if (port2?.side === "bottom") {
2852
+ return { x: node.x + (offset ?? node.width / 2), y: node.y + node.height };
2853
+ }
2854
+ if (port2?.side === "left") {
2855
+ return { x: node.x, y: node.y + (offset ?? node.height / 2) };
2856
+ }
2857
+ if (port2?.side === "right") {
2858
+ return { x: node.x + node.width, y: node.y + (offset ?? node.height / 2) };
2859
+ }
2860
+ const nodeCenterX = node.x + node.width / 2;
2861
+ const otherCenterX = other.x + other.width / 2;
2862
+ return {
2863
+ x: otherCenterX >= nodeCenterX ? node.x + node.width : node.x,
2864
+ y: node.y + node.height / 2
2865
+ };
2866
+ };
2867
+ var inflate = (box, padding) => ({
2868
+ x: box.x - padding,
2869
+ y: box.y - padding,
2870
+ width: box.width + padding * 2,
2871
+ height: box.height + padding * 2
2872
+ });
2873
+ var intersects = (left, right) => left.x < right.x + right.width && left.x + left.width > right.x && left.y < right.y + right.height && left.y + left.height > right.y;
2874
+ var fit = (value, maxChars) => value.length <= maxChars ? value : `${value.slice(0, maxChars - 3)}...`;
2875
+
2876
+ // src/render/edge-renderer.ts
2877
+ var renderEdge = (trace, edge, index, graph, options, semanticNodes, semantic) => {
2878
+ if (!graph.nodes.has(edge.from) || !graph.nodes.has(edge.to)) return "";
2879
+ const path = edgePath(edge, graph);
2880
+ const style = visualEdgeStyle(edge.kind);
2881
+ const scriptFocus = options.view === "scriptInteraction" && isScriptInteractionEdge(edge, semanticNodes);
2882
+ const label = graph.edgeLabels.get(edge.id);
2883
+ return [
2884
+ `<g data-txg-id="${opaqueId(semantic, edge.id)}" data-txg-type="edge" class="txg-edge txg-${edge.kind}">`,
2885
+ `<title>${xmlText(semanticEdgeLabel(trace, edge, options))}</title>`,
2886
+ `<path class="txg-edge-hit" d="${path}" fill="none" stroke="transparent" stroke-width="12" pointer-events="stroke"/>`,
2887
+ `<path d="${path}" fill="none" stroke="${style.color}" stroke-width="${strokeWidth(edge, scriptFocus)}" marker-end="url(#txg-arrow)"${dashArray(style.style)}/>`,
2888
+ label ? renderEdgeLabel(
2889
+ label.x,
2890
+ label.y,
2891
+ label.width,
2892
+ label.height,
2893
+ label.text,
2894
+ style.color
2895
+ ) : "",
2896
+ "</g>"
2897
+ ].join("\n");
2898
+ };
2899
+ var renderEdgeLabel = (x, y, width, height, label, color) => [
2900
+ `<rect class="txg-edge-label" x="${x}" y="${y}" width="${width}" height="${height}" rx="5" fill="#ffffff" stroke="#cbd5e1"/>`,
2901
+ `<text x="${x + width / 2}" y="${y + 14}" text-anchor="middle" font-family="Inter, Arial" font-size="10" font-weight="700" fill="${color}">${xmlText(label)}</text>`
2902
+ ].join("\n");
2903
+ var strokeWidth = (edge, scriptFocus) => {
2904
+ if (scriptFocus) return "2.8";
2905
+ if (edge.kind === "spend") return "2.2";
2906
+ if (edge.kind === "produce") return "1.9";
2907
+ if (edge.kind === "diagnostic") return "2";
2908
+ return "1.35";
2909
+ };
2910
+ var dashArray = (style) => style === "solid" ? "" : style === "dotted" ? ' stroke-dasharray="2 4"' : ' stroke-dasharray="6 5"';
2911
+ var opaqueId = (graph, id) => {
2912
+ const nodeIndex = graph.nodes.findIndex((node) => node.id === id);
2913
+ if (nodeIndex >= 0) return `node:${nodeIndex}`;
2914
+ const edgeIndex = graph.edges.findIndex((edge) => edge.id === id);
2915
+ return edgeIndex >= 0 ? `edge:${edgeIndex}` : id;
2916
+ };
2917
+ var xmlText = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2918
+
2919
+ // src/render/node-renderer.ts
2920
+ var renderNode = (layoutNode, graph, options, index, semantic) => {
2921
+ switch (layoutNode.node.kind) {
2922
+ case "transaction":
2923
+ return renderTransactionNode(layoutNode, graph, options, index, semantic);
2924
+ case "utxo":
2925
+ return renderUtxoNode(layoutNode, graph, options, index, semantic);
2926
+ default:
2927
+ return renderGenericNode(layoutNode, graph, options, index, semantic);
2928
+ }
2929
+ };
2930
+ var renderLegend = (graph, semantic) => {
2931
+ const y = graph.height - LEGEND_HEIGHT + 34;
2932
+ const itemWidth = 156;
2933
+ const items = edgeLegendItems(semantic);
2934
+ const perRow = Math.max(1, Math.floor((graph.width - 80) / itemWidth));
2935
+ return [
2936
+ `<g class="txg-legend">`,
2937
+ `<text x="40" y="${y}" font-family="Inter, Arial" font-size="12" font-weight="700" fill="#111827">Legend</text>`,
2938
+ ...items.map(([label, title], index) => {
2939
+ const style = visualEdgeStyle(label);
2940
+ const x = 40 + index % perRow * itemWidth;
2941
+ const rowY = y + 24 + Math.floor(index / perRow) * 30;
2942
+ return [
2943
+ `<g>`,
2944
+ `<title>${xmlText2(title)}</title>`,
2945
+ `<line x1="${x}" y1="${rowY}" x2="${x + 30}" y2="${rowY}" stroke="${style.color}" stroke-width="2.2"${dashArray2(style.style)}/>`,
2946
+ `<text x="${x + 38}" y="${rowY + 4}" font-family="Inter, Arial" font-size="11" fill="#374151">${label}</text>`,
2947
+ `</g>`
2948
+ ].join("\n");
2949
+ }),
2950
+ "</g>"
2951
+ ].join("\n");
2952
+ };
2953
+ var edgeLegendItems = (semantic) => {
2954
+ const present = new Set(semantic.edges.map((edge) => edge.kind));
2955
+ const ordered = [
2956
+ "spend",
2957
+ "read",
2958
+ "collateral",
2959
+ "produce",
2960
+ "mint",
2961
+ "burn",
2962
+ "withdraw",
2963
+ "certify",
2964
+ "sign",
2965
+ "collateralReturn",
2966
+ "diagnostic",
2967
+ "summary"
2968
+ ].filter((kind) => present.has(kind));
2969
+ return (ordered.length > 0 ? ordered : ["spend", "produce"]).map((kind) => [
2970
+ kind,
2971
+ edgeLegendTitle(kind)
2972
+ ]);
2973
+ };
2974
+ var edgeLegendTitle = (kind) => {
2975
+ switch (kind) {
2976
+ case "spend":
2977
+ return "Consumes an input UTxO";
2978
+ case "read":
2979
+ return "Reads a reference input without consuming it";
2980
+ case "collateral":
2981
+ return "Uses collateral for script validation";
2982
+ case "produce":
2983
+ return "Creates an output UTxO";
2984
+ case "mint":
2985
+ return "Mints assets under a policy";
2986
+ case "burn":
2987
+ return "Burns assets under a policy";
2988
+ case "withdraw":
2989
+ return "Withdraws staking rewards";
2990
+ case "certify":
2991
+ return "Applies a certificate";
2992
+ case "sign":
2993
+ return "Requires a signer";
2994
+ case "collateralReturn":
2995
+ return "Returns collateral";
2996
+ case "diagnostic":
2997
+ return "Warning or error";
2998
+ default:
2999
+ return kind;
3000
+ }
3001
+ };
3002
+ var dashArray2 = (style) => style === "solid" ? "" : style === "dotted" ? ' stroke-dasharray="2 4"' : ' stroke-dasharray="6 5"';
3003
+ var renderTransactionNode = (layoutNode, graph, options, index, semantic) => {
3004
+ const { node, width, height } = layoutNode;
3005
+ const x = layoutNode.x + graph.offsetX;
3006
+ const y = layoutNode.y + graph.offsetY;
3007
+ const palette = nodePalette2(node);
3008
+ const title = fit2(semanticNodeTitle(node, options), 38);
3009
+ const subtitle = semanticNodeSubtitle(node, options);
3010
+ const status = node.chips[0]?.label ?? "tx";
3011
+ const metrics = transactionMetrics(node, options);
3012
+ const facts = sectionRows2(node, options, "facts").slice(0, 4);
3013
+ const redeemers = sectionRows2(node, options, "redeemers").slice(0, 1);
3014
+ let cursor = y + 64;
3015
+ const lines = baseNodeOpen(
3016
+ index,
3017
+ node,
3018
+ semantic,
3019
+ options,
3020
+ x,
3021
+ y,
3022
+ width,
3023
+ height,
3024
+ palette
3025
+ );
3026
+ lines.push(
3027
+ `<rect x="${x}" y="${y}" width="${width}" height="54" rx="8" fill="${palette.header}"/>`,
3028
+ `<text x="${x + 14}" y="${y + 21}" font-family="Inter, Arial" font-size="14" font-weight="700" fill="#0f172a">${xmlText2(title)}</text>`,
3029
+ `<text x="${x + 14}" y="${y + 40}" font-family="ui-monospace, SFMono-Regular, monospace" font-size="10" fill="#475569">${xmlText2(fit2(subtitle ?? "", 36))}</text>`,
3030
+ `<rect x="${x + width - 88}" y="${y + 13}" width="72" height="24" rx="12" fill="${statusFill(node)}" stroke="${statusStroke(node)}"/>`,
3031
+ `<text x="${x + width - 52}" y="${y + 29}" text-anchor="middle" font-family="Inter, Arial" font-size="10" font-weight="700" fill="${statusText(node)}">${xmlText2(status)}</text>`
3032
+ );
3033
+ if (metrics.length > 0) {
3034
+ lines.push(`<g class="txg-metrics">`);
3035
+ metrics.forEach((metric, metricIndex) => {
3036
+ const column = metricIndex % 4;
3037
+ const row = Math.floor(metricIndex / 4);
3038
+ const boxX = x + 14 + column * 74;
3039
+ const boxY = cursor + row * 34;
3040
+ lines.push(
3041
+ `<rect x="${boxX}" y="${boxY}" width="66" height="27" rx="6" fill="#f8fafc" stroke="#e5e7eb"/>`,
3042
+ `<text x="${boxX + 8}" y="${boxY + 11}" font-family="Inter, Arial" font-size="8" font-weight="700" fill="#64748b">${xmlText2(fit2(metric.label, 8))}</text>`,
3043
+ `<text x="${boxX + 8}" y="${boxY + 23}" font-family="Inter, Arial" font-size="12" font-weight="700" fill="#111827">${xmlText2(metric.value)}</text>`
3044
+ );
3045
+ });
3046
+ lines.push(`</g>`);
3047
+ cursor += Math.ceil(metrics.length / 4) * 34 + 8;
3048
+ }
3049
+ renderRows(lines, x, cursor, width, "Facts", [...facts, ...redeemers]);
3050
+ lines.push("</g>");
3051
+ return lines.join("\n");
3052
+ };
3053
+ var renderUtxoNode = (layoutNode, graph, options, index, semantic) => {
3054
+ const { node, width, height } = layoutNode;
3055
+ const x = layoutNode.x + graph.offsetX;
3056
+ const y = layoutNode.y + graph.offsetY;
3057
+ const palette = nodePalette2(node);
3058
+ const title = fit2(semanticNodeTitle(node, options), 34);
3059
+ const subtitle = semanticNodeSubtitle(node, options);
3060
+ const chips = usefulChips(node, options);
3061
+ const assetRows = sectionRows2(node, options, "assets");
3062
+ const stateRows = sectionRows2(node, options, "state");
3063
+ const ownerRows = sectionRows2(node, options, "owner");
3064
+ let cursor = y + 62;
3065
+ const lines = baseNodeOpen(
3066
+ index,
3067
+ node,
3068
+ semantic,
3069
+ options,
3070
+ x,
3071
+ y,
3072
+ width,
3073
+ height,
3074
+ palette
3075
+ );
3076
+ lines.push(
3077
+ `<rect x="${x}" y="${y}" width="${width}" height="52" rx="8" fill="${palette.header}"/>`,
3078
+ `<text x="${x + 14}" y="${y + 21}" font-family="Inter, Arial" font-size="13" font-weight="700" fill="#0f172a">${xmlText2(title)}</text>`,
3079
+ `<text x="${x + 14}" y="${y + 39}" font-family="ui-monospace, SFMono-Regular, monospace" font-size="10" fill="#475569">${xmlText2(fit2(subtitle ?? "", 38))}</text>`
3080
+ );
3081
+ if (chips.length > 0) {
3082
+ cursor = renderChips(lines, x + 14, cursor, chips, width - 28) + 8;
3083
+ }
3084
+ cursor = renderRows(lines, x, cursor, width, "Value", assetRows);
3085
+ if (stateRows.length > 0) {
3086
+ cursor = renderRows(lines, x, cursor + 2, width, "State", stateRows);
3087
+ }
3088
+ if (options.mode !== "overview" && ownerRows.length > 0) {
3089
+ cursor = renderRows(lines, x, cursor + 2, width, "Owner", ownerRows);
3090
+ }
3091
+ lines.push("</g>");
3092
+ return lines.join("\n");
3093
+ };
3094
+ var renderGenericNode = (layoutNode, graph, options, index, semantic) => {
3095
+ const { node, width, height } = layoutNode;
3096
+ const x = layoutNode.x + graph.offsetX;
3097
+ const y = layoutNode.y + graph.offsetY;
3098
+ const palette = nodePalette2(node);
3099
+ const title = fit2(semanticNodeTitle(node, options), 34);
3100
+ const subtitle = semanticNodeSubtitle(node, options);
3101
+ const chips = usefulChips(node, options);
3102
+ let cursor = y + 60;
3103
+ const lines = baseNodeOpen(
3104
+ index,
3105
+ node,
3106
+ semantic,
3107
+ options,
3108
+ x,
3109
+ y,
3110
+ width,
3111
+ height,
3112
+ palette
3113
+ );
3114
+ lines.push(
3115
+ `<rect x="${x}" y="${y}" width="${width}" height="50" rx="8" fill="${palette.header}"/>`,
3116
+ `<text x="${x + 14}" y="${y + 21}" font-family="Inter, Arial" font-size="13" font-weight="700" fill="#0f172a">${xmlText2(title)}</text>`
3117
+ );
3118
+ if (subtitle) {
3119
+ lines.push(
3120
+ `<text x="${x + 14}" y="${y + 38}" font-family="ui-monospace, SFMono-Regular, monospace" font-size="10" fill="#475569">${xmlText2(fit2(subtitle, 34))}</text>`
3121
+ );
3122
+ }
3123
+ if (chips.length > 0) {
3124
+ cursor = renderChips(lines, x + 14, cursor, chips, width - 28) + 8;
3125
+ }
3126
+ for (const section2 of visibleSemanticSections(node, options)) {
3127
+ const rows = visibleSemanticRows(section2, node, options).map((row) => ({
3128
+ ...row,
3129
+ label: semanticFieldLabel(row, options),
3130
+ value: semanticFieldValue(row, options)
3131
+ }));
3132
+ if (rows.length === 0) continue;
3133
+ cursor = renderRows(lines, x, cursor, width, section2.title, rows);
3134
+ }
3135
+ lines.push("</g>");
3136
+ return lines.join("\n");
3137
+ };
3138
+ var baseNodeOpen = (index, node, semantic, options, x, y, width, height, palette) => [
3139
+ `<g id="txg-node-${index}" data-txg-id="${opaqueId2(semantic, node.id)}" data-txg-type="node" class="txg-node txg-${node.kind}">`,
3140
+ `<title>${xmlText2(nodeTitle(node, options))}</title>`,
3141
+ `<rect x="${x}" y="${y}" width="${width}" height="${height}" rx="8" fill="#ffffff" stroke="${palette.border}" stroke-width="${node.severity ? "1.8" : "1.1"}"/>`
3142
+ ];
3143
+ var renderRows = (lines, x, y, width, title, rows) => {
3144
+ if (rows.length === 0) return y;
3145
+ lines.push(
3146
+ `<line x1="${x + 12}" y1="${y - 9}" x2="${x + width - 12}" y2="${y - 9}" stroke="#e5e7eb"/>`,
3147
+ `<text x="${x + 14}" y="${y}" font-family="Inter, Arial" font-size="9" font-weight="800" fill="#475569" letter-spacing="0">${xmlText2(title)}</text>`
3148
+ );
3149
+ let cursor = y + 19;
3150
+ for (const row of rows) {
3151
+ const label = fit2(row.label, 14);
3152
+ const value = fit2(row.value, row.mono ? 26 : 24);
3153
+ lines.push(
3154
+ `<text x="${x + 16}" y="${cursor}" font-family="Inter, Arial" font-size="10" fill="#64748b">${xmlText2(label)}</text>`,
3155
+ `<text x="${x + 118}" y="${cursor}" font-family="${row.mono ? "ui-monospace, SFMono-Regular, monospace" : "Inter, Arial"}" font-size="10" fill="#111827">${xmlText2(value)}</text>`
3156
+ );
3157
+ cursor += 21;
3158
+ }
3159
+ return cursor + 8;
3160
+ };
3161
+ var renderChips = (lines, x, y, chips, maxWidth) => {
3162
+ let cursorX = x;
3163
+ let cursorY = y;
3164
+ for (const chip of chips) {
3165
+ const label = fit2(chip.label, 18);
3166
+ const width = Math.max(42, label.length * 6 + 18);
3167
+ if (cursorX + width > x + maxWidth) {
3168
+ cursorX = x;
3169
+ cursorY += 24;
3170
+ }
3171
+ const palette = chipPalette(chip);
3172
+ lines.push(
3173
+ `<rect x="${cursorX}" y="${cursorY - 13}" width="${width}" height="19" rx="9.5" fill="${palette.fill}" stroke="${palette.stroke}"/>`,
3174
+ `<text x="${cursorX + width / 2}" y="${cursorY}" text-anchor="middle" font-family="Inter, Arial" font-size="9" font-weight="700" fill="${palette.text}">${xmlText2(label)}</text>`
3175
+ );
3176
+ cursorX += width + 6;
3177
+ }
3178
+ return cursorY + 10;
3179
+ };
3180
+ var transactionMetrics = (node, options) => visibleSemanticSections(node, options).filter(
3181
+ (section2) => ["inputs", "assets", "authority", "redeemers"].includes(section2.id)
3182
+ ).flatMap(
3183
+ (section2) => visibleSemanticRows(section2, node, options).map((row) => ({
3184
+ ...row,
3185
+ label: metricLabel(row.label)
3186
+ }))
3187
+ ).filter((row) => row.value !== "0");
3188
+ var sectionRows2 = (node, options, sectionId) => visibleSemanticSections(node, options).find((section2) => section2.id === sectionId)?.rows.map((row) => ({
3189
+ ...row,
3190
+ label: semanticFieldLabel(row, options),
3191
+ value: semanticFieldValue(row, options)
3192
+ })) ?? [];
3193
+ var usefulChips = (node, options) => {
3194
+ const labels = new Set(semanticChipLabels(node, options));
3195
+ const chips = node.chips.filter((chip) => {
3196
+ if (node.kind === "utxo" && chip.label === "resolved") return false;
3197
+ return labels.has(chip.label);
3198
+ });
3199
+ if (isGenesisNode(node) && !chips.some((chip) => chip.label === "genesis")) {
3200
+ return [{ label: "genesis", tone: "warning" }, ...chips];
3201
+ }
3202
+ return chips;
3203
+ };
3204
+ var metricLabel = (label) => {
3205
+ switch (label) {
3206
+ case "Mint policies":
3207
+ return "Mints";
3208
+ case "Certificates":
3209
+ return "Certs";
3210
+ case "Withdrawals":
3211
+ return "Wdrls";
3212
+ default:
3213
+ return label;
3214
+ }
3215
+ };
3216
+ var nodeTitle = (node, options) => [
3217
+ semanticNodeTitle(node, options),
3218
+ semanticNodeSubtitle(node, options),
3219
+ ...usefulChips(node, options).map((chip) => chip.label)
3220
+ ].filter(Boolean).join("\n");
3221
+ var opaqueId2 = (graph, id) => {
3222
+ const nodeIndex = graph.nodes.findIndex((node) => node.id === id);
3223
+ if (nodeIndex >= 0) return `node:${nodeIndex}`;
3224
+ const edgeIndex = graph.edges.findIndex((edge) => edge.id === id);
3225
+ return edgeIndex >= 0 ? `edge:${edgeIndex}` : id;
3226
+ };
3227
+ var nodePalette2 = (node) => {
3228
+ if (node.severity === "error")
3229
+ return { header: "#fee2e2", border: "#dc2626" };
3230
+ if (isGenesisNode(node)) return { header: "#fef3c7", border: "#d97706" };
3231
+ switch (node.kind) {
3232
+ case "transaction":
3233
+ return { header: "#eef2ff", border: "#6366f1" };
3234
+ case "utxo":
3235
+ return { header: "#ecfdf5", border: "#10b981" };
3236
+ case "asset":
3237
+ return { header: "#f5f3ff", border: "#7c3aed" };
3238
+ case "withdrawal":
3239
+ return { header: "#ecfeff", border: "#0891b2" };
3240
+ case "certificate":
3241
+ return { header: "#eef2ff", border: "#4f46e5" };
3242
+ case "collateralReturn":
3243
+ return { header: "#fffbeb", border: "#f59e0b" };
3244
+ case "signer":
3245
+ return { header: "#f8fafc", border: "#94a3b8" };
3246
+ case "diagnostic":
3247
+ return { header: "#fef2f2", border: "#dc2626" };
3248
+ }
3249
+ };
3250
+ var chipPalette = (chip) => {
3251
+ switch (chip.tone) {
3252
+ case "danger":
3253
+ return { fill: "#fee2e2", stroke: "#fca5a5", text: "#991b1b" };
3254
+ case "warning":
3255
+ return { fill: "#fef3c7", stroke: "#fbbf24", text: "#92400e" };
3256
+ case "success":
3257
+ return { fill: "#dcfce7", stroke: "#86efac", text: "#166534" };
3258
+ case "accent":
3259
+ return { fill: "#ede9fe", stroke: "#c4b5fd", text: "#5b21b6" };
3260
+ case "info":
3261
+ return { fill: "#dbeafe", stroke: "#93c5fd", text: "#1e40af" };
3262
+ default:
3263
+ return { fill: "#f8fafc", stroke: "#cbd5e1", text: "#334155" };
3264
+ }
3265
+ };
3266
+ var statusFill = (node) => node.severity === "error" ? "#fee2e2" : "#ffffff";
3267
+ var statusStroke = (node) => node.severity === "error" ? "#fca5a5" : "#c7d2fe";
3268
+ var statusText = (node) => node.severity === "error" ? "#991b1b" : "#3730a3";
3269
+ var fit2 = (value, maxChars) => value.length <= maxChars ? value : `${value.slice(0, maxChars - 3)}...`;
3270
+ var xmlText2 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
3271
+
3272
+ // src/render/svg.ts
3273
+ var MARGIN2 = 40;
3274
+ var traceToSvg = (trace, options = {}) => {
3275
+ const resolved = resolveVisualRendererOptions(options);
3276
+ const semantic = formatSemanticGraph(
3277
+ buildSemanticRenderGraph(trace, { redeemers: options.redeemers }),
3278
+ resolved
3279
+ );
3280
+ const layout = layoutGraph(trace, semantic, resolved);
3281
+ const title = options.title ?? "Lucid Evolution Transaction Graph";
3282
+ const diagnostics = semantic.nodes.filter(
3283
+ (node) => node.kind === "diagnostic"
3284
+ );
3285
+ const semanticNodes = new Map(
3286
+ [...layout.nodes.entries()].map(([id, layoutNode]) => [
3287
+ id,
3288
+ layoutNode.node
3289
+ ])
3290
+ );
3291
+ return [
3292
+ `<svg xmlns="http://www.w3.org/2000/svg" role="img" width="${layout.width}" height="${layout.height}" viewBox="0 0 ${layout.width} ${layout.height}">`,
3293
+ `<title>${xmlText3(title)}</title>`,
3294
+ "<defs>",
3295
+ '<marker id="txg-arrow" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth"><path d="M0,0 L0,6 L9,3 z" fill="context-stroke"/></marker>',
3296
+ "</defs>",
3297
+ `<rect width="100%" height="100%" fill="#ffffff"/>`,
3298
+ renderTitle(title, trace, diagnostics.length),
3299
+ ...semantic.edges.map(
3300
+ (edge, index) => renderEdge(trace, edge, index, layout, resolved, semanticNodes, semantic)
3301
+ ),
3302
+ ...[...layout.nodes.values()].sort((left, right) => left.node.id.localeCompare(right.node.id)).map(
3303
+ (node, index) => renderNode(node, layout, resolved, index, semantic)
3304
+ ),
3305
+ renderLegend(layout, semantic),
3306
+ "</svg>"
3307
+ ].join("\n");
3308
+ };
3309
+ var semanticSvgForTesting = (trace, options = {}) => {
3310
+ const resolved = resolveVisualRendererOptions(options);
3311
+ const semantic = formatSemanticGraph(
3312
+ buildSemanticRenderGraph(trace, { redeemers: options.redeemers }),
3313
+ resolved
3314
+ );
3315
+ return { semantic, nodes: semantic.nodes };
3316
+ };
3317
+ var renderTitle = (title, trace, diagnosticCount) => [
3318
+ `<text x="${MARGIN2}" y="34" font-family="Inter, Arial" font-size="20" font-weight="700" fill="#111827">${xmlText3(title)}</text>`,
3319
+ `<text x="${MARGIN2}" y="58" font-family="Inter, Arial" font-size="12" fill="#475569">${trace.transactions.length} transaction${trace.transactions.length === 1 ? "" : "s"} \xB7 ${Object.keys(trace.utxos).length} UTxOs \xB7 ${diagnosticCount} diagnostic${diagnosticCount === 1 ? "" : "s"}</text>`
3320
+ ].join("\n");
3321
+ var xmlText3 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
3322
+
3323
+ // src/render/html.ts
3324
+ var traceToHtml = (trace, options = {}) => {
3325
+ const resolved = resolveVisualRendererOptions(options);
3326
+ const semantic = formatSemanticGraph(
3327
+ buildSemanticRenderGraph(trace, { redeemers: options.redeemers }),
3328
+ resolved
3329
+ );
3330
+ const title = options.title ?? "Lucid Evolution Transaction Graph";
3331
+ const nodeIds = new Map(
3332
+ semantic.nodes.map((node, index) => [node.id, `node:${index}`])
3333
+ );
3334
+ const metadata = {
3335
+ nodes: semantic.nodes.map(
3336
+ (node, index) => nodeMetadata(node, index, resolved, options.includeRawMetadata ?? false)
3337
+ ),
3338
+ edges: semantic.edges.map(
3339
+ (edge, index) => edgeMetadata(
3340
+ trace,
3341
+ edge,
3342
+ index,
3343
+ nodeIds,
3344
+ resolved,
3345
+ options.includeRawMetadata ?? false
3346
+ )
3347
+ )
3348
+ };
3349
+ const filterKinds = [
3350
+ ...new Set(semantic.edges.map((edge) => edge.kind))
3351
+ ].sort();
3352
+ return [
3353
+ "<!doctype html>",
3354
+ '<html lang="en">',
3355
+ "<head>",
3356
+ '<meta charset="utf-8"/>',
3357
+ '<meta name="viewport" content="width=device-width, initial-scale=1"/>',
3358
+ `<title>${htmlText2(title)}</title>`,
3359
+ `<style>${cssText()}</style>`,
3360
+ "</head>",
3361
+ "<body>",
3362
+ '<main class="txg-shell">',
3363
+ '<aside class="txg-sidebar">',
3364
+ `<h1>${htmlText2(title)}</h1>`,
3365
+ '<input id="txg-search" type="search" placeholder="Search tx, UTxO, asset, action..." autocomplete="off"/>',
3366
+ '<div class="txg-filters">',
3367
+ ...filterKinds.map(
3368
+ (kind) => `<label><input type="checkbox" data-kind="${kind}" checked/> ${kind}</label>`
3369
+ ),
3370
+ "</div>",
3371
+ `<div class="txg-sidebar-legend"><h2>Legend</h2>${edgeLegendHtml(filterKinds)}</div>`,
3372
+ '<section id="txg-details" class="txg-details">Select a node or edge.</section>',
3373
+ "</aside>",
3374
+ '<section class="txg-canvas">',
3375
+ '<div class="txg-toolbar"><button id="txg-zoom-in">+</button><button id="txg-zoom-out">-</button><button id="txg-fit">Fit</button><button id="txg-reset">Reset</button><label>Inspector <select id="txg-detail-mode"><option value="overview">overview</option><option value="audit" selected>audit</option><option value="debug">debug</option></select></label><button id="txg-copy">Copy JSON</button></div>',
3376
+ `<div id="txg-viewport" class="txg-viewport">${traceToSvg(trace, options)}</div>`,
3377
+ "</section>",
3378
+ "</main>",
3379
+ `<script type="application/json" id="txg-data">${scriptJson(metadata)}</script>`,
3380
+ `<script>${clientScript()}</script>`,
3381
+ "</body>",
3382
+ "</html>"
3383
+ ].join("\n");
3384
+ };
3385
+ var nodeMetadata = (node, index, resolved, includeRawMetadata) => ({
3386
+ id: `node:${index}`,
3387
+ graphId: includeRawMetadata ? node.id : void 0,
3388
+ kind: node.kind,
3389
+ title: semanticNodeTitle(node, resolved),
3390
+ subtitle: semanticNodeSubtitle(node, resolved),
3391
+ chips: semanticChipLabels(node, resolved),
3392
+ sections: visibleSemanticSections(node, resolved).map((section2) => ({
3393
+ title: section2.title,
3394
+ rows: visibleSemanticRows(section2, node, resolved).map((row) => ({
3395
+ label: semanticFieldLabel(row, resolved),
3396
+ value: semanticFieldValue(row, resolved)
3397
+ }))
3398
+ })),
3399
+ ...includeRawMetadata ? { rawRef: node.rawRef } : {}
3400
+ });
3401
+ var edgeMetadata = (trace, edge, index, nodeIds, resolved, includeRawMetadata) => ({
3402
+ id: `edge:${index}`,
3403
+ graphId: includeRawMetadata ? edge.id : void 0,
3404
+ kind: edge.kind,
3405
+ title: semanticEdgeLabel(trace, edge, resolved),
3406
+ from: includeRawMetadata ? edge.from : nodeIds.get(edge.from),
3407
+ to: includeRawMetadata ? edge.to : nodeIds.get(edge.to),
3408
+ action: edge.action,
3409
+ redeemerKey: edge.redeemerKey,
3410
+ ...includeRawMetadata ? { targetRef: edge.targetRef, rawRef: edge.rawRef } : {}
3411
+ });
3412
+ var edgeLegendHtml = (kinds) => kinds.map((kind) => {
3413
+ const style = visualEdgeStyle(kind);
3414
+ return [
3415
+ '<div class="txg-legend-row">',
3416
+ `<svg width="42" height="14" viewBox="0 0 42 14" aria-hidden="true"><line x1="2" y1="7" x2="36" y2="7" stroke="${style.color}" stroke-width="2.4"${htmlDashArray(style.style)}/></svg>`,
3417
+ `<span>${htmlText2(kind)}</span>`,
3418
+ "</div>"
3419
+ ].join("");
3420
+ }).join("");
3421
+ var htmlDashArray = (style) => style === "solid" ? "" : style === "dotted" ? ' stroke-dasharray="2 4"' : ' stroke-dasharray="6 5"';
3422
+ var cssText = () => `
3423
+ html, body { height: 100%; }
3424
+ body { margin: 0; overflow: hidden; font-family: Inter, Arial, sans-serif; color: #111827; background: #f8fafc; }
3425
+ .txg-shell { display: grid; grid-template-columns: 320px 1fr; height: 100vh; min-height: 0; }
3426
+ .txg-sidebar { position: sticky; top: 0; max-height: 100vh; border-right: 1px solid #d1d5db; background: #ffffff; padding: 16px; overflow: auto; }
3427
+ .txg-sidebar h1 { font-size: 16px; margin: 0 0 14px; }
3428
+ #txg-search { box-sizing: border-box; width: 100%; padding: 8px 10px; border: 1px solid #cbd5e1; border-radius: 6px; }
3429
+ .txg-filters { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 10px; margin: 14px 0; font-size: 12px; }
3430
+ .txg-sidebar-legend { border-top: 1px solid #e5e7eb; padding-top: 12px; margin-bottom: 12px; }
3431
+ .txg-sidebar-legend h2 { font-size: 12px; margin: 0 0 8px; color: #111827; }
3432
+ .txg-legend-row { display: inline-flex; align-items: center; gap: 6px; min-width: 132px; margin: 0 6px 6px 0; font-size: 11px; color: #374151; }
3433
+ .txg-details { border-top: 1px solid #e5e7eb; padding-top: 12px; font-size: 12px; line-height: 1.45; }
3434
+ .txg-details h2 { font-size: 14px; margin: 0 0 8px; }
3435
+ .txg-details table { border-collapse: collapse; width: 100%; margin: 8px 0; }
3436
+ .txg-details td { border-top: 1px solid #e5e7eb; padding: 4px 0; vertical-align: top; }
3437
+ .txg-details td:first-child { color: #64748b; width: 38%; }
3438
+ .txg-canvas { display: grid; grid-template-rows: auto 1fr; min-width: 0; min-height: 0; overflow: hidden; }
3439
+ .txg-toolbar { display: flex; gap: 8px; padding: 10px; border-bottom: 1px solid #d1d5db; background: #ffffff; }
3440
+ .txg-toolbar button { border: 1px solid #cbd5e1; background: #ffffff; border-radius: 6px; padding: 6px 10px; }
3441
+ .txg-toolbar label { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: #475569; }
3442
+ .txg-toolbar select { border: 1px solid #cbd5e1; border-radius: 6px; padding: 5px 8px; background: #ffffff; }
3443
+ .txg-viewport { min-height: 0; overflow: auto; padding: 18px; transform-origin: top left; cursor: grab; }
3444
+ .txg-viewport.txg-panning { cursor: grabbing; user-select: none; }
3445
+ .txg-viewport svg { background: #ffffff; box-shadow: 0 1px 4px rgba(15, 23, 42, 0.12); }
3446
+ .txg-hidden { opacity: 0.08; pointer-events: none; }
3447
+ .txg-match rect:first-of-type { stroke-width: 3px; }
3448
+ .txg-selected rect:first-of-type { stroke-width: 3px; stroke: #0f172a; }
3449
+ .txg-connected path { stroke-width: 3px; }
3450
+ .txg-details pre { white-space: pre-wrap; word-break: break-word; background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 6px; padding: 8px; }
3451
+ @media (max-width: 800px) { body { overflow: auto; } .txg-shell { grid-template-columns: 1fr; height: auto; min-height: 100vh; } .txg-sidebar { position: static; max-height: none; border-right: 0; border-bottom: 1px solid #d1d5db; } .txg-canvas { min-height: 70vh; } }
3452
+ `;
3453
+ var clientScript = () => `
3454
+ const data = JSON.parse(document.getElementById("txg-data").textContent);
3455
+ let selected = null;
3456
+ let zoom = 1;
3457
+ const viewport = document.getElementById("txg-viewport");
3458
+ const svg = viewport.querySelector("svg");
3459
+ const details = document.getElementById("txg-details");
3460
+ const search = document.getElementById("txg-search");
3461
+ const detailMode = document.getElementById("txg-detail-mode");
3462
+ const checks = [...document.querySelectorAll("[data-kind]")];
3463
+ const byId = new Map([...data.nodes, ...data.edges].map((item) => [item.id, item]));
3464
+ const itemElements = new Map([...document.querySelectorAll(".txg-node,.txg-edge")].map((el) => [el.dataset.txgId, el]));
3465
+
3466
+ const setZoom = (nextZoom) => {
3467
+ zoom = Math.max(0.25, Math.min(2.5, nextZoom));
3468
+ viewport.style.zoom = zoom;
3469
+ };
3470
+
3471
+ const itemBox = (item) => {
3472
+ const el = item ? itemElements.get(item.id) : null;
3473
+ if (!el || !el.getBBox) return null;
3474
+ try { return el.getBBox(); } catch (_error) { return null; }
3475
+ };
3476
+
3477
+ const centerBox = (box) => {
3478
+ if (!box) return;
3479
+ viewport.scrollLeft = Math.max(0, (box.x + box.width / 2) * zoom - viewport.clientWidth / 2);
3480
+ viewport.scrollTop = Math.max(0, (box.y + box.height / 2) * zoom - viewport.clientHeight / 2);
3481
+ };
3482
+
3483
+ const primaryTransaction = () =>
3484
+ data.nodes.find((node) => node.kind === "transaction" && node.chips?.includes("failed")) ||
3485
+ [...data.nodes].reverse().find((node) => node.kind === "transaction") ||
3486
+ data.nodes[0];
3487
+
3488
+ const fitToView = (overview = false) => {
3489
+ if (!svg?.viewBox?.baseVal) return;
3490
+ const viewBox = svg.viewBox.baseVal;
3491
+ const widthZoom = (viewport.clientWidth - 36) / Math.max(1, viewBox.width);
3492
+ const readableFloor = viewport.clientWidth < 720 ? 0.55 : 0.72;
3493
+ setZoom(
3494
+ overview
3495
+ ? Math.min(1, Math.max(0.35, widthZoom))
3496
+ : Math.min(1, Math.max(readableFloor, widthZoom)),
3497
+ );
3498
+ requestAnimationFrame(() => centerBox(itemBox(primaryTransaction())));
3499
+ };
3500
+
3501
+ const renderDetails = (item) => {
3502
+ selected = item;
3503
+ if (!item) { details.textContent = "Select a node or edge."; return; }
3504
+ const mode = detailMode.value;
3505
+ const summaryRows = [["kind", item.kind], ...(item.subtitle ? [["subtitle", item.subtitle]] : []), ...(item.chips?.length ? [["tags", item.chips.join(", ")]] : [])];
3506
+ const auditRows = item.sections ? item.sections.flatMap((section) => section.rows.map((row) => [section.title + "." + row.label, row.value])) : Object.entries(item).filter(([key]) => !["sections"].includes(key)).map(([key, value]) => [key, typeof value === "string" ? value : JSON.stringify(value)]);
3507
+ if (mode === "debug") {
3508
+ details.innerHTML = "<h2>" + escapeHtml(item.title || item.id) + "</h2><pre>" + escapeHtml(JSON.stringify(item, null, 2)) + "</pre>";
3509
+ return;
3510
+ }
3511
+ const rows = mode === "overview" ? summaryRows : [...summaryRows, ...auditRows];
3512
+ details.innerHTML = "<h2>" + escapeHtml(item.title || item.id) + "</h2>" + "<table>" + rows.map(([key, value]) => "<tr><td>" + escapeHtml(String(key)) + "</td><td>" + escapeHtml(String(value)) + "</td></tr>").join("") + "</table>";
3513
+ };
3514
+
3515
+ const textFor = (item) => JSON.stringify(item).toLowerCase();
3516
+ const applyFilters = () => {
3517
+ const query = search.value.toLowerCase();
3518
+ const enabled = new Set(checks.filter((check) => check.checked).map((check) => check.dataset.kind));
3519
+ const visibleEdgeIds = new Set(data.edges.filter((edge) => enabled.has(edge.kind) && (!query || textFor(edge).includes(query))).map((edge) => edge.id));
3520
+ const matchingNodeIds = new Set(data.nodes.filter((node) => query && textFor(node).includes(query)).map((node) => node.id));
3521
+ document.querySelectorAll(".txg-edge").forEach((edge) => {
3522
+ edge.classList.toggle("txg-hidden", !visibleEdgeIds.has(edge.dataset.txgId));
3523
+ });
3524
+ document.querySelectorAll(".txg-node").forEach((node) => {
3525
+ node.classList.toggle("txg-match", matchingNodeIds.has(node.dataset.txgId));
3526
+ });
3527
+ };
3528
+
3529
+ document.querySelectorAll(".txg-node,.txg-edge").forEach((el) => {
3530
+ el.addEventListener("click", () => {
3531
+ const item = byId.get(el.dataset.txgId) || null;
3532
+ renderDetails(item);
3533
+ document.querySelectorAll(".txg-selected,.txg-connected").forEach((candidate) => candidate.classList.remove("txg-selected", "txg-connected"));
3534
+ el.classList.add("txg-selected");
3535
+ if (item?.from) itemElements.get(item.from)?.classList.add("txg-connected");
3536
+ if (item?.to) itemElements.get(item.to)?.classList.add("txg-connected");
3537
+ if (item?.id?.startsWith("node:")) {
3538
+ data.edges.filter((edge) => edge.from === item.id || edge.to === item.id).forEach((edge) => itemElements.get(edge.id)?.classList.add("txg-connected"));
3539
+ }
3540
+ });
3541
+ });
3542
+ search.addEventListener("input", applyFilters);
3543
+ checks.forEach((check) => check.addEventListener("change", applyFilters));
3544
+ detailMode.addEventListener("change", () => renderDetails(selected));
3545
+ document.getElementById("txg-zoom-in").addEventListener("click", () => setZoom(zoom + 0.1));
3546
+ document.getElementById("txg-zoom-out").addEventListener("click", () => setZoom(zoom - 0.1));
3547
+ document.getElementById("txg-fit").addEventListener("click", () => fitToView(true));
3548
+ document.getElementById("txg-reset").addEventListener("click", () => { search.value = ""; checks.forEach((check) => check.checked = true); applyFilters(); fitToView(false); });
3549
+ document.getElementById("txg-copy").addEventListener("click", async () => { if (selected) await navigator.clipboard.writeText(JSON.stringify(selected, null, 2)); });
3550
+
3551
+ let panStart = null;
3552
+ viewport.addEventListener("pointerdown", (event) => {
3553
+ if (event.target.closest(".txg-node,.txg-edge")) return;
3554
+ panStart = { x: event.clientX, y: event.clientY, left: viewport.scrollLeft, top: viewport.scrollTop };
3555
+ viewport.classList.add("txg-panning");
3556
+ viewport.setPointerCapture(event.pointerId);
3557
+ });
3558
+ viewport.addEventListener("pointermove", (event) => {
3559
+ if (!panStart) return;
3560
+ viewport.scrollLeft = panStart.left - (event.clientX - panStart.x);
3561
+ viewport.scrollTop = panStart.top - (event.clientY - panStart.y);
3562
+ });
3563
+ viewport.addEventListener("pointerup", (event) => {
3564
+ panStart = null;
3565
+ viewport.classList.remove("txg-panning");
3566
+ viewport.releasePointerCapture(event.pointerId);
3567
+ });
3568
+ window.addEventListener("resize", () => fitToView(false));
3569
+ requestAnimationFrame(() => fitToView(false));
3570
+
3571
+ const escapeHtml = (value) => value.replace(/[&<>"]/g, (char) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", "\\"": "&quot;" }[char]));
3572
+ `;
3573
+ var htmlText2 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
3574
+ var scriptJson = (value) => JSON.stringify(value).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
3575
+
3576
+ // src/render/mermaid.ts
3577
+ var traceToMermaid = (trace, options = {}) => {
3578
+ if (options.legacy) return traceToLegacyMermaid(trace, options);
3579
+ const resolved = resolveVisualRendererOptions(options);
3580
+ const graph = formatSemanticGraph(
3581
+ buildSemanticRenderGraph(trace, {
3582
+ redeemers: options.redeemers
3583
+ }),
3584
+ resolved
3585
+ );
3586
+ const nodeIds = new Map(
3587
+ graph.nodes.map((node, index) => [node.id, `n${index}`])
3588
+ );
3589
+ const semanticNodes = new Map(
3590
+ graph.nodes.map((node) => [node.id, node])
3591
+ );
3592
+ const lines = [
3593
+ `flowchart ${options.direction ?? "LR"}`,
3594
+ " classDef tx fill:#eef2ff,stroke:#6366f1,color:#111827;",
3595
+ " classDef utxo fill:#ecfdf5,stroke:#10b981,color:#111827;",
3596
+ " classDef asset fill:#f5f3ff,stroke:#7c3aed,color:#111827;",
3597
+ " classDef authority fill:#f3f4f6,stroke:#6b7280,color:#111827;",
3598
+ " classDef governance fill:#eef2ff,stroke:#4f46e5,color:#111827;",
3599
+ " classDef collateral fill:#fffbeb,stroke:#f59e0b,color:#111827;",
3600
+ " classDef unresolved fill:#fee2e2,stroke:#dc2626,color:#111827;",
3601
+ " classDef genesis fill:#fef3c7,stroke:#d97706,color:#111827;"
3602
+ ];
3603
+ const linkStyles = [];
3604
+ for (const node of graph.nodes) {
3605
+ const id = nodeIds.get(node.id);
3606
+ lines.push(` ${id}${semanticNodeShape(node, resolved)}`);
3607
+ lines.push(` class ${id} ${semanticNodeClass(node)};`);
3608
+ }
3609
+ for (const [index, edge] of graph.edges.entries()) {
3610
+ const style = visualEdgeStyle(edge.kind);
3611
+ const scriptFocus = resolved.view === "scriptInteraction" && isScriptInteractionEdge(edge, semanticNodes);
3612
+ lines.push(
3613
+ ` ${nodeIds.get(edge.from)} -->|${mermaidText(
3614
+ semanticEdgeLabel(trace, edge, resolved)
3615
+ )}| ${nodeIds.get(edge.to)}`
3616
+ );
3617
+ linkStyles.push(
3618
+ ` linkStyle ${index} stroke:${style.color},color:${style.color}${scriptFocus ? ",stroke-width:3px" : ""}${style.style === "solid" ? "" : ",stroke-dasharray: 5 5"};`
3619
+ );
3620
+ }
3621
+ return `${[...lines, ...linkStyles].join("\n")}
3622
+ `;
3623
+ };
3624
+ var semanticNodeShape = (node, options) => {
3625
+ const label = mermaidText(semanticNodeLabel2(node, options));
3626
+ switch (node.kind) {
3627
+ case "transaction":
3628
+ return `["${label}"]`;
3629
+ case "utxo":
3630
+ return node.severity === "error" ? `{{"${label}"}}` : `("${label}")`;
3631
+ case "asset":
3632
+ return `(["${label}"])`;
3633
+ case "signer":
3634
+ return `{{"${label}"}}`;
3635
+ case "withdrawal":
3636
+ case "certificate":
3637
+ case "collateralReturn":
3638
+ case "diagnostic":
3639
+ return `["${label}"]`;
3640
+ }
3641
+ };
3642
+ var semanticNodeLabel2 = (node, options) => {
3643
+ const subtitle = semanticNodeSubtitle(node, options);
3644
+ const chipLabels = semanticChipLabels(node, options);
3645
+ const chips = chipLabels.length > 0 ? chipLabels.join(" | ") : void 0;
3646
+ return [
3647
+ semanticNodeTitle(node, options),
3648
+ ...subtitle ? [subtitle] : [],
3649
+ ...chips ? [chips] : [],
3650
+ ...visibleSemanticSections(node, options).map((section2) => mermaidSectionLine(section2, node, options)).filter((line) => Boolean(line))
3651
+ ].join("<br/>");
3652
+ };
3653
+ var mermaidSectionLine = (section2, node, options) => {
3654
+ const rows = visibleSemanticRows(section2, node, options);
3655
+ if (rows.length === 0) return void 0;
3656
+ return `${section2.title}: ${rows.map((row) => mermaidRowText(section2, row, options)).join(", ")}`;
3657
+ };
3658
+ var mermaidRowText = (section2, row, options) => {
3659
+ const value = semanticFieldValue(row, options);
3660
+ return section2.id === "assets" && row.id.startsWith("asset:") ? `${value} ${semanticFieldLabel(row, options)}` : `${semanticFieldLabel(row, options)} ${value}`;
3661
+ };
3662
+ var semanticNodeClass = (node) => {
3663
+ if (node.severity === "error") return "unresolved";
3664
+ if (isGenesisNode(node)) return "genesis";
3665
+ if (node.rawRef.type === "diagnostic" && node.rawRef.code === "collapsed") {
3666
+ return "authority";
3667
+ }
3668
+ switch (node.kind) {
3669
+ case "transaction":
3670
+ return "tx";
3671
+ case "utxo":
3672
+ return "utxo";
3673
+ case "asset":
3674
+ return "asset";
3675
+ case "withdrawal":
3676
+ case "signer":
3677
+ return "authority";
3678
+ case "certificate":
3679
+ return "governance";
3680
+ case "collateralReturn":
3681
+ return "collateral";
3682
+ case "diagnostic":
3683
+ return "unresolved";
3684
+ }
3685
+ };
3686
+ var traceToLegacyMermaid = (trace, options = {}) => {
3687
+ const graph = buildRenderGraph(trace);
3688
+ const nodeIds = new Map(
3689
+ graph.nodes.map((node, index) => [node.id, `n${index}`])
3690
+ );
3691
+ const lines = [
3692
+ `flowchart ${options.direction ?? "LR"}`,
3693
+ " classDef tx fill:#eef2ff,stroke:#6366f1,color:#111827;",
3694
+ " classDef utxo fill:#ecfdf5,stroke:#10b981,color:#111827;",
3695
+ " classDef unresolved fill:#fee2e2,stroke:#dc2626,color:#111827;",
3696
+ " classDef genesis fill:#fef3c7,stroke:#d97706,color:#111827;",
3697
+ " classDef external fill:#f9fafb,stroke:#9ca3af,color:#111827;"
3698
+ ];
3699
+ for (const node of graph.nodes) {
3700
+ lines.push(` ${nodeIds.get(node.id)}${legacyNodeShape2(node)}`);
3701
+ lines.push(
3702
+ ` class ${nodeIds.get(node.id)} ${node.unresolved ? "unresolved" : node.genesis ? "genesis" : legacyNodeClass(node)};`
3703
+ );
3704
+ }
3705
+ for (const edge of graph.edges) {
3706
+ const style = edgeStyle(edge.kind);
3707
+ lines.push(
3708
+ ` ${nodeIds.get(edge.from)} -->|${mermaidText(style.label)}| ${nodeIds.get(
3709
+ edge.to
3710
+ )}`
3711
+ );
3712
+ }
3713
+ return `${lines.join("\n")}
3714
+ `;
3715
+ };
3716
+ var legacyNodeShape2 = (node) => {
3717
+ const label = mermaidText(node.label);
3718
+ switch (node.kind) {
3719
+ case "transaction":
3720
+ return `["${label}"]`;
3721
+ case "utxo":
3722
+ return node.unresolved ? `{{"${label}"}}` : `("${label}")`;
3723
+ case "asset":
3724
+ return `(["${label}"])`;
3725
+ case "signer":
3726
+ return `{{"${label}"}}`;
3727
+ case "withdrawal":
3728
+ case "certificate":
3729
+ case "collateralReturn":
3730
+ case "external":
3731
+ return `["${label}"]`;
3732
+ }
3733
+ };
3734
+ var legacyNodeClass = (node) => node.kind === "transaction" ? "tx" : node.kind === "utxo" ? "utxo" : "external";
3735
+ var mermaidText = (value) => value.replace(/\\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\\/g, "\\\\").replace(/"/g, "'").replace(/\|/g, "/");
3736
+
3737
+ // src/provider-wrapper.ts
3738
+ var wrapProvider = (provider, graph, options = {}) => {
3739
+ graph.resolveWith((outRefs) => provider.getUtxosByOutRef(outRefs));
3740
+ return new Proxy(provider, {
3741
+ get(target, property, receiver) {
3742
+ if (property === "submitTx") {
3743
+ return (tx) => submitTx(target, graph, tx, options);
3744
+ }
3745
+ if (property === "evaluateTx") {
3746
+ return (tx, additionalUTxOs) => evaluateTx(target, graph, tx, additionalUTxOs, options);
3747
+ }
3748
+ const value = Reflect.get(target, property, receiver);
3749
+ return typeof value === "function" ? value.bind(target) : value;
3750
+ }
3751
+ });
3752
+ };
3753
+ var submitTx = async (provider, graph, tx, options) => {
3754
+ const parsed = await graph.record(tx, {
3755
+ label: options.submitLabel,
3756
+ status: "signed"
3757
+ });
3758
+ try {
3759
+ const txHash = await provider.submitTx(tx);
3760
+ await recordSubmitted(graph, tx, options.submitLabel, parsed.hash);
3761
+ if (txHash !== parsed.hash) {
3762
+ graph.addWarning({
3763
+ code: "provider-tx-hash-mismatch",
3764
+ message: `Provider returned transaction hash ${txHash}, but the submitted body hashes to ${parsed.hash}`,
3765
+ txHash: parsed.hash
3766
+ });
3767
+ }
3768
+ return txHash;
3769
+ } catch (error) {
3770
+ await recordFailed(graph, tx, options.submitLabel, error);
3771
+ throw error;
3772
+ }
3773
+ };
3774
+ var evaluateTx = async (provider, graph, tx, additionalUTxOs, options) => {
3775
+ if (options.recordEvaluate === false) {
3776
+ return provider.evaluateTx(tx, additionalUTxOs);
3777
+ }
3778
+ if (additionalUTxOs && additionalUTxOs.length > 0) {
3779
+ graph.addResolvedUtxos(additionalUTxOs);
3780
+ }
3781
+ await graph.record(tx, {
3782
+ label: options.evaluateLabel,
3783
+ status: "built"
3784
+ });
3785
+ try {
3786
+ const evaluation = await provider.evaluateTx(tx, additionalUTxOs);
3787
+ await recordEvaluation(graph, tx, options.evaluateLabel, evaluation);
3788
+ return evaluation;
3789
+ } catch (error) {
3790
+ await recordFailed(graph, tx, options.evaluateLabel, error);
3791
+ throw error;
3792
+ }
3793
+ };
3794
+ var recordSubmitted = async (graph, tx, label, txHash) => {
3795
+ try {
3796
+ await graph.record(tx, { label, status: "submitted" });
3797
+ } catch (error) {
3798
+ graph.addWarning(
3799
+ traceRecordWarning("trace-submit-record-error", txHash, error)
3800
+ );
3801
+ }
3802
+ };
3803
+ var recordEvaluation = async (graph, tx, label, evaluation) => {
3804
+ try {
3805
+ await graph.record(tx, {
3806
+ label,
3807
+ status: "built",
3808
+ evaluation: evaluation.map(traceEvaluationRedeemer)
3809
+ });
3810
+ } catch (error) {
3811
+ graph.addWarning(
3812
+ traceRecordWarning("trace-evaluate-record-error", void 0, error)
3813
+ );
3814
+ }
3815
+ };
3816
+ var recordFailed = async (graph, tx, label, error) => {
3817
+ try {
3818
+ await graph.record(tx, {
3819
+ label,
3820
+ status: "failed",
3821
+ failureMessage: errorMessage2(error)
3822
+ });
3823
+ } catch {
3824
+ }
3825
+ };
3826
+ var traceEvaluationRedeemer = (redeemer) => ({
3827
+ tag: redeemer.redeemer_tag,
3828
+ redeemerIndex: redeemer.redeemer_index,
3829
+ exUnits: {
3830
+ mem: redeemer.ex_units.mem.toString(),
3831
+ steps: redeemer.ex_units.steps.toString()
3832
+ }
3833
+ });
3834
+ var traceRecordWarning = (code, txHash, error) => ({
3835
+ code,
3836
+ message: errorMessage2(error),
3837
+ ...txHash ? { txHash } : {}
3838
+ });
3839
+ var errorMessage2 = (error) => error instanceof Error ? error.message : String(error);
3840
+
3841
+ // src/graph.ts
3842
+ var createTxGraph = (options = {}) => {
3843
+ const transactions = [];
3844
+ const preloadedUtxos = [];
3845
+ let utxos = /* @__PURE__ */ new Map();
3846
+ let edges = [];
3847
+ let warnings = [];
3848
+ let spentBy = /* @__PURE__ */ new Map();
3849
+ let resolutionCache = createResolutionCache();
3850
+ const externalWarnings = [];
3851
+ const resolvers = options.resolver ? [options.resolver] : [];
3852
+ const aliases = {
3853
+ assets: sortRecord({
3854
+ ...options.aliases?.assets ?? {},
3855
+ ...options.assets ?? {}
3856
+ }),
3857
+ addresses: sortRecord({
3858
+ ...options.aliases?.addresses ?? {},
3859
+ ...options.addresses ?? {}
3860
+ })
3861
+ };
3862
+ const taggers = [...options.labels ?? []];
3863
+ const graph = {
3864
+ record: async (input, recordOptions = {}) => {
3865
+ const parsed = parseTransaction(input, recordOptions);
3866
+ const nextTransaction = withFailureMessage(parsed, recordOptions);
3867
+ const existingIndex = transactions.findIndex(
3868
+ (transaction2) => transaction2.hash === nextTransaction.hash
3869
+ );
3870
+ const transaction = existingIndex >= 0 ? mergeRecordedTransaction(
3871
+ transactions[existingIndex],
3872
+ nextTransaction
3873
+ ) : nextTransaction;
3874
+ if (existingIndex >= 0) {
3875
+ transactions[existingIndex] = transaction;
3876
+ await rebuildTrace();
3877
+ } else {
3878
+ transactions.push(transaction);
3879
+ await appendTransactionToTrace(transaction);
3880
+ }
3881
+ return jsonClone(transaction);
3882
+ },
3883
+ recordCbor: async (cbor, recordOptions = {}) => graph.record(cbor, recordOptions),
3884
+ addResolvedUtxos: (resolvedUtxos) => {
3885
+ preloadedUtxos.push(...resolvedUtxos);
3886
+ resolutionCache.addResolvedUtxos(resolvedUtxos);
3887
+ },
3888
+ resolveWith: (resolver) => {
3889
+ resolvers.push(resolver);
3890
+ },
3891
+ addWarning: (warning) => {
3892
+ externalWarnings.push(jsonClone(warning));
3893
+ },
3894
+ wrapProvider: (provider, wrapperOptions = {}) => wrapProvider(provider, graph, wrapperOptions),
3895
+ toJSON: () => {
3896
+ const trace = {
3897
+ version: 1,
3898
+ ...options.createdAt ? { createdAt: options.createdAt } : {},
3899
+ transactions: transactions.map(jsonClone),
3900
+ utxos: sortedUtxoRecord(utxos),
3901
+ edges: sortedEdges2(edges),
3902
+ warnings: [...warnings, ...externalWarnings].map(jsonClone),
3903
+ aliases
3904
+ };
3905
+ return jsonClone(trace);
3906
+ },
3907
+ toSemantic: (renderOptions = {}) => buildSemanticRenderGraph(graph.toJSON(), renderOptions),
3908
+ toDot: (renderOptions = {}) => traceToDot(graph.toJSON(), renderOptions),
3909
+ toHtml: (renderOptions = {}) => traceToHtml(graph.toJSON(), renderOptions),
3910
+ toMermaid: (renderOptions = {}) => traceToMermaid(graph.toJSON(), renderOptions),
3911
+ toSvg: (renderOptions = {}) => traceToSvg(graph.toJSON(), renderOptions)
3912
+ };
3913
+ const rebuildTrace = async () => {
3914
+ utxos = /* @__PURE__ */ new Map();
3915
+ edges = [];
3916
+ warnings = [];
3917
+ spentBy = /* @__PURE__ */ new Map();
3918
+ resolutionCache = createResolutionCache();
3919
+ resolutionCache.addResolvedUtxos(preloadedUtxos);
3920
+ for (const transaction of transactions) {
3921
+ await appendTransactionToTrace(transaction);
3922
+ }
3923
+ };
3924
+ const appendTransactionToTrace = async (transaction) => {
3925
+ await resolveTransactionInputs(transaction);
3926
+ recordDoubleSpends(transaction);
3927
+ addProducedOutputs(transaction);
3928
+ addEdges(transaction);
3929
+ if (transaction.status === "failed") {
3930
+ warnings.push({
3931
+ code: "failed-transaction",
3932
+ message: transaction.failureMessage ?? `Transaction ${transaction.hash} was recorded as failed`,
3933
+ txHash: transaction.hash
3934
+ });
3935
+ }
3936
+ };
3937
+ const resolveTransactionInputs = async (transaction) => {
3938
+ const allInputs = [
3939
+ ...transaction.inputs,
3940
+ ...transaction.referenceInputs,
3941
+ ...transaction.collateralInputs
3942
+ ];
3943
+ if (allInputs.length === 0) return;
3944
+ const resolution = await resolutionCache.resolveOutRefs(allInputs, {
3945
+ provider: options.provider,
3946
+ resolver: resolvers.length > 0 ? resolveWithUserResolvers : void 0
3947
+ });
3948
+ for (const warning of resolution.warnings) {
3949
+ warnings.push({ ...warning, txHash: transaction.hash });
3950
+ }
3951
+ for (const utxo of resolution.utxos) {
3952
+ setInputUtxo(tagUtxo(utxo, "input", transaction));
3953
+ }
3954
+ const resolvedByKey = new Map(
3955
+ resolution.utxos.map((utxo) => [outRefKey(utxo), utxo])
3956
+ );
3957
+ warnUnresolvedInputs(
3958
+ transaction,
3959
+ "spend",
3960
+ transaction.inputs,
3961
+ resolvedByKey
3962
+ );
3963
+ warnUnresolvedInputs(
3964
+ transaction,
3965
+ "reference",
3966
+ transaction.referenceInputs,
3967
+ resolvedByKey
3968
+ );
3969
+ warnUnresolvedInputs(
3970
+ transaction,
3971
+ "collateral",
3972
+ transaction.collateralInputs,
3973
+ resolvedByKey
3974
+ );
3975
+ };
3976
+ const resolveWithUserResolvers = async (outRefs) => {
3977
+ const resolved = [];
3978
+ let missing = [...outRefs];
3979
+ for (const resolver of resolvers) {
3980
+ if (missing.length === 0) break;
3981
+ const resolverUtxos = await resolver(missing);
3982
+ resolved.push(...resolverUtxos);
3983
+ const resolvedKeys = new Set(resolverUtxos.map(outRefKey));
3984
+ missing = missing.filter(
3985
+ (outRef) => !resolvedKeys.has(outRefKey(outRef))
3986
+ );
3987
+ }
3988
+ return resolved;
3989
+ };
3990
+ const addProducedOutputs = (transaction) => {
3991
+ const outputs = transaction.outputs.map(
3992
+ (utxo) => tagUtxo(utxo, "output", transaction)
3993
+ );
3994
+ for (const utxo of outputs) {
3995
+ utxos.set(outRefKey(utxo), jsonClone(utxo));
3996
+ }
3997
+ if (transaction.status !== "failed") {
3998
+ resolutionCache.addTransactionOutputs({ outputs });
3999
+ }
4000
+ updateStoredTransactionOutputs(transaction.hash, outputs);
4001
+ };
4002
+ const recordDoubleSpends = (transaction) => {
4003
+ if (transaction.status === "failed") return;
4004
+ const seenInTx = /* @__PURE__ */ new Set();
4005
+ for (const input of transaction.inputs) {
4006
+ const key = outRefKey(input);
4007
+ const previousTxHash = seenInTx.has(key) ? transaction.hash : spentBy.get(key);
4008
+ if (previousTxHash) {
4009
+ warnings.push({
4010
+ code: "duplicate-spend",
4011
+ message: previousTxHash === transaction.hash ? `Transaction ${transaction.hash} spends ${key} more than once` : `Transaction ${transaction.hash} spends ${key}, already spent by ${previousTxHash}`,
4012
+ txHash: transaction.hash,
4013
+ outRef: input
4014
+ });
4015
+ }
4016
+ seenInTx.add(key);
4017
+ if (!spentBy.has(key)) spentBy.set(key, transaction.hash);
4018
+ }
4019
+ };
4020
+ const setInputUtxo = (utxo) => {
4021
+ const key = outRefKey(utxo);
4022
+ const existing = utxos.get(key);
4023
+ if (existing?.resolution === "resolved" && utxo.resolution === "unresolved") {
4024
+ return;
4025
+ }
4026
+ utxos.set(key, jsonClone(utxo));
4027
+ };
4028
+ const warnUnresolvedInputs = (transaction, kind, inputs, resolvedByKey) => {
4029
+ for (const input of inputs) {
4030
+ const resolved = resolvedByKey.get(outRefKey(input));
4031
+ if (resolved?.resolution !== "unresolved") continue;
4032
+ warnings.push({
4033
+ code: `unresolved-${kind}-input`,
4034
+ message: `Transaction ${transaction.hash} has unresolved ${kind} input ${outRefKey(input)} (${resolved.unresolvedReason ?? "unresolved"})`,
4035
+ txHash: transaction.hash,
4036
+ outRef: input
4037
+ });
4038
+ }
4039
+ };
4040
+ const addEdges = (transaction) => {
4041
+ addInputEdges2("spends", transaction.inputs, transaction);
4042
+ addInputEdges2("reads", transaction.referenceInputs, transaction);
4043
+ addInputEdges2("collateral", transaction.collateralInputs, transaction);
4044
+ for (const output of transaction.outputs) {
4045
+ edges.push({
4046
+ kind: "produces",
4047
+ from: txNodeId2(transaction.hash),
4048
+ to: utxoNodeId3(output)
4049
+ });
4050
+ }
4051
+ if (transaction.collateralReturn) {
4052
+ edges.push({
4053
+ kind: "collateralReturn",
4054
+ from: txNodeId2(transaction.hash),
4055
+ to: collateralReturnNodeId2(transaction.hash)
4056
+ });
4057
+ }
4058
+ addAssetEdges("mints", transaction.hash, transaction.mintedAssets);
4059
+ addAssetEdges("burns", transaction.hash, transaction.burnedAssets);
4060
+ for (const withdrawal of transaction.withdrawals) {
4061
+ edges.push({
4062
+ kind: "withdraws",
4063
+ from: withdrawalNodeId2(withdrawal.rewardAddress),
4064
+ to: txNodeId2(transaction.hash)
4065
+ });
4066
+ }
4067
+ for (const certificate of transaction.certificates) {
4068
+ edges.push({
4069
+ kind: "certifies",
4070
+ from: txNodeId2(transaction.hash),
4071
+ to: certificateNodeId2(transaction.hash, certificate.index)
4072
+ });
4073
+ }
4074
+ for (const signer of transaction.requiredSigners) {
4075
+ edges.push({
4076
+ kind: "requiresSigner",
4077
+ from: signerNodeId2(signer),
4078
+ to: txNodeId2(transaction.hash)
4079
+ });
4080
+ }
4081
+ };
4082
+ const addInputEdges2 = (kind, inputs, transaction) => {
4083
+ for (const input of inputs) {
4084
+ edges.push({
4085
+ kind,
4086
+ from: utxoNodeId3(input),
4087
+ to: txNodeId2(transaction.hash)
4088
+ });
4089
+ }
4090
+ };
4091
+ const addAssetEdges = (kind, txHash, assets) => {
4092
+ for (const unit of Object.keys(assets).sort()) {
4093
+ edges.push(
4094
+ kind === "mints" ? {
4095
+ kind,
4096
+ from: txNodeId2(txHash),
4097
+ to: assetNodeId(unit)
4098
+ } : {
4099
+ kind,
4100
+ from: assetNodeId(unit),
4101
+ to: txNodeId2(txHash)
4102
+ }
4103
+ );
4104
+ }
4105
+ };
4106
+ const tagUtxo = (utxo, direction, transaction) => {
4107
+ if (taggers.length === 0) return utxo;
4108
+ const tags = new Set(utxo.tags);
4109
+ for (const tagger of taggers) {
4110
+ const nextTags = tagger({
4111
+ utxo,
4112
+ direction,
4113
+ transaction,
4114
+ graph: currentTrace()
4115
+ });
4116
+ for (const tag of normalizeTags(nextTags)) {
4117
+ tags.add(tag);
4118
+ }
4119
+ }
4120
+ return {
4121
+ ...utxo,
4122
+ tags: [...tags].sort()
4123
+ };
4124
+ };
4125
+ const updateStoredTransactionOutputs = (txHash, outputs) => {
4126
+ const index = transactions.findIndex(
4127
+ (transaction) => transaction.hash === txHash
4128
+ );
4129
+ if (index >= 0) {
4130
+ transactions[index] = {
4131
+ ...transactions[index],
4132
+ outputs: outputs.map(jsonClone)
4133
+ };
4134
+ }
4135
+ };
4136
+ const currentTrace = () => ({
4137
+ version: 1,
4138
+ ...options.createdAt ? { createdAt: options.createdAt } : {},
4139
+ transactions: transactions.map(jsonClone),
4140
+ utxos: sortedUtxoRecord(utxos),
4141
+ edges: sortedEdges2(edges),
4142
+ warnings: [...warnings, ...externalWarnings].map(jsonClone),
4143
+ aliases
4144
+ });
4145
+ return graph;
4146
+ };
4147
+ var withFailureMessage = (transaction, options) => ({
4148
+ ...transaction,
4149
+ ...options.failureMessage ? { failureMessage: options.failureMessage } : {},
4150
+ ...options.evaluation ? { evaluation: [...options.evaluation] } : {}
4151
+ });
4152
+ var mergeRecordedTransaction = (existing, next) => {
4153
+ const failureMessage = next.status === "failed" ? next.failureMessage ?? existing.failureMessage : void 0;
4154
+ const evaluation = next.evaluation ?? (next.status === "failed" ? void 0 : existing.evaluation);
4155
+ return {
4156
+ ...next,
4157
+ label: next.label ?? existing.label,
4158
+ ...failureMessage !== void 0 ? { failureMessage } : {},
4159
+ ...evaluation !== void 0 ? { evaluation } : {}
4160
+ };
4161
+ };
4162
+ var sortedUtxoRecord = (utxos) => {
4163
+ const result = {};
4164
+ for (const key of [...utxos.keys()].sort()) {
4165
+ const utxo = utxos.get(key);
4166
+ if (utxo) result[key] = jsonClone(utxo);
4167
+ }
4168
+ return result;
4169
+ };
4170
+ var sortedEdges2 = (edges) => [...edges].sort((left, right) => {
4171
+ const leftKey = edgeSortKey2(left);
4172
+ const rightKey = edgeSortKey2(right);
4173
+ return leftKey.localeCompare(rightKey);
4174
+ });
4175
+ var edgeSortKey2 = (edge) => `${edge.from}\0${edge.kind}\0${edge.to}`;
4176
+ var sortRecord = (record) => {
4177
+ const result = {};
4178
+ for (const key of Object.keys(record).sort()) {
4179
+ result[key] = record[key];
4180
+ }
4181
+ return result;
4182
+ };
4183
+ var normalizeTags = (tags) => {
4184
+ if (!tags) return [];
4185
+ const values = Array.isArray(tags) ? tags : [tags];
4186
+ return values.map((tag) => tag.trim()).filter((tag) => tag.length > 0);
4187
+ };
4188
+ var jsonClone = (value) => JSON.parse(JSON.stringify(value));
4189
+ var txNodeId2 = (txHash) => `tx:${txHash}`;
4190
+ var utxoNodeId3 = (outRef) => `utxo:${outRefKey(outRef)}`;
4191
+ var assetNodeId = (unit) => `asset:${unit}`;
4192
+ var withdrawalNodeId2 = (rewardAddress) => `withdrawal:${rewardAddress}`;
4193
+ var certificateNodeId2 = (txHash, index) => `certificate:${txHash}#${index}`;
4194
+ var signerNodeId2 = (keyHash) => `signer:${keyHash}`;
4195
+ var collateralReturnNodeId2 = (txHash) => `collateral-return:${txHash}`;
4196
+
4197
+ // src/labels.ts
4198
+ var tagByAddress = (label, address) => ({ utxo }) => utxo.address === address ? label : void 0;
4199
+ var tagByCredential = (label, credential) => ({ utxo }) => credentialEquals(utxo.paymentCredential, credential) || credentialEquals(utxo.stakeCredential, credential) ? label : void 0;
4200
+ var tagByUnit = (label, unit) => ({ utxo }) => Object.hasOwn(utxo.assets, unit) ? label : void 0;
4201
+ var tagByPolicyId = (label, policyId) => ({ utxo }) => Object.keys(utxo.assets).some(
4202
+ (unit) => unit !== "lovelace" && unit.startsWith(policyId)
4203
+ ) ? label : void 0;
4204
+ var tagByDatumHash = (label, datumHash) => ({ utxo }) => utxo.datumHash === datumHash ? label : void 0;
4205
+ var tagByDatum = (label, predicate) => (context) => context.utxo.datum && predicate(context.utxo.datum, context) ? label : void 0;
4206
+ var tagByScriptRef = (label, options = {}) => ({ utxo }) => {
4207
+ if (!utxo.scriptRef) return void 0;
4208
+ if (options.type && utxo.scriptRef.type !== options.type) {
4209
+ return void 0;
4210
+ }
4211
+ if (options.hash && utxo.scriptRef.hash !== options.hash) {
4212
+ return void 0;
4213
+ }
4214
+ return label;
4215
+ };
4216
+ var tagChange = (label, options) => {
4217
+ const addresses = new Set(
4218
+ [options.changeAddress, options.walletAddress].filter(
4219
+ (address) => typeof address === "string"
4220
+ )
4221
+ );
4222
+ for (const utxo of options.knownWalletUtxos ?? []) {
4223
+ addresses.add(utxo.address);
4224
+ }
4225
+ const knownOutRefs = new Set(
4226
+ (options.knownWalletUtxos ?? []).map((utxo) => outRefKey(utxo))
4227
+ );
4228
+ return ({ direction, utxo }) => {
4229
+ if (direction === "output" && addresses.has(utxo.address)) return label;
4230
+ if (direction === "input" && knownOutRefs.has(outRefKey(utxo))) {
4231
+ return label;
4232
+ }
4233
+ return void 0;
4234
+ };
4235
+ };
4236
+ var credentialEquals = (left, right) => left?.type === right.type && left.hash === right.hash;
4237
+
4238
+ // src/render/json.ts
4239
+ var traceToJSON = (trace) => ({
4240
+ version: 1,
4241
+ ...trace.createdAt ? { createdAt: trace.createdAt } : {},
4242
+ transactions: trace.transactions.map(jsonClone2),
4243
+ utxos: Object.fromEntries(
4244
+ Object.entries(trace.utxos).sort(([left], [right]) => left.localeCompare(right)).map(([key, utxo]) => [key, jsonClone2(utxo)])
4245
+ ),
4246
+ edges: sortedEdges(trace.edges),
4247
+ warnings: trace.warnings.map(jsonClone2),
4248
+ aliases: {
4249
+ assets: sortRecord2(trace.aliases.assets),
4250
+ addresses: sortRecord2(trace.aliases.addresses)
4251
+ }
4252
+ });
4253
+ var sortRecord2 = (record) => Object.fromEntries(
4254
+ Object.entries(record).sort(([left], [right]) => left.localeCompare(right))
4255
+ );
4256
+ var jsonClone2 = (value) => JSON.parse(JSON.stringify(value));
4257
+ // Annotate the CommonJS export names for ESM import in node:
4258
+ 0 && (module.exports = {
4259
+ buildSemanticRenderGraph,
4260
+ createResolutionCache,
4261
+ createTxGraph,
4262
+ describeRedeemerByConstructor,
4263
+ describeRedeemerWith,
4264
+ genesisUtxo,
4265
+ labelRedeemer,
4266
+ outRefKey,
4267
+ parseOutRefKey,
4268
+ parseTransaction,
4269
+ parseTransactionCbor,
4270
+ producedUtxosFromTransaction,
4271
+ resolveVisualRendererOptions,
4272
+ semanticSvgForTesting,
4273
+ tagByAddress,
4274
+ tagByCredential,
4275
+ tagByDatum,
4276
+ tagByDatumHash,
4277
+ tagByPolicyId,
4278
+ tagByScriptRef,
4279
+ tagByUnit,
4280
+ tagChange,
4281
+ toTraceTxOutput,
4282
+ toTraceUtxo,
4283
+ traceToDot,
4284
+ traceToHtml,
4285
+ traceToJSON,
4286
+ traceToMermaid,
4287
+ traceToSvg,
4288
+ unresolvedUtxo,
4289
+ visualEdgeStyle,
4290
+ wrapProvider
4291
+ });
4292
+ //# sourceMappingURL=index.cjs.map