codama-renderers-dart 0.4.1 → 0.4.2

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.
@@ -211,6 +211,60 @@ function emptyTypeManifest() {
211
211
  };
212
212
  }
213
213
 
214
+ // src/utils/wellKnownAddresses.ts
215
+ var WELL_KNOWN_ADDRESSES = /* @__PURE__ */ new Map([
216
+ // Agave / Solana native programs
217
+ ["11111111111111111111111111111111", "systemProgramAddress"],
218
+ ["AddressLookupTab1e1111111111111111111111111", "addressLookupTableProgramAddress"],
219
+ ["BPFLoader2111111111111111111111111111111111", "bpfLoaderProgramAddress"],
220
+ ["BPFLoader1111111111111111111111111111111111", "bpfLoaderDeprecatedProgramAddress"],
221
+ ["BPFLoaderUpgradeab1e11111111111111111111111", "bpfLoaderUpgradeableProgramAddress"],
222
+ ["ComputeBudget111111111111111111111111111111", "computeBudgetProgramAddress"],
223
+ ["Config1111111111111111111111111111111111111", "configProgramAddress"],
224
+ ["Ed25519SigVerify111111111111111111111111111", "ed25519ProgramAddress"],
225
+ ["Feature111111111111111111111111111111111111", "featureProgramAddress"],
226
+ ["1nc1nerator11111111111111111111111111111111", "incineratorAddress"],
227
+ ["LoaderV411111111111111111111111111111111111", "loaderV4ProgramAddress"],
228
+ ["NativeLoader1111111111111111111111111111", "nativeLoaderProgramAddress"],
229
+ ["KeccakSecp256k11111111111111111111111111111", "secp256k1ProgramAddress"],
230
+ ["Secp256r1SigVerify1111111111111111111111111", "secp256r1ProgramAddress"],
231
+ ["Stake11111111111111111111111111111111111111", "stakeProgramAddress"],
232
+ ["StakeConfig1111111111111111111111111111", "stakeConfigAddress"],
233
+ ["Vote111111111111111111111111111111111111111", "voteProgramAddress"],
234
+ ["ZkTokenProof1111111111111111111111111111111", "zkTokenProofProgramAddress"],
235
+ ["ZkE1Gama1Proof11111111111111111111111111111", "zkElgamalProofProgramAddress"],
236
+ // Sysvars
237
+ ["SysvarC1ock11111111111111111111111111111111", "sysvarClockAddress"],
238
+ ["Rent11111111111111111111111111111111111111", "sysvarRentAddress"],
239
+ ["11111111111111111111111111111111111111111111", "sysvarRecentBlockhashesAddress"],
240
+ // note: 44 chars
241
+ ["SysvarFees1111111111111111111111111111111111", "sysvarFeesAddress"],
242
+ ["Sysvar1nstruct1ons11111111111111111111111111", "sysvarInstructionsAddress"],
243
+ ["SysvarEpochSchedu1e111111111111111111111111", "sysvarEpochScheduleAddress"],
244
+ ["SysvarRewards111111111111111111111111111111", "sysvarRewardsAddress"],
245
+ ["SysvarStakeHistory1111111111111111111111111", "sysvarStakeHistoryAddress"],
246
+ ["SysvarS1otHashes111111111111111111111111111", "sysvarSlotHashesAddress"],
247
+ ["SysvarS1otHistory11111111111111111111111111", "sysvarSlotHistoryAddress"],
248
+ ["Sysvar1111111111111111111111111111111111111", "sysvarOwnerAddress"],
249
+ // SPL programs
250
+ ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "tokenProgramAddress"],
251
+ ["TokenzQdBNb4qyze1S1U9AHB8MGXmNK1REkTPT5Z3Y", "token2022ProgramAddress"],
252
+ ["ATokenGPvbdGVxr1b2hvZbsiqW5xWH25ef7s3c8BnQKu", "associatedTokenProgramAddress"],
253
+ ["Memo1UhkJRfRhVq1sR7Y7Nto1s3J2mXTT3L6Wk4K2m6", "memoProgramAddress"],
254
+ ["MemoSq4gqABXKKYWVqKkV3m3kb1cTR7F2UMy9r6kFNK", "memoLegacyProgramAddress"],
255
+ // Metaplex programs
256
+ ["metaqbxxU9qX8rZXkmUJ7haCrXXz2W1PSUq1Rn3E1od", "tokenMetadataProgramAddress"],
257
+ ["BGUMAp9V1mQ2KV8t34L5u3gZsXYZaYDBdKkc1xYk1qA1", "mplBubblegumProgramAddress"],
258
+ ["auth9SigNpDKz4s8cHr3MFBPS1phbRKLSCN3aBe9sS", "authRulesProgramAddress"],
259
+ ["CoREENxT6tW1Ho6BW6W5Z4N41ci5Xz2mmrR2kErmM5TR", "metaplexCoreProgramAddress"],
260
+ ["cmtMqR4bPM9sEV3bPnJQq5s2EnYw4N5vQ8hWhEd1xzS", "splAccountCompressionProgramAddress"],
261
+ ["noopb9skMVngR5Qi4sczwFr1jU4V4F7nm8xzFQK9C", "noopProgramAddress"],
262
+ // Well-known mints
263
+ ["So11111111111111111111111111111111111111112", "wrappedSolAddress"],
264
+ ["EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "usdcAddress"],
265
+ ["Es9vMFrzaCERm6YKXx6bqKr1Rmd9b5v3J8rM5e4rfF4", "usdtAddress"]
266
+ ]);
267
+
214
268
  // src/visitors/getTypeManifestVisitor.ts
215
269
  function getTypeManifestVisitor(input) {
216
270
  const { nameApi, linkables, nonScalarEnums = [] } = input;
@@ -268,7 +322,8 @@ function getTypeManifestVisitor(input) {
268
322
  const dartType = getNumberDartType(format);
269
323
  const codecName = getNumberCodecName(format);
270
324
  return {
271
- type: dartType === "BigInt" ? use("BigInt", "dartTypedData") : fragmentFromString(dartType),
325
+ // `BigInt` and other number types live in `dart:core` and need no import.
326
+ type: fragmentFromString(dartType),
272
327
  encoder: fragment`${use(
273
328
  `get${codecName}Encoder`,
274
329
  "solanaCodecsNumbers"
@@ -498,51 +553,55 @@ function getTypeManifestVisitor(input) {
498
553
  },
499
554
  visitOptionType(node, { self }) {
500
555
  const itemManifest = visitorsCore.visit(node.item, self);
556
+ const itemTypeStr = itemManifest.type.content;
501
557
  const resolvedPrefix = nodes.resolveNestedTypeNode(node.prefix);
502
558
  const prefixExpr = resolvedPrefix.format !== "u8" ? `, prefix: ${getNumberCodecExpression(resolvedPrefix.format, "Encoder")}` : "";
503
559
  const prefixDecoderExpr = resolvedPrefix.format !== "u8" ? `, prefix: ${getNumberCodecExpression(resolvedPrefix.format, "Decoder")}` : "";
560
+ const noneValueExpr = node.fixed ? ", noneValue: const ZeroesNoneValue()" : "";
504
561
  return {
505
562
  type: fragment`${itemManifest.type}?`,
506
563
  encoder: fragment`${use(
507
564
  "getNullableEncoder",
508
565
  "solanaCodecsDataStructures"
509
- )}(${itemManifest.encoder}${fragmentFromString(prefixExpr)})`,
566
+ )}<${fragmentFromString(itemTypeStr)}>(${itemManifest.encoder}${fragmentFromString(prefixExpr)}${fragmentFromString(noneValueExpr)})`,
510
567
  decoder: fragment`${use(
511
568
  "getNullableDecoder",
512
569
  "solanaCodecsDataStructures"
513
- )}(${itemManifest.decoder}${fragmentFromString(prefixDecoderExpr)})`,
570
+ )}<${fragmentFromString(itemTypeStr)}>(${itemManifest.decoder}${fragmentFromString(prefixDecoderExpr)}${fragmentFromString(noneValueExpr)})`,
514
571
  value: emptyTypeManifest().value,
515
572
  isEnum: false
516
573
  };
517
574
  },
518
575
  visitRemainderOptionType(node, { self }) {
519
576
  const itemManifest = visitorsCore.visit(node.item, self);
577
+ const itemTypeStr = itemManifest.type.content;
520
578
  return {
521
579
  type: fragment`${itemManifest.type}?`,
522
580
  encoder: fragment`${use(
523
581
  "getNullableEncoder",
524
582
  "solanaCodecsDataStructures"
525
- )}(${itemManifest.encoder}, hasPrefix: false)`,
583
+ )}<${fragmentFromString(itemTypeStr)}>(${itemManifest.encoder}, hasPrefix: false)`,
526
584
  decoder: fragment`${use(
527
585
  "getNullableDecoder",
528
586
  "solanaCodecsDataStructures"
529
- )}(${itemManifest.decoder}, hasPrefix: false)`,
587
+ )}<${fragmentFromString(itemTypeStr)}>(${itemManifest.decoder}, hasPrefix: false)`,
530
588
  value: emptyTypeManifest().value,
531
589
  isEnum: false
532
590
  };
533
591
  },
534
592
  visitZeroableOptionType(node, { self }) {
535
593
  const itemManifest = visitorsCore.visit(node.item, self);
594
+ const itemTypeStr = itemManifest.type.content;
536
595
  return {
537
596
  type: fragment`${itemManifest.type}?`,
538
597
  encoder: fragment`${use(
539
598
  "getNullableEncoder",
540
599
  "solanaCodecsDataStructures"
541
- )}(${itemManifest.encoder}, hasPrefix: false, noneValue: const ZeroesNoneValue())`,
600
+ )}<${fragmentFromString(itemTypeStr)}>(${itemManifest.encoder}, hasPrefix: false, noneValue: const ZeroesNoneValue())`,
542
601
  decoder: fragment`${use(
543
602
  "getNullableDecoder",
544
603
  "solanaCodecsDataStructures"
545
- )}(${itemManifest.decoder}, hasPrefix: false, noneValue: const ZeroesNoneValue())`,
604
+ )}<${fragmentFromString(itemTypeStr)}>(${itemManifest.decoder}, hasPrefix: false, noneValue: const ZeroesNoneValue())`,
546
605
  value: emptyTypeManifest().value,
547
606
  isEnum: false
548
607
  };
@@ -565,7 +624,16 @@ function getTypeManifestVisitor(input) {
565
624
  },
566
625
  visitSizePrefixType(node, { self }) {
567
626
  const innerManifest = visitorsCore.visit(node.type, self);
568
- const prefixManifest = visitorsCore.visit(node.prefix, self);
627
+ const bigIntFormats = /* @__PURE__ */ new Set(["u64", "u128", "i64", "i128"]);
628
+ let prefixManifest;
629
+ if (node.prefix.kind === "numberTypeNode" && bigIntFormats.has(node.prefix.format)) {
630
+ prefixManifest = {
631
+ encoder: fragment`${use("getU32Encoder", "solanaCodecsNumbers")}()`,
632
+ decoder: fragment`${use("getU32Decoder", "solanaCodecsNumbers")}()`
633
+ };
634
+ } else {
635
+ prefixManifest = visitorsCore.visit(node.prefix, self);
636
+ }
569
637
  return {
570
638
  type: innerManifest.type,
571
639
  encoder: fragment`${use(
@@ -582,7 +650,7 @@ function getTypeManifestVisitor(input) {
582
650
  },
583
651
  visitHiddenPrefixType(node, { self }) {
584
652
  const innerManifest = visitorsCore.visit(node.type, self);
585
- const prefixes = node.prefix.map((p) => visitorsCore.visit(p, self));
653
+ const prefixes = node.prefix.map((p) => getHiddenAffixManifest(p, self));
586
654
  const prefixEncoders = prefixes.map((p) => p.encoder.content).join(", ");
587
655
  const prefixDecoders = prefixes.map((p) => p.decoder.content).join(", ");
588
656
  const encoderFrag = fragment`${use(
@@ -609,7 +677,7 @@ function getTypeManifestVisitor(input) {
609
677
  },
610
678
  visitHiddenSuffixType(node, { self }) {
611
679
  const innerManifest = visitorsCore.visit(node.type, self);
612
- const suffixes = node.suffix.map((s) => visitorsCore.visit(s, self));
680
+ const suffixes = node.suffix.map((s) => getHiddenAffixManifest(s, self));
613
681
  const suffixEncoders = suffixes.map((s) => s.encoder.content).join(", ");
614
682
  const suffixDecoders = suffixes.map((s) => s.decoder.content).join(", ");
615
683
  const encoderFrag = fragment`${use(
@@ -805,6 +873,42 @@ function getSetSizeExpression(node) {
805
873
  return "";
806
874
  }
807
875
  }
876
+ function getHiddenAffixManifest(node, self) {
877
+ if (node?.kind !== "constantValueNode") {
878
+ const manifest = visitorsCore.visit(node, self);
879
+ return { encoder: manifest.encoder, decoder: manifest.decoder };
880
+ }
881
+ const constantBytes = getConstantBytesExpression(node);
882
+ return {
883
+ encoder: fragment`${use("getConstantEncoder", "solanaCodecsDataStructures")}(${constantBytes})`,
884
+ decoder: fragment`${use("getConstantDecoder", "solanaCodecsDataStructures")}(${constantBytes})`
885
+ };
886
+ }
887
+ function getConstantBytesExpression(node) {
888
+ const valueNode = node.value;
889
+ const typeNode = node.type;
890
+ if (valueNode?.kind !== "numberValueNode") {
891
+ throw new Error(
892
+ `Unsupported hidden affix constant value kind: ${valueNode?.kind}`
893
+ );
894
+ }
895
+ const value = Number(valueNode.number);
896
+ if (typeNode?.kind === "numberTypeNode") {
897
+ return fragment`${use(`get${getNumberCodecName(typeNode.format)}Encoder`, "solanaCodecsNumbers")}().encode(${value})`;
898
+ }
899
+ if (typeNode?.kind === "preOffsetTypeNode") {
900
+ const inner = typeNode.type;
901
+ if (inner?.kind !== "numberTypeNode") {
902
+ throw new Error(
903
+ `Unsupported hidden affix pre-offset inner type kind: ${inner?.kind}`
904
+ );
905
+ }
906
+ return fragment`${use("padLeftEncoder", "solanaCodecsCore")}(${use(`get${getNumberCodecName(inner.format)}Encoder`, "solanaCodecsNumbers")}(), ${typeNode.offset}).encode(${value})`;
907
+ }
908
+ throw new Error(
909
+ `Unsupported hidden affix constant type kind: ${typeNode?.kind}`
910
+ );
911
+ }
808
912
 
809
913
  // src/fragments/discriminatorConstants.ts
810
914
  function getDiscriminatorConstantsFragment(node, scope) {
@@ -870,10 +974,14 @@ function getAccountPageFragment(node, scope) {
870
974
  return ` final ${manifest.type.content} ${camelCase(f.name)};`;
871
975
  }).join("\n");
872
976
  const ctorParams = fields.map((f) => ` required this.${camelCase(f.name)},`).join("\n");
977
+ const ctorSignature = fields.length === 0 ? `const ${typeName}();` : `const ${typeName}({
978
+ ${ctorParams}
979
+ });`;
873
980
  const eqChecks = fields.length === 0 ? "true" : fields.map(
874
981
  (f) => `${camelCase(f.name)} == other.${camelCase(f.name)}`
875
982
  ).join(" &&\n ");
876
- const hashFields = fields.map((f) => camelCase(f.name)).join(", ");
983
+ const hashFieldNames = fields.map((f) => camelCase(f.name));
984
+ const hashCodeExpr = hashFieldNames.length === 0 ? "0" : hashFieldNames.length === 1 ? `${hashFieldNames[0]}.hashCode` : `Object.hash(${hashFieldNames.join(", ")})`;
877
985
  const toStringFields = fields.map(
878
986
  (f) => `${camelCase(f.name)}: $${camelCase(f.name)}`
879
987
  ).join(", ");
@@ -920,9 +1028,7 @@ ${use("decodeAccount", "solanaAccounts")}
920
1028
 
921
1029
  @immutable
922
1030
  class ${fragmentFromString(typeName)} {
923
- const ${fragmentFromString(typeName)}({
924
- ${fragmentFromString(ctorParams)}
925
- });
1031
+ ${fragmentFromString(ctorSignature)}
926
1032
 
927
1033
  ${fragmentFromString(fieldDecls)}
928
1034
 
@@ -934,7 +1040,7 @@ ${fragmentFromString(fieldDecls)}
934
1040
  ${fragmentFromString(eqChecks)};
935
1041
 
936
1042
  @override
937
- int get hashCode => Object.hash(${fragmentFromString(hashFields)});
1043
+ int get hashCode => ${fragmentFromString(hashCodeExpr)};
938
1044
 
939
1045
  @override
940
1046
  String toString() => '${fragmentFromString(typeName)}(${fragmentFromString(toStringFields)})';
@@ -1085,13 +1191,20 @@ function getInstructionPageFragment(node, scope) {
1085
1191
  const fieldName = camelCase(arg.name);
1086
1192
  return ` final ${manifest.type.content} ${fieldName};`;
1087
1193
  }).join("\n");
1088
- const dataCtorParams = allArgManifests.map(({ arg }) => {
1194
+ const dataCtorParams = allArgManifests.map(({ arg, manifest }) => {
1089
1195
  const fieldName = camelCase(arg.name);
1090
1196
  if (isDiscriminatorArg(arg, node)) {
1091
- return ` this.${fieldName} = ${getDiscriminatorDefault(arg)},`;
1197
+ return isConstDefaultValue(arg.defaultValue) ? ` this.${fieldName} = ${getDiscriminatorDefault(arg)},` : ` ${manifest.type.content}? ${fieldName},`;
1092
1198
  }
1093
1199
  return ` required this.${fieldName},`;
1094
1200
  }).join("\n");
1201
+ const dataCtorInitializers = allArgManifests.map(({ arg }) => {
1202
+ const fieldName = camelCase(arg.name);
1203
+ if (isDiscriminatorArg(arg, node) && !isConstDefaultValue(arg.defaultValue)) {
1204
+ return ` ${fieldName} = ${fieldName} ?? ${getDiscriminatorDefault(arg)}`;
1205
+ }
1206
+ return null;
1207
+ }).filter((value) => value !== null).join(",\n");
1095
1208
  const encFields = allArgManifests.map(({ arg, manifest }) => {
1096
1209
  return ` ('${arg.name}', ${manifest.encoder.content}),`;
1097
1210
  }).join("\n");
@@ -1107,9 +1220,14 @@ function getInstructionPageFragment(node, scope) {
1107
1220
  const accessor = isNullable ? `map['${arg.name}']` : `map['${arg.name}']!`;
1108
1221
  return ` ${camelCase(arg.name)}: ${accessor} as ${typeStr},`;
1109
1222
  }).join("\n");
1223
+ const dataCtorKeyword = dataCtorInitializers ? "" : "const ";
1110
1224
  const dataEncoderName = `get${typeName}InstructionDataEncoder`;
1111
1225
  const dataDecoderName = `get${typeName}InstructionDataDecoder`;
1112
1226
  const dataCodecName = `get${typeName}InstructionDataCodec`;
1227
+ const argNames = new Set(args.map((a) => camelCase(a.name)));
1228
+ const accountNames = new Set(accounts.map((a) => camelCase(a.name)));
1229
+ const hasProgramAddressCollision = argNames.has("programAddress") || accountNames.has("programAddress");
1230
+ const instrProgramParam = hasProgramAddressCollision ? "instructionProgramAddress" : "programAddress";
1113
1231
  const accountParams = accounts.map((acc) => {
1114
1232
  const fieldName = camelCase(acc.name);
1115
1233
  const isRequired = !(acc.isOptional ?? false);
@@ -1119,11 +1237,13 @@ function getInstructionPageFragment(node, scope) {
1119
1237
  const argParams = args.map((arg) => {
1120
1238
  const manifest = argManifestMap.get(arg);
1121
1239
  const fieldName = camelCase(arg.name);
1240
+ const typeStr = manifest.type.content;
1122
1241
  const hasDefault = arg.defaultValue != null;
1123
1242
  if (hasDefault) {
1124
- return ` ${manifest.type.content}? ${fieldName},`;
1243
+ const nullableType = typeStr.endsWith("?") ? typeStr : `${typeStr}?`;
1244
+ return ` ${nullableType} ${fieldName},`;
1125
1245
  }
1126
- return ` required ${manifest.type.content} ${fieldName},`;
1246
+ return ` required ${typeStr} ${fieldName},`;
1127
1247
  }).join("\n");
1128
1248
  const accountMetas = accounts.map((acc) => {
1129
1249
  const fieldName = camelCase(acc.name);
@@ -1165,9 +1285,10 @@ ${use("AccountRole", "solanaInstructions")}`
1165
1285
  parts.push(fragment`
1166
1286
  @immutable
1167
1287
  class ${fragmentFromString(dataClassName)} {
1168
- const ${fragmentFromString(dataClassName)}({
1288
+ ${fragmentFromString(dataCtorKeyword)}${fragmentFromString(dataClassName)}({
1169
1289
  ${fragmentFromString(dataCtorParams)}
1170
- });
1290
+ })${fragmentFromString(dataCtorInitializers ? ` :
1291
+ ${dataCtorInitializers}` : "")};
1171
1292
 
1172
1293
  ${fragmentFromString(dataFieldDecls)}
1173
1294
  }`);
@@ -1204,20 +1325,20 @@ Codec<${fragmentFromString(dataClassName)}, ${fragmentFromString(dataClassName)}
1204
1325
  parts.push(fragment`
1205
1326
  /// Creates a [${fragmentFromString(typeName)}] instruction.
1206
1327
  Instruction ${fragmentFromString(instrFnName)}({
1207
- required Address programAddress,
1328
+ required Address ${fragmentFromString(instrProgramParam)},
1208
1329
  ${fragmentFromString(accountParams)}
1209
1330
  ${fragmentFromString(argParams)}
1210
1331
  }) {
1211
- final data = ${fragmentFromString(dataClassName)}(
1332
+ final instructionData = ${fragmentFromString(dataClassName)}(
1212
1333
  ${fragmentFromString(dataConstruction)}
1213
1334
  );
1214
1335
 
1215
1336
  return Instruction(
1216
- programAddress: programAddress,
1337
+ programAddress: ${fragmentFromString(instrProgramParam)},
1217
1338
  accounts: [
1218
1339
  ${fragmentFromString(accountMetas)}
1219
1340
  ],
1220
- data: ${fragmentFromString(dataEncoderName)}().encode(data),
1341
+ data: ${fragmentFromString(dataEncoderName)}().encode(instructionData),
1221
1342
  );
1222
1343
  }`);
1223
1344
  parts.push(fragment`
@@ -1252,9 +1373,26 @@ function getDiscriminatorDefault(arg, _node) {
1252
1373
  if (arg.defaultValue.kind === "numberValueNode") {
1253
1374
  return String(arg.defaultValue.number);
1254
1375
  }
1376
+ if (arg.defaultValue.kind === "bytesValueNode") {
1377
+ const data = arg.defaultValue.data;
1378
+ const bytes = data.match(/.{1,2}/g)?.map((b) => parseInt(b, 16)) ?? [];
1379
+ return `Uint8List.fromList([${bytes.join(", ")}])`;
1380
+ }
1255
1381
  }
1256
1382
  return "0";
1257
1383
  }
1384
+ function isConstDefaultValue(defaultValue) {
1385
+ if (!defaultValue) return false;
1386
+ switch (defaultValue.kind) {
1387
+ case "numberValueNode":
1388
+ case "booleanValueNode":
1389
+ case "stringValueNode":
1390
+ case "noneValueNode":
1391
+ return true;
1392
+ default:
1393
+ return false;
1394
+ }
1395
+ }
1258
1396
  function getDefaultValue(arg) {
1259
1397
  if (!arg.defaultValue) return "null";
1260
1398
  const dv = arg.defaultValue;
@@ -1265,10 +1403,20 @@ function getDefaultValue(arg) {
1265
1403
  return String(dv.boolean);
1266
1404
  case "stringValueNode":
1267
1405
  return `'${dv.string}'`;
1268
- case "publicKeyValueNode":
1406
+ case "publicKeyValueNode": {
1407
+ const wellKnownName = WELL_KNOWN_ADDRESSES.get(dv.publicKey);
1408
+ if (wellKnownName) {
1409
+ use(wellKnownName, "solanaAddresses");
1410
+ return wellKnownName;
1411
+ }
1269
1412
  return `Address('${dv.publicKey}')`;
1413
+ }
1270
1414
  case "noneValueNode":
1271
1415
  return "null";
1416
+ case "bytesValueNode": {
1417
+ const bytes = dv.data.match(/.{1,2}/g)?.map((b) => parseInt(b, 16)) ?? [];
1418
+ return `Uint8List.fromList([${bytes.join(", ")}])`;
1419
+ }
1272
1420
  default:
1273
1421
  return "null";
1274
1422
  }
@@ -1278,6 +1426,9 @@ function getPdaPageFragment(node, scope) {
1278
1426
  const findFnName = scope.nameApi.pdaFindFunction(name);
1279
1427
  const seedsClassName = `${pascalCase(name)}Seeds`;
1280
1428
  const seeds = node.seeds ?? [];
1429
+ const hasByteSeeds = seeds.some(
1430
+ (seed) => seed.kind === "constantPdaSeedNode" && seed.value.kind === "bytesValueNode"
1431
+ );
1281
1432
  const variableSeeds = seeds.filter(
1282
1433
  (s) => s.kind === "variablePdaSeedNode"
1283
1434
  );
@@ -1285,44 +1436,65 @@ function getPdaPageFragment(node, scope) {
1285
1436
  (s) => s.kind === "constantPdaSeedNode"
1286
1437
  );
1287
1438
  const hasSeedsClass = variableSeeds.length > 0;
1439
+ const seedManifests = [];
1288
1440
  const seedFieldDecls = variableSeeds.map((s) => {
1289
1441
  if (s.kind !== "variablePdaSeedNode") return "";
1290
1442
  const manifest = visitorsCore.visit(s.type, scope.typeManifestVisitor);
1443
+ seedManifests.push(manifest);
1291
1444
  return ` final ${manifest.type.content} ${camelCase(s.name)};`;
1292
1445
  }).join("\n");
1293
1446
  const seedCtorParams = variableSeeds.map((s) => {
1294
1447
  if (s.kind !== "variablePdaSeedNode") return "";
1295
1448
  return ` required this.${camelCase(s.name)},`;
1296
1449
  }).join("\n");
1297
- const seedBytesList = [];
1450
+ const seedValues = [];
1298
1451
  for (const seed of seeds) {
1299
1452
  if (seed.kind === "constantPdaSeedNode") {
1300
1453
  if (seed.value.kind === "bytesValueNode") {
1301
- seedBytesList.push(` ...${bytesValueToDart(seed.value.data)},`);
1454
+ seedValues.push(` Uint8List.fromList(${bytesValueToDart(seed.value.data)}),`);
1302
1455
  } else if (seed.value.kind === "stringValueNode") {
1303
- seedBytesList.push(` ...utf8.encode('${seed.value.string}'),`);
1456
+ seedValues.push(` '${seed.value.string}',`);
1304
1457
  } else if (seed.value.kind === "publicKeyValueNode") {
1305
- seedBytesList.push(` ...getAddressEncoder().encode(Address('${seed.value.publicKey}')),`);
1458
+ const wellKnownSeedName = WELL_KNOWN_ADDRESSES.get(seed.value.publicKey);
1459
+ if (wellKnownSeedName) {
1460
+ use(wellKnownSeedName, "solanaAddresses");
1461
+ seedValues.push(
1462
+ ` getAddressEncoder().encode(${wellKnownSeedName}),`
1463
+ );
1464
+ } else {
1465
+ seedValues.push(
1466
+ ` getAddressEncoder().encode(Address('${seed.value.publicKey}')),`
1467
+ );
1468
+ }
1306
1469
  } else if (seed.value.kind === "numberValueNode") {
1307
1470
  const manifest = visitorsCore.visit(seed.type, scope.typeManifestVisitor);
1308
- seedBytesList.push(` ...${manifest.encoder.content}.encode(${seed.value.number}),`);
1471
+ seedManifests.push(manifest);
1472
+ seedValues.push(` ${manifest.encoder.content}.encode(${seed.value.number}),`);
1309
1473
  }
1310
1474
  } else if (seed.kind === "variablePdaSeedNode") {
1311
1475
  const manifest = visitorsCore.visit(seed.type, scope.typeManifestVisitor);
1312
- seedBytesList.push(
1313
- ` ...${manifest.encoder.content}.encode(seeds.${camelCase(seed.name)}),`
1476
+ seedManifests.push(manifest);
1477
+ seedValues.push(
1478
+ ` ${manifest.encoder.content}.encode(seeds.${camelCase(seed.name)}),`
1314
1479
  );
1315
1480
  }
1316
1481
  }
1317
- const programIdParam = node.programId ? `Address programAddress = const Address('${node.programId}')` : "required Address programAddress";
1482
+ const wellKnownProgramName = node.programId ? WELL_KNOWN_ADDRESSES.get(node.programId) : void 0;
1483
+ if (wellKnownProgramName) {
1484
+ use(wellKnownProgramName, "solanaAddresses");
1485
+ }
1486
+ const programIdParam = node.programId ? wellKnownProgramName ? `Address programAddress = ${wellKnownProgramName}` : `Address programAddress = const Address('${node.programId}')` : "required Address programAddress";
1487
+ const typedDataImport = hasByteSeeds ? use("Uint8List", "dartTypedData") : fragmentFromString("");
1488
+ const metaImport = hasSeedsClass ? use("immutable", "meta") : fragmentFromString("");
1318
1489
  const parts = [
1319
1490
  fragment`// Auto-generated. Do not edit.
1320
1491
  // ignore_for_file: type=lint
1321
1492
 
1322
- ${use("Uint8List", "dartTypedData")}
1323
- ${use("immutable", "meta")}
1493
+ ${typedDataImport}
1494
+ ${metaImport}
1324
1495
  ${use("Address", "solanaAddresses")}
1325
- ${use("getAddressEncoder", "solanaAddresses")}`
1496
+ ${use("getAddressEncoder", "solanaAddresses")}
1497
+ ${use("getProgramDerivedAddress", "solanaAddresses")}`
1326
1498
  ];
1327
1499
  if (hasSeedsClass) {
1328
1500
  parts.push(fragment`
@@ -1342,14 +1514,21 @@ Future<(Address, int)> ${fragmentFromString(findFnName)}({
1342
1514
  ${fragmentFromString(seedsParam)}
1343
1515
  ${fragmentFromString(programIdParam)},
1344
1516
  }) async {
1345
- final seedBytes = <int>[
1346
- ${fragmentFromString(seedBytesList.join("\n"))}
1517
+ final seedValues = <Object>[
1518
+ ${fragmentFromString(seedValues.join("\n"))}
1347
1519
  ];
1348
1520
 
1349
- // TODO: Call getProgramDerivedAddress with seedBytes and programAddress
1350
- throw UnimplementedError('PDA derivation not yet implemented');
1521
+ return getProgramDerivedAddress(
1522
+ programAddress: programAddress,
1523
+ seeds: seedValues,
1524
+ );
1351
1525
  }`);
1352
- return mergeFragments(parts, (cs) => cs.join("\n"));
1526
+ const result = mergeFragments(parts, (cs) => cs.join("\n"));
1527
+ for (const manifest of seedManifests) {
1528
+ result.imports.mergeWith(manifest.encoder.imports);
1529
+ result.imports.mergeWith(manifest.type.imports);
1530
+ }
1531
+ return result;
1353
1532
  }
1354
1533
  function bytesValueToDart(hex) {
1355
1534
  const clean = hex.replace(/^0x/, "");
@@ -1364,15 +1543,25 @@ function bytesValueToDart(hex) {
1364
1543
  function getProgramPageFragment(node, scope) {
1365
1544
  const name = node.name;
1366
1545
  const addressConstName = scope.nameApi.programAddressConstant(name);
1367
- const parts = [
1368
- fragment`// Auto-generated. Do not edit.
1546
+ const wellKnownName = WELL_KNOWN_ADDRESSES.get(node.publicKey);
1547
+ const addressDeclaration = wellKnownName ? addressConstName === wellKnownName ? fragment`// Auto-generated. Do not edit.
1548
+ // ignore_for_file: type=lint
1549
+
1550
+ /// The address of the ${fragmentFromString(pascalCase(name))} program.
1551
+ export 'package:solana_kit_addresses/solana_kit_addresses.dart' show ${fragmentFromString(wellKnownName)};` : fragment`${use(wellKnownName, "solanaAddresses")}
1552
+
1553
+ // Auto-generated. Do not edit.
1554
+ // ignore_for_file: type=lint
1555
+
1556
+ /// The address of the ${fragmentFromString(pascalCase(name))} program.
1557
+ const ${fragmentFromString(addressConstName)} = ${fragmentFromString(wellKnownName)};` : fragment`// Auto-generated. Do not edit.
1369
1558
  // ignore_for_file: type=lint
1370
1559
 
1371
1560
  ${use("Address", "solanaAddresses")}
1372
1561
 
1373
1562
  /// The address of the ${fragmentFromString(pascalCase(name))} program.
1374
- const ${fragmentFromString(addressConstName)} = Address('${fragmentFromString(node.publicKey)}');`
1375
- ];
1563
+ const ${fragmentFromString(addressConstName)} = Address('${fragmentFromString(node.publicKey)}');`;
1564
+ const parts = [addressDeclaration];
1376
1565
  const accounts = node.accounts ?? [];
1377
1566
  if (accounts.length > 0) {
1378
1567
  const accountVariants = accounts.map((acc) => ` ${camelCase(acc.name)},`).join("\n");
@@ -1470,6 +1659,8 @@ function getDataEnumPageFragment(node, scope) {
1470
1659
  const variantClasses = [];
1471
1660
  const encoderVariants = [];
1472
1661
  const decoderVariants = [];
1662
+ const encodeCases = [];
1663
+ const decodeCases = [];
1473
1664
  const allVariantManifests = [];
1474
1665
  for (let i = 0; i < enumNode.variants.length; i++) {
1475
1666
  const variant = enumNode.variants[i];
@@ -1495,8 +1686,10 @@ function getDataEnumPageFragment(node, scope) {
1495
1686
  `(${i}, getStructEncoder(<(String, Encoder<Object?>)>[]))`
1496
1687
  );
1497
1688
  decoderVariants.push(
1498
- `(${i}, getStructDecoder(<(String, Decoder<Object?>)>[]).map((_) => const ${variantClassName}()))`
1689
+ `(${i}, transformDecoder<Map<String, Object?>, Map<String, Object?>>(getStructDecoder(<(String, Decoder<Object?>)>[]), (Map<String, Object?> map, Uint8List bytes, int offset) => <String, Object?>{}))`
1499
1690
  );
1691
+ encodeCases.push(`${variantClassName}() => <String, Object?>{'__kind': ${i}},`);
1692
+ decodeCases.push(`case ${i}: return const ${variantClassName}();`);
1500
1693
  } else if (variant.kind === "enumStructVariantTypeNode") {
1501
1694
  const resolvedStruct = nodes.resolveNestedTypeNode(variant.struct);
1502
1695
  const fields = resolvedStruct.fields;
@@ -1511,13 +1704,16 @@ function getDataEnumPageFragment(node, scope) {
1511
1704
  return ` final ${manifest.type.content} ${camelCase(f.name)};`;
1512
1705
  }).join("\n");
1513
1706
  const ctorParams = fields.map((f) => ` required this.${camelCase(f.name)},`).join("\n");
1514
- const eqChecks = fields.map((f) => `${camelCase(f.name)} == other.${camelCase(f.name)}`).join(" &&\n ");
1515
- const hashFields = fields.map((f) => camelCase(f.name)).join(", ");
1707
+ const ctorSignature = fields.length === 0 ? ` const ${variantClassName}();` : ` const ${variantClassName}({
1708
+ ${ctorParams}
1709
+ });`;
1710
+ const eqChecks = fields.length === 0 ? "true" : fields.map((f) => `${camelCase(f.name)} == other.${camelCase(f.name)}`).join(" &&\n ");
1711
+ const hashExpression = getHashExpression(
1712
+ fields.map((f) => camelCase(f.name))
1713
+ );
1516
1714
  const toStringFields = fields.map((f) => `${camelCase(f.name)}: $${camelCase(f.name)}`).join(", ");
1517
1715
  variantClasses.push(`final class ${variantClassName} extends ${typeName} {
1518
- const ${variantClassName}({
1519
- ${ctorParams}
1520
- });
1716
+ ${ctorSignature}
1521
1717
 
1522
1718
  ${fieldDecls}
1523
1719
 
@@ -1528,7 +1724,7 @@ ${fieldDecls}
1528
1724
  ${eqChecks};
1529
1725
 
1530
1726
  @override
1531
- int get hashCode => Object.hash(${hashFields});
1727
+ int get hashCode => ${hashExpression};
1532
1728
 
1533
1729
  @override
1534
1730
  String toString() => '${typeName}.${variantClassName}(${toStringFields})';
@@ -1547,15 +1743,19 @@ ${fieldDecls}
1547
1743
  return `${camelCase(f.name)}: ${accessor} as ${typeStr}`;
1548
1744
  }
1549
1745
  ).join(", ");
1550
- const toMapFields = fields.map(
1746
+ fields.map(
1551
1747
  (f) => `'${f.name}': value.${camelCase(f.name)}`
1552
1748
  ).join(", ");
1553
1749
  encoderVariants.push(
1554
- `(${i}, transformEncoder(getStructEncoder([${encFields}]), (${variantClassName} value) => <String, Object?>{${toMapFields}}))`
1750
+ `(${i}, getStructEncoder([${encFields}]))`
1555
1751
  );
1556
1752
  decoderVariants.push(
1557
- `(${i}, transformDecoder(getStructDecoder([${decFields}]), (Map<String, Object?> map, Uint8List bytes, int offset) => ${variantClassName}(${fromMapFields})))`
1753
+ `(${i}, transformDecoder<Map<String, Object?>, Map<String, Object?>>(getStructDecoder([${decFields}]), (Map<String, Object?> map, Uint8List bytes, int offset) => map))`
1558
1754
  );
1755
+ const structPattern = fields.length === 0 ? "" : fields.map((f) => `${camelCase(f.name)}: final ${camelCase(f.name)}`).join(", ");
1756
+ const structMapEntries = fields.length === 0 ? "" : `, ${fields.map((f) => `'${f.name}': ${camelCase(f.name)}`).join(", ")}`;
1757
+ encodeCases.push(`${variantClassName}(${structPattern}) => <String, Object?>{'__kind': ${i}${structMapEntries}},`);
1758
+ decodeCases.push(`case ${i}: return ${variantClassName}(${fromMapFields.replace(/\n/g, " ").replace(/,$/, "")});`);
1559
1759
  } else if (variant.kind === "enumTupleVariantTypeNode") {
1560
1760
  const resolvedTuple = nodes.resolveNestedTypeNode(variant.tuple);
1561
1761
  const items = resolvedTuple.items;
@@ -1579,11 +1779,13 @@ ${fieldDecls}
1579
1779
  String toString() => '${typeName}.${variantClassName}($value)';
1580
1780
  }`);
1581
1781
  encoderVariants.push(
1582
- `(${i}, transformEncoder(${manifest.encoder.content}, (${variantClassName} value) => value.value))`
1782
+ `(${i}, transformEncoder<${manifest.type.content}, Map<String, Object?>>(${manifest.encoder.content}, (Map<String, Object?> map) => map['value']! as ${manifest.type.content}))`
1583
1783
  );
1584
1784
  decoderVariants.push(
1585
- `(${i}, transformDecoder(${manifest.decoder.content}, (${manifest.type.content} value, Uint8List bytes, int offset) => ${variantClassName}(value)))`
1785
+ `(${i}, transformDecoder<${manifest.type.content}, Map<String, Object?>>(${manifest.decoder.content}, (${manifest.type.content} value, Uint8List bytes, int offset) => <String, Object?>{'value': value}))`
1586
1786
  );
1787
+ encodeCases.push(`${variantClassName}(value: final value) => <String, Object?>{'__kind': ${i}, 'value': value},`);
1788
+ decodeCases.push(`case ${i}: return ${variantClassName}(map['value']! as ${manifest.type.content});`);
1587
1789
  }
1588
1790
  }
1589
1791
  }
@@ -1597,7 +1799,6 @@ ${fieldDecls}
1597
1799
  // ignore_for_file: type=lint
1598
1800
 
1599
1801
  ${use("Uint8List", "dartTypedData")}
1600
- ${use("immutable", "meta")}
1601
1802
  ${use("Encoder", "solanaCodecsCore")}
1602
1803
  ${use("Decoder", "solanaCodecsCore")}
1603
1804
  ${use("Codec", "solanaCodecsCore")}
@@ -1618,15 +1819,28 @@ sealed class ${fragmentFromString(typeName)} {
1618
1819
  ${fragmentFromString(variantClasses.join("\n\n"))}
1619
1820
 
1620
1821
  Encoder<${fragmentFromString(typeName)}> ${fragmentFromString(encoderName)}() {
1621
- return getDiscriminatedUnionEncoder([
1622
- ${fragmentFromString(encoderVariants.join(",\n "))},
1623
- ], size: get${fragmentFromString(sizeCodecName)}Encoder());
1822
+ return transformEncoder<Map<String, Object?>, ${fragmentFromString(typeName)}>(
1823
+ getDiscriminatedUnionEncoder([
1824
+ ${fragmentFromString(encoderVariants.join(",\n "))},
1825
+ ], size: get${fragmentFromString(sizeCodecName)}Encoder()),
1826
+ (${fragmentFromString(typeName)} value) => switch (value) {
1827
+ ${fragmentFromString(encodeCases.join("\n "))}
1828
+ },
1829
+ );
1624
1830
  }
1625
1831
 
1626
1832
  Decoder<${fragmentFromString(typeName)}> ${fragmentFromString(decoderName)}() {
1627
- return getDiscriminatedUnionDecoder([
1628
- ${fragmentFromString(decoderVariants.join(",\n "))},
1629
- ], size: get${fragmentFromString(sizeCodecName)}Decoder());
1833
+ return transformDecoder<Map<String, Object?>, ${fragmentFromString(typeName)}>(
1834
+ getDiscriminatedUnionDecoder([
1835
+ ${fragmentFromString(decoderVariants.join(",\n "))},
1836
+ ], size: get${fragmentFromString(sizeCodecName)}Decoder()),
1837
+ (Map<String, Object?> map, Uint8List bytes, int offset) {
1838
+ switch (map['__kind']) {
1839
+ ${fragmentFromString(decodeCases.join("\n "))}
1840
+ }
1841
+ throw StateError('Unsupported ${typeName} discriminator: \${map['__kind']}');
1842
+ },
1843
+ );
1630
1844
  }
1631
1845
 
1632
1846
  Codec<${fragmentFromString(typeName)}, ${fragmentFromString(typeName)}> ${fragmentFromString(codecName)}() {
@@ -1653,10 +1867,13 @@ function getStructPageFragment(node, scope) {
1653
1867
  return ` final ${manifest.type.content} ${camelCase(f.name)};`;
1654
1868
  }).join("\n");
1655
1869
  const ctorParams = fields.map((f) => ` required this.${camelCase(f.name)},`).join("\n");
1870
+ const ctorSignature = fields.length === 0 ? ` const ${typeName}();` : ` const ${typeName}({
1871
+ ${ctorParams}
1872
+ });`;
1656
1873
  const eqChecks = fields.length === 0 ? "true" : fields.map(
1657
1874
  (f) => `${camelCase(f.name)} == other.${camelCase(f.name)}`
1658
1875
  ).join(" &&\n ");
1659
- const hashFields = fields.map((f) => camelCase(f.name)).join(", ");
1876
+ fields.length === 0 ? "runtimeType" : fields.map((f) => camelCase(f.name)).join(", ");
1660
1877
  const toStringFields = fields.map(
1661
1878
  (f) => `${camelCase(f.name)}: $${camelCase(f.name)}`
1662
1879
  ).join(", ");
@@ -1694,9 +1911,7 @@ ${use("getStructDecoder", "solanaCodecsDataStructures")}
1694
1911
 
1695
1912
  @immutable
1696
1913
  class ${fragmentFromString(typeName)} {
1697
- const ${fragmentFromString(typeName)}({
1698
- ${fragmentFromString(ctorParams)}
1699
- });
1914
+ ${fragmentFromString(ctorSignature)}
1700
1915
 
1701
1916
  ${fragmentFromString(fieldDecls)}
1702
1917
 
@@ -1708,7 +1923,7 @@ ${fragmentFromString(fieldDecls)}
1708
1923
  ${fragmentFromString(eqChecks)};
1709
1924
 
1710
1925
  @override
1711
- int get hashCode => Object.hash(${fragmentFromString(hashFields)});
1926
+ int get hashCode => ${fragmentFromString(getHashExpression(fields.map((f) => camelCase(f.name))))};
1712
1927
 
1713
1928
  @override
1714
1929
  String toString() => '${fragmentFromString(typeName)}(${fragmentFromString(toStringFields)})';
@@ -1779,6 +1994,11 @@ Codec<${fragmentFromString(typeName)}, ${fragmentFromString(typeName)}> ${fragme
1779
1994
  return combineCodec(${fragmentFromString(encoderName)}(), ${fragmentFromString(decoderName)}());
1780
1995
  }`;
1781
1996
  }
1997
+ function getHashExpression(fields) {
1998
+ if (fields.length === 0) return "runtimeType.hashCode";
1999
+ if (fields.length === 1) return `${fields[0]}.hashCode`;
2000
+ return `Object.hash(${fields.join(", ")})`;
2001
+ }
1782
2002
  function getNumberCodecName2(format) {
1783
2003
  switch (format) {
1784
2004
  case "u8":
@@ -1847,7 +2067,9 @@ function getRenderMapVisitor(options = {}) {
1847
2067
  visitorsCore.visit(node.program, self),
1848
2068
  ...node.additionalPrograms.map((p) => visitorsCore.visit(p, self))
1849
2069
  ];
1850
- return renderersCore.mergeRenderMaps(programMaps);
2070
+ const mergedProgramMap = renderersCore.mergeRenderMaps(programMaps);
2071
+ const categoryMaps = getSharedCategoryIndexMaps(mergedProgramMap);
2072
+ return renderersCore.mergeRenderMaps([mergedProgramMap, ...categoryMaps]);
1851
2073
  },
1852
2074
  visitProgram(node, { self }) {
1853
2075
  const programName = node.name;
@@ -1894,66 +2116,12 @@ function getRenderMapVisitor(options = {}) {
1894
2116
  maps.push(errorMap);
1895
2117
  }
1896
2118
  const categories = [];
1897
- if (accountFiles.length > 0) {
1898
- categories.push("accounts");
1899
- let indexMap = renderersCore.createRenderMap();
1900
- indexMap = renderersCore.addToRenderMap(
1901
- indexMap,
1902
- "accounts/accounts.dart",
1903
- getIndexPageFragment(accountFiles)
1904
- );
1905
- maps.push(indexMap);
1906
- }
1907
- if (instructionFiles.length > 0) {
1908
- categories.push("instructions");
1909
- let indexMap = renderersCore.createRenderMap();
1910
- indexMap = renderersCore.addToRenderMap(
1911
- indexMap,
1912
- "instructions/instructions.dart",
1913
- getIndexPageFragment(instructionFiles)
1914
- );
1915
- maps.push(indexMap);
1916
- }
1917
- if (typeFiles.length > 0) {
1918
- categories.push("types");
1919
- let indexMap = renderersCore.createRenderMap();
1920
- indexMap = renderersCore.addToRenderMap(
1921
- indexMap,
1922
- "types/types.dart",
1923
- getIndexPageFragment(typeFiles)
1924
- );
1925
- maps.push(indexMap);
1926
- }
1927
- if (pdaFiles.length > 0) {
1928
- categories.push("pdas");
1929
- let indexMap = renderersCore.createRenderMap();
1930
- indexMap = renderersCore.addToRenderMap(
1931
- indexMap,
1932
- "pdas/pdas.dart",
1933
- getIndexPageFragment(pdaFiles)
1934
- );
1935
- maps.push(indexMap);
1936
- }
1937
- if (errorFiles.length > 0) {
1938
- categories.push("errors");
1939
- let indexMap = renderersCore.createRenderMap();
1940
- indexMap = renderersCore.addToRenderMap(
1941
- indexMap,
1942
- "errors/errors.dart",
1943
- getIndexPageFragment(errorFiles)
1944
- );
1945
- maps.push(indexMap);
1946
- }
1947
- if (programFiles.length > 0) {
1948
- categories.push("programs");
1949
- let indexMap = renderersCore.createRenderMap();
1950
- indexMap = renderersCore.addToRenderMap(
1951
- indexMap,
1952
- "programs/programs.dart",
1953
- getIndexPageFragment(programFiles)
1954
- );
1955
- maps.push(indexMap);
1956
- }
2119
+ if (accountFiles.length > 0) categories.push("accounts");
2120
+ if (instructionFiles.length > 0) categories.push("instructions");
2121
+ if (typeFiles.length > 0) categories.push("types");
2122
+ if (pdaFiles.length > 0) categories.push("pdas");
2123
+ if (errorFiles.length > 0) categories.push("errors");
2124
+ if (programFiles.length > 0) categories.push("programs");
1957
2125
  if (categories.length > 0) {
1958
2126
  let rootMap = renderersCore.createRenderMap();
1959
2127
  rootMap = renderersCore.addToRenderMap(
@@ -1998,6 +2166,41 @@ function getRenderMapVisitor(options = {}) {
1998
2166
  (v) => visitorsCore.recordLinkablesOnFirstVisitVisitor(v, linkables)
1999
2167
  );
2000
2168
  }
2169
+ function getSharedCategoryIndexMaps(renderMap) {
2170
+ const categories = /* @__PURE__ */ new Map();
2171
+ const categoryIndexNames = {
2172
+ accounts: "accounts.dart",
2173
+ instructions: "instructions.dart",
2174
+ types: "types.dart",
2175
+ pdas: "pdas.dart",
2176
+ errors: "errors.dart",
2177
+ programs: "programs.dart"
2178
+ };
2179
+ for (const renderPath of renderMap.keys()) {
2180
+ const match = renderPath.match(
2181
+ /^(accounts|instructions|types|pdas|errors|programs)\/([^/]+\.dart)$/
2182
+ );
2183
+ if (!match) continue;
2184
+ const [, category, fileName] = match;
2185
+ if (fileName === categoryIndexNames[category]) continue;
2186
+ if (!categories.has(category)) {
2187
+ categories.set(category, /* @__PURE__ */ new Set());
2188
+ }
2189
+ categories.get(category).add(fileName);
2190
+ }
2191
+ const maps = [];
2192
+ for (const [category, files] of categories.entries()) {
2193
+ const sortedFiles = [...files].sort();
2194
+ let map = renderersCore.createRenderMap();
2195
+ map = renderersCore.addToRenderMap(
2196
+ map,
2197
+ `${category}/${categoryIndexNames[category]}`,
2198
+ getIndexPageFragment(sortedFiles)
2199
+ );
2200
+ maps.push(map);
2201
+ }
2202
+ return maps;
2203
+ }
2001
2204
 
2002
2205
  // src/visitors/renderVisitor.ts
2003
2206
  function renderVisitor(outputDir, options = {}) {