codama-renderers-dart 0.4.0 → 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.
@@ -207,6 +207,60 @@ function emptyTypeManifest() {
207
207
  };
208
208
  }
209
209
 
210
+ // src/utils/wellKnownAddresses.ts
211
+ var WELL_KNOWN_ADDRESSES = /* @__PURE__ */ new Map([
212
+ // Agave / Solana native programs
213
+ ["11111111111111111111111111111111", "systemProgramAddress"],
214
+ ["AddressLookupTab1e1111111111111111111111111", "addressLookupTableProgramAddress"],
215
+ ["BPFLoader2111111111111111111111111111111111", "bpfLoaderProgramAddress"],
216
+ ["BPFLoader1111111111111111111111111111111111", "bpfLoaderDeprecatedProgramAddress"],
217
+ ["BPFLoaderUpgradeab1e11111111111111111111111", "bpfLoaderUpgradeableProgramAddress"],
218
+ ["ComputeBudget111111111111111111111111111111", "computeBudgetProgramAddress"],
219
+ ["Config1111111111111111111111111111111111111", "configProgramAddress"],
220
+ ["Ed25519SigVerify111111111111111111111111111", "ed25519ProgramAddress"],
221
+ ["Feature111111111111111111111111111111111111", "featureProgramAddress"],
222
+ ["1nc1nerator11111111111111111111111111111111", "incineratorAddress"],
223
+ ["LoaderV411111111111111111111111111111111111", "loaderV4ProgramAddress"],
224
+ ["NativeLoader1111111111111111111111111111", "nativeLoaderProgramAddress"],
225
+ ["KeccakSecp256k11111111111111111111111111111", "secp256k1ProgramAddress"],
226
+ ["Secp256r1SigVerify1111111111111111111111111", "secp256r1ProgramAddress"],
227
+ ["Stake11111111111111111111111111111111111111", "stakeProgramAddress"],
228
+ ["StakeConfig1111111111111111111111111111", "stakeConfigAddress"],
229
+ ["Vote111111111111111111111111111111111111111", "voteProgramAddress"],
230
+ ["ZkTokenProof1111111111111111111111111111111", "zkTokenProofProgramAddress"],
231
+ ["ZkE1Gama1Proof11111111111111111111111111111", "zkElgamalProofProgramAddress"],
232
+ // Sysvars
233
+ ["SysvarC1ock11111111111111111111111111111111", "sysvarClockAddress"],
234
+ ["Rent11111111111111111111111111111111111111", "sysvarRentAddress"],
235
+ ["11111111111111111111111111111111111111111111", "sysvarRecentBlockhashesAddress"],
236
+ // note: 44 chars
237
+ ["SysvarFees1111111111111111111111111111111111", "sysvarFeesAddress"],
238
+ ["Sysvar1nstruct1ons11111111111111111111111111", "sysvarInstructionsAddress"],
239
+ ["SysvarEpochSchedu1e111111111111111111111111", "sysvarEpochScheduleAddress"],
240
+ ["SysvarRewards111111111111111111111111111111", "sysvarRewardsAddress"],
241
+ ["SysvarStakeHistory1111111111111111111111111", "sysvarStakeHistoryAddress"],
242
+ ["SysvarS1otHashes111111111111111111111111111", "sysvarSlotHashesAddress"],
243
+ ["SysvarS1otHistory11111111111111111111111111", "sysvarSlotHistoryAddress"],
244
+ ["Sysvar1111111111111111111111111111111111111", "sysvarOwnerAddress"],
245
+ // SPL programs
246
+ ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "tokenProgramAddress"],
247
+ ["TokenzQdBNb4qyze1S1U9AHB8MGXmNK1REkTPT5Z3Y", "token2022ProgramAddress"],
248
+ ["ATokenGPvbdGVxr1b2hvZbsiqW5xWH25ef7s3c8BnQKu", "associatedTokenProgramAddress"],
249
+ ["Memo1UhkJRfRhVq1sR7Y7Nto1s3J2mXTT3L6Wk4K2m6", "memoProgramAddress"],
250
+ ["MemoSq4gqABXKKYWVqKkV3m3kb1cTR7F2UMy9r6kFNK", "memoLegacyProgramAddress"],
251
+ // Metaplex programs
252
+ ["metaqbxxU9qX8rZXkmUJ7haCrXXz2W1PSUq1Rn3E1od", "tokenMetadataProgramAddress"],
253
+ ["BGUMAp9V1mQ2KV8t34L5u3gZsXYZaYDBdKkc1xYk1qA1", "mplBubblegumProgramAddress"],
254
+ ["auth9SigNpDKz4s8cHr3MFBPS1phbRKLSCN3aBe9sS", "authRulesProgramAddress"],
255
+ ["CoREENxT6tW1Ho6BW6W5Z4N41ci5Xz2mmrR2kErmM5TR", "metaplexCoreProgramAddress"],
256
+ ["cmtMqR4bPM9sEV3bPnJQq5s2EnYw4N5vQ8hWhEd1xzS", "splAccountCompressionProgramAddress"],
257
+ ["noopb9skMVngR5Qi4sczwFr1jU4V4F7nm8xzFQK9C", "noopProgramAddress"],
258
+ // Well-known mints
259
+ ["So11111111111111111111111111111111111111112", "wrappedSolAddress"],
260
+ ["EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "usdcAddress"],
261
+ ["Es9vMFrzaCERm6YKXx6bqKr1Rmd9b5v3J8rM5e4rfF4", "usdtAddress"]
262
+ ]);
263
+
210
264
  // src/visitors/getTypeManifestVisitor.ts
211
265
  function getTypeManifestVisitor(input) {
212
266
  const { nameApi, linkables, nonScalarEnums = [] } = input;
@@ -264,7 +318,8 @@ function getTypeManifestVisitor(input) {
264
318
  const dartType = getNumberDartType(format);
265
319
  const codecName = getNumberCodecName(format);
266
320
  return {
267
- type: dartType === "BigInt" ? use("BigInt", "dartTypedData") : fragmentFromString(dartType),
321
+ // `BigInt` and other number types live in `dart:core` and need no import.
322
+ type: fragmentFromString(dartType),
268
323
  encoder: fragment`${use(
269
324
  `get${codecName}Encoder`,
270
325
  "solanaCodecsNumbers"
@@ -494,51 +549,55 @@ function getTypeManifestVisitor(input) {
494
549
  },
495
550
  visitOptionType(node, { self }) {
496
551
  const itemManifest = visit(node.item, self);
552
+ const itemTypeStr = itemManifest.type.content;
497
553
  const resolvedPrefix = resolveNestedTypeNode(node.prefix);
498
554
  const prefixExpr = resolvedPrefix.format !== "u8" ? `, prefix: ${getNumberCodecExpression(resolvedPrefix.format, "Encoder")}` : "";
499
555
  const prefixDecoderExpr = resolvedPrefix.format !== "u8" ? `, prefix: ${getNumberCodecExpression(resolvedPrefix.format, "Decoder")}` : "";
556
+ const noneValueExpr = node.fixed ? ", noneValue: const ZeroesNoneValue()" : "";
500
557
  return {
501
558
  type: fragment`${itemManifest.type}?`,
502
559
  encoder: fragment`${use(
503
560
  "getNullableEncoder",
504
561
  "solanaCodecsDataStructures"
505
- )}(${itemManifest.encoder}${fragmentFromString(prefixExpr)})`,
562
+ )}<${fragmentFromString(itemTypeStr)}>(${itemManifest.encoder}${fragmentFromString(prefixExpr)}${fragmentFromString(noneValueExpr)})`,
506
563
  decoder: fragment`${use(
507
564
  "getNullableDecoder",
508
565
  "solanaCodecsDataStructures"
509
- )}(${itemManifest.decoder}${fragmentFromString(prefixDecoderExpr)})`,
566
+ )}<${fragmentFromString(itemTypeStr)}>(${itemManifest.decoder}${fragmentFromString(prefixDecoderExpr)}${fragmentFromString(noneValueExpr)})`,
510
567
  value: emptyTypeManifest().value,
511
568
  isEnum: false
512
569
  };
513
570
  },
514
571
  visitRemainderOptionType(node, { self }) {
515
572
  const itemManifest = visit(node.item, self);
573
+ const itemTypeStr = itemManifest.type.content;
516
574
  return {
517
575
  type: fragment`${itemManifest.type}?`,
518
576
  encoder: fragment`${use(
519
577
  "getNullableEncoder",
520
578
  "solanaCodecsDataStructures"
521
- )}(${itemManifest.encoder}, hasPrefix: false)`,
579
+ )}<${fragmentFromString(itemTypeStr)}>(${itemManifest.encoder}, hasPrefix: false)`,
522
580
  decoder: fragment`${use(
523
581
  "getNullableDecoder",
524
582
  "solanaCodecsDataStructures"
525
- )}(${itemManifest.decoder}, hasPrefix: false)`,
583
+ )}<${fragmentFromString(itemTypeStr)}>(${itemManifest.decoder}, hasPrefix: false)`,
526
584
  value: emptyTypeManifest().value,
527
585
  isEnum: false
528
586
  };
529
587
  },
530
588
  visitZeroableOptionType(node, { self }) {
531
589
  const itemManifest = visit(node.item, self);
590
+ const itemTypeStr = itemManifest.type.content;
532
591
  return {
533
592
  type: fragment`${itemManifest.type}?`,
534
593
  encoder: fragment`${use(
535
594
  "getNullableEncoder",
536
595
  "solanaCodecsDataStructures"
537
- )}(${itemManifest.encoder}, hasPrefix: false, noneValue: const ZeroesNoneValue())`,
596
+ )}<${fragmentFromString(itemTypeStr)}>(${itemManifest.encoder}, hasPrefix: false, noneValue: const ZeroesNoneValue())`,
538
597
  decoder: fragment`${use(
539
598
  "getNullableDecoder",
540
599
  "solanaCodecsDataStructures"
541
- )}(${itemManifest.decoder}, hasPrefix: false, noneValue: const ZeroesNoneValue())`,
600
+ )}<${fragmentFromString(itemTypeStr)}>(${itemManifest.decoder}, hasPrefix: false, noneValue: const ZeroesNoneValue())`,
542
601
  value: emptyTypeManifest().value,
543
602
  isEnum: false
544
603
  };
@@ -561,7 +620,16 @@ function getTypeManifestVisitor(input) {
561
620
  },
562
621
  visitSizePrefixType(node, { self }) {
563
622
  const innerManifest = visit(node.type, self);
564
- const prefixManifest = visit(node.prefix, self);
623
+ const bigIntFormats = /* @__PURE__ */ new Set(["u64", "u128", "i64", "i128"]);
624
+ let prefixManifest;
625
+ if (node.prefix.kind === "numberTypeNode" && bigIntFormats.has(node.prefix.format)) {
626
+ prefixManifest = {
627
+ encoder: fragment`${use("getU32Encoder", "solanaCodecsNumbers")}()`,
628
+ decoder: fragment`${use("getU32Decoder", "solanaCodecsNumbers")}()`
629
+ };
630
+ } else {
631
+ prefixManifest = visit(node.prefix, self);
632
+ }
565
633
  return {
566
634
  type: innerManifest.type,
567
635
  encoder: fragment`${use(
@@ -578,7 +646,7 @@ function getTypeManifestVisitor(input) {
578
646
  },
579
647
  visitHiddenPrefixType(node, { self }) {
580
648
  const innerManifest = visit(node.type, self);
581
- const prefixes = node.prefix.map((p) => visit(p, self));
649
+ const prefixes = node.prefix.map((p) => getHiddenAffixManifest(p, self));
582
650
  const prefixEncoders = prefixes.map((p) => p.encoder.content).join(", ");
583
651
  const prefixDecoders = prefixes.map((p) => p.decoder.content).join(", ");
584
652
  const encoderFrag = fragment`${use(
@@ -605,7 +673,7 @@ function getTypeManifestVisitor(input) {
605
673
  },
606
674
  visitHiddenSuffixType(node, { self }) {
607
675
  const innerManifest = visit(node.type, self);
608
- const suffixes = node.suffix.map((s) => visit(s, self));
676
+ const suffixes = node.suffix.map((s) => getHiddenAffixManifest(s, self));
609
677
  const suffixEncoders = suffixes.map((s) => s.encoder.content).join(", ");
610
678
  const suffixDecoders = suffixes.map((s) => s.decoder.content).join(", ");
611
679
  const encoderFrag = fragment`${use(
@@ -801,6 +869,42 @@ function getSetSizeExpression(node) {
801
869
  return "";
802
870
  }
803
871
  }
872
+ function getHiddenAffixManifest(node, self) {
873
+ if (node?.kind !== "constantValueNode") {
874
+ const manifest = visit(node, self);
875
+ return { encoder: manifest.encoder, decoder: manifest.decoder };
876
+ }
877
+ const constantBytes = getConstantBytesExpression(node);
878
+ return {
879
+ encoder: fragment`${use("getConstantEncoder", "solanaCodecsDataStructures")}(${constantBytes})`,
880
+ decoder: fragment`${use("getConstantDecoder", "solanaCodecsDataStructures")}(${constantBytes})`
881
+ };
882
+ }
883
+ function getConstantBytesExpression(node) {
884
+ const valueNode = node.value;
885
+ const typeNode = node.type;
886
+ if (valueNode?.kind !== "numberValueNode") {
887
+ throw new Error(
888
+ `Unsupported hidden affix constant value kind: ${valueNode?.kind}`
889
+ );
890
+ }
891
+ const value = Number(valueNode.number);
892
+ if (typeNode?.kind === "numberTypeNode") {
893
+ return fragment`${use(`get${getNumberCodecName(typeNode.format)}Encoder`, "solanaCodecsNumbers")}().encode(${value})`;
894
+ }
895
+ if (typeNode?.kind === "preOffsetTypeNode") {
896
+ const inner = typeNode.type;
897
+ if (inner?.kind !== "numberTypeNode") {
898
+ throw new Error(
899
+ `Unsupported hidden affix pre-offset inner type kind: ${inner?.kind}`
900
+ );
901
+ }
902
+ return fragment`${use("padLeftEncoder", "solanaCodecsCore")}(${use(`get${getNumberCodecName(inner.format)}Encoder`, "solanaCodecsNumbers")}(), ${typeNode.offset}).encode(${value})`;
903
+ }
904
+ throw new Error(
905
+ `Unsupported hidden affix constant type kind: ${typeNode?.kind}`
906
+ );
907
+ }
804
908
 
805
909
  // src/fragments/discriminatorConstants.ts
806
910
  function getDiscriminatorConstantsFragment(node, scope) {
@@ -866,10 +970,14 @@ function getAccountPageFragment(node, scope) {
866
970
  return ` final ${manifest.type.content} ${camelCase(f.name)};`;
867
971
  }).join("\n");
868
972
  const ctorParams = fields.map((f) => ` required this.${camelCase(f.name)},`).join("\n");
973
+ const ctorSignature = fields.length === 0 ? `const ${typeName}();` : `const ${typeName}({
974
+ ${ctorParams}
975
+ });`;
869
976
  const eqChecks = fields.length === 0 ? "true" : fields.map(
870
977
  (f) => `${camelCase(f.name)} == other.${camelCase(f.name)}`
871
978
  ).join(" &&\n ");
872
- const hashFields = fields.map((f) => camelCase(f.name)).join(", ");
979
+ const hashFieldNames = fields.map((f) => camelCase(f.name));
980
+ const hashCodeExpr = hashFieldNames.length === 0 ? "0" : hashFieldNames.length === 1 ? `${hashFieldNames[0]}.hashCode` : `Object.hash(${hashFieldNames.join(", ")})`;
873
981
  const toStringFields = fields.map(
874
982
  (f) => `${camelCase(f.name)}: $${camelCase(f.name)}`
875
983
  ).join(", ");
@@ -916,9 +1024,7 @@ ${use("decodeAccount", "solanaAccounts")}
916
1024
 
917
1025
  @immutable
918
1026
  class ${fragmentFromString(typeName)} {
919
- const ${fragmentFromString(typeName)}({
920
- ${fragmentFromString(ctorParams)}
921
- });
1027
+ ${fragmentFromString(ctorSignature)}
922
1028
 
923
1029
  ${fragmentFromString(fieldDecls)}
924
1030
 
@@ -930,7 +1036,7 @@ ${fragmentFromString(fieldDecls)}
930
1036
  ${fragmentFromString(eqChecks)};
931
1037
 
932
1038
  @override
933
- int get hashCode => Object.hash(${fragmentFromString(hashFields)});
1039
+ int get hashCode => ${fragmentFromString(hashCodeExpr)};
934
1040
 
935
1041
  @override
936
1042
  String toString() => '${fragmentFromString(typeName)}(${fragmentFromString(toStringFields)})';
@@ -1081,13 +1187,20 @@ function getInstructionPageFragment(node, scope) {
1081
1187
  const fieldName = camelCase(arg.name);
1082
1188
  return ` final ${manifest.type.content} ${fieldName};`;
1083
1189
  }).join("\n");
1084
- const dataCtorParams = allArgManifests.map(({ arg }) => {
1190
+ const dataCtorParams = allArgManifests.map(({ arg, manifest }) => {
1085
1191
  const fieldName = camelCase(arg.name);
1086
1192
  if (isDiscriminatorArg(arg, node)) {
1087
- return ` this.${fieldName} = ${getDiscriminatorDefault(arg)},`;
1193
+ return isConstDefaultValue(arg.defaultValue) ? ` this.${fieldName} = ${getDiscriminatorDefault(arg)},` : ` ${manifest.type.content}? ${fieldName},`;
1088
1194
  }
1089
1195
  return ` required this.${fieldName},`;
1090
1196
  }).join("\n");
1197
+ const dataCtorInitializers = allArgManifests.map(({ arg }) => {
1198
+ const fieldName = camelCase(arg.name);
1199
+ if (isDiscriminatorArg(arg, node) && !isConstDefaultValue(arg.defaultValue)) {
1200
+ return ` ${fieldName} = ${fieldName} ?? ${getDiscriminatorDefault(arg)}`;
1201
+ }
1202
+ return null;
1203
+ }).filter((value) => value !== null).join(",\n");
1091
1204
  const encFields = allArgManifests.map(({ arg, manifest }) => {
1092
1205
  return ` ('${arg.name}', ${manifest.encoder.content}),`;
1093
1206
  }).join("\n");
@@ -1103,9 +1216,14 @@ function getInstructionPageFragment(node, scope) {
1103
1216
  const accessor = isNullable ? `map['${arg.name}']` : `map['${arg.name}']!`;
1104
1217
  return ` ${camelCase(arg.name)}: ${accessor} as ${typeStr},`;
1105
1218
  }).join("\n");
1219
+ const dataCtorKeyword = dataCtorInitializers ? "" : "const ";
1106
1220
  const dataEncoderName = `get${typeName}InstructionDataEncoder`;
1107
1221
  const dataDecoderName = `get${typeName}InstructionDataDecoder`;
1108
1222
  const dataCodecName = `get${typeName}InstructionDataCodec`;
1223
+ const argNames = new Set(args.map((a) => camelCase(a.name)));
1224
+ const accountNames = new Set(accounts.map((a) => camelCase(a.name)));
1225
+ const hasProgramAddressCollision = argNames.has("programAddress") || accountNames.has("programAddress");
1226
+ const instrProgramParam = hasProgramAddressCollision ? "instructionProgramAddress" : "programAddress";
1109
1227
  const accountParams = accounts.map((acc) => {
1110
1228
  const fieldName = camelCase(acc.name);
1111
1229
  const isRequired = !(acc.isOptional ?? false);
@@ -1115,11 +1233,13 @@ function getInstructionPageFragment(node, scope) {
1115
1233
  const argParams = args.map((arg) => {
1116
1234
  const manifest = argManifestMap.get(arg);
1117
1235
  const fieldName = camelCase(arg.name);
1236
+ const typeStr = manifest.type.content;
1118
1237
  const hasDefault = arg.defaultValue != null;
1119
1238
  if (hasDefault) {
1120
- return ` ${manifest.type.content}? ${fieldName},`;
1239
+ const nullableType = typeStr.endsWith("?") ? typeStr : `${typeStr}?`;
1240
+ return ` ${nullableType} ${fieldName},`;
1121
1241
  }
1122
- return ` required ${manifest.type.content} ${fieldName},`;
1242
+ return ` required ${typeStr} ${fieldName},`;
1123
1243
  }).join("\n");
1124
1244
  const accountMetas = accounts.map((acc) => {
1125
1245
  const fieldName = camelCase(acc.name);
@@ -1161,9 +1281,10 @@ ${use("AccountRole", "solanaInstructions")}`
1161
1281
  parts.push(fragment`
1162
1282
  @immutable
1163
1283
  class ${fragmentFromString(dataClassName)} {
1164
- const ${fragmentFromString(dataClassName)}({
1284
+ ${fragmentFromString(dataCtorKeyword)}${fragmentFromString(dataClassName)}({
1165
1285
  ${fragmentFromString(dataCtorParams)}
1166
- });
1286
+ })${fragmentFromString(dataCtorInitializers ? ` :
1287
+ ${dataCtorInitializers}` : "")};
1167
1288
 
1168
1289
  ${fragmentFromString(dataFieldDecls)}
1169
1290
  }`);
@@ -1200,20 +1321,20 @@ Codec<${fragmentFromString(dataClassName)}, ${fragmentFromString(dataClassName)}
1200
1321
  parts.push(fragment`
1201
1322
  /// Creates a [${fragmentFromString(typeName)}] instruction.
1202
1323
  Instruction ${fragmentFromString(instrFnName)}({
1203
- required Address programAddress,
1324
+ required Address ${fragmentFromString(instrProgramParam)},
1204
1325
  ${fragmentFromString(accountParams)}
1205
1326
  ${fragmentFromString(argParams)}
1206
1327
  }) {
1207
- final data = ${fragmentFromString(dataClassName)}(
1328
+ final instructionData = ${fragmentFromString(dataClassName)}(
1208
1329
  ${fragmentFromString(dataConstruction)}
1209
1330
  );
1210
1331
 
1211
1332
  return Instruction(
1212
- programAddress: programAddress,
1333
+ programAddress: ${fragmentFromString(instrProgramParam)},
1213
1334
  accounts: [
1214
1335
  ${fragmentFromString(accountMetas)}
1215
1336
  ],
1216
- data: ${fragmentFromString(dataEncoderName)}().encode(data),
1337
+ data: ${fragmentFromString(dataEncoderName)}().encode(instructionData),
1217
1338
  );
1218
1339
  }`);
1219
1340
  parts.push(fragment`
@@ -1248,9 +1369,26 @@ function getDiscriminatorDefault(arg, _node) {
1248
1369
  if (arg.defaultValue.kind === "numberValueNode") {
1249
1370
  return String(arg.defaultValue.number);
1250
1371
  }
1372
+ if (arg.defaultValue.kind === "bytesValueNode") {
1373
+ const data = arg.defaultValue.data;
1374
+ const bytes = data.match(/.{1,2}/g)?.map((b) => parseInt(b, 16)) ?? [];
1375
+ return `Uint8List.fromList([${bytes.join(", ")}])`;
1376
+ }
1251
1377
  }
1252
1378
  return "0";
1253
1379
  }
1380
+ function isConstDefaultValue(defaultValue) {
1381
+ if (!defaultValue) return false;
1382
+ switch (defaultValue.kind) {
1383
+ case "numberValueNode":
1384
+ case "booleanValueNode":
1385
+ case "stringValueNode":
1386
+ case "noneValueNode":
1387
+ return true;
1388
+ default:
1389
+ return false;
1390
+ }
1391
+ }
1254
1392
  function getDefaultValue(arg) {
1255
1393
  if (!arg.defaultValue) return "null";
1256
1394
  const dv = arg.defaultValue;
@@ -1261,10 +1399,20 @@ function getDefaultValue(arg) {
1261
1399
  return String(dv.boolean);
1262
1400
  case "stringValueNode":
1263
1401
  return `'${dv.string}'`;
1264
- case "publicKeyValueNode":
1402
+ case "publicKeyValueNode": {
1403
+ const wellKnownName = WELL_KNOWN_ADDRESSES.get(dv.publicKey);
1404
+ if (wellKnownName) {
1405
+ use(wellKnownName, "solanaAddresses");
1406
+ return wellKnownName;
1407
+ }
1265
1408
  return `Address('${dv.publicKey}')`;
1409
+ }
1266
1410
  case "noneValueNode":
1267
1411
  return "null";
1412
+ case "bytesValueNode": {
1413
+ const bytes = dv.data.match(/.{1,2}/g)?.map((b) => parseInt(b, 16)) ?? [];
1414
+ return `Uint8List.fromList([${bytes.join(", ")}])`;
1415
+ }
1268
1416
  default:
1269
1417
  return "null";
1270
1418
  }
@@ -1274,6 +1422,9 @@ function getPdaPageFragment(node, scope) {
1274
1422
  const findFnName = scope.nameApi.pdaFindFunction(name);
1275
1423
  const seedsClassName = `${pascalCase(name)}Seeds`;
1276
1424
  const seeds = node.seeds ?? [];
1425
+ const hasByteSeeds = seeds.some(
1426
+ (seed) => seed.kind === "constantPdaSeedNode" && seed.value.kind === "bytesValueNode"
1427
+ );
1277
1428
  const variableSeeds = seeds.filter(
1278
1429
  (s) => s.kind === "variablePdaSeedNode"
1279
1430
  );
@@ -1281,44 +1432,65 @@ function getPdaPageFragment(node, scope) {
1281
1432
  (s) => s.kind === "constantPdaSeedNode"
1282
1433
  );
1283
1434
  const hasSeedsClass = variableSeeds.length > 0;
1435
+ const seedManifests = [];
1284
1436
  const seedFieldDecls = variableSeeds.map((s) => {
1285
1437
  if (s.kind !== "variablePdaSeedNode") return "";
1286
1438
  const manifest = visit(s.type, scope.typeManifestVisitor);
1439
+ seedManifests.push(manifest);
1287
1440
  return ` final ${manifest.type.content} ${camelCase(s.name)};`;
1288
1441
  }).join("\n");
1289
1442
  const seedCtorParams = variableSeeds.map((s) => {
1290
1443
  if (s.kind !== "variablePdaSeedNode") return "";
1291
1444
  return ` required this.${camelCase(s.name)},`;
1292
1445
  }).join("\n");
1293
- const seedBytesList = [];
1446
+ const seedValues = [];
1294
1447
  for (const seed of seeds) {
1295
1448
  if (seed.kind === "constantPdaSeedNode") {
1296
1449
  if (seed.value.kind === "bytesValueNode") {
1297
- seedBytesList.push(` ...${bytesValueToDart(seed.value.data)},`);
1450
+ seedValues.push(` Uint8List.fromList(${bytesValueToDart(seed.value.data)}),`);
1298
1451
  } else if (seed.value.kind === "stringValueNode") {
1299
- seedBytesList.push(` ...utf8.encode('${seed.value.string}'),`);
1452
+ seedValues.push(` '${seed.value.string}',`);
1300
1453
  } else if (seed.value.kind === "publicKeyValueNode") {
1301
- seedBytesList.push(` ...getAddressEncoder().encode(Address('${seed.value.publicKey}')),`);
1454
+ const wellKnownSeedName = WELL_KNOWN_ADDRESSES.get(seed.value.publicKey);
1455
+ if (wellKnownSeedName) {
1456
+ use(wellKnownSeedName, "solanaAddresses");
1457
+ seedValues.push(
1458
+ ` getAddressEncoder().encode(${wellKnownSeedName}),`
1459
+ );
1460
+ } else {
1461
+ seedValues.push(
1462
+ ` getAddressEncoder().encode(Address('${seed.value.publicKey}')),`
1463
+ );
1464
+ }
1302
1465
  } else if (seed.value.kind === "numberValueNode") {
1303
1466
  const manifest = visit(seed.type, scope.typeManifestVisitor);
1304
- seedBytesList.push(` ...${manifest.encoder.content}.encode(${seed.value.number}),`);
1467
+ seedManifests.push(manifest);
1468
+ seedValues.push(` ${manifest.encoder.content}.encode(${seed.value.number}),`);
1305
1469
  }
1306
1470
  } else if (seed.kind === "variablePdaSeedNode") {
1307
1471
  const manifest = visit(seed.type, scope.typeManifestVisitor);
1308
- seedBytesList.push(
1309
- ` ...${manifest.encoder.content}.encode(seeds.${camelCase(seed.name)}),`
1472
+ seedManifests.push(manifest);
1473
+ seedValues.push(
1474
+ ` ${manifest.encoder.content}.encode(seeds.${camelCase(seed.name)}),`
1310
1475
  );
1311
1476
  }
1312
1477
  }
1313
- const programIdParam = node.programId ? `Address programAddress = const Address('${node.programId}')` : "required Address programAddress";
1478
+ const wellKnownProgramName = node.programId ? WELL_KNOWN_ADDRESSES.get(node.programId) : void 0;
1479
+ if (wellKnownProgramName) {
1480
+ use(wellKnownProgramName, "solanaAddresses");
1481
+ }
1482
+ const programIdParam = node.programId ? wellKnownProgramName ? `Address programAddress = ${wellKnownProgramName}` : `Address programAddress = const Address('${node.programId}')` : "required Address programAddress";
1483
+ const typedDataImport = hasByteSeeds ? use("Uint8List", "dartTypedData") : fragmentFromString("");
1484
+ const metaImport = hasSeedsClass ? use("immutable", "meta") : fragmentFromString("");
1314
1485
  const parts = [
1315
1486
  fragment`// Auto-generated. Do not edit.
1316
1487
  // ignore_for_file: type=lint
1317
1488
 
1318
- ${use("Uint8List", "dartTypedData")}
1319
- ${use("immutable", "meta")}
1489
+ ${typedDataImport}
1490
+ ${metaImport}
1320
1491
  ${use("Address", "solanaAddresses")}
1321
- ${use("getAddressEncoder", "solanaAddresses")}`
1492
+ ${use("getAddressEncoder", "solanaAddresses")}
1493
+ ${use("getProgramDerivedAddress", "solanaAddresses")}`
1322
1494
  ];
1323
1495
  if (hasSeedsClass) {
1324
1496
  parts.push(fragment`
@@ -1338,14 +1510,21 @@ Future<(Address, int)> ${fragmentFromString(findFnName)}({
1338
1510
  ${fragmentFromString(seedsParam)}
1339
1511
  ${fragmentFromString(programIdParam)},
1340
1512
  }) async {
1341
- final seedBytes = <int>[
1342
- ${fragmentFromString(seedBytesList.join("\n"))}
1513
+ final seedValues = <Object>[
1514
+ ${fragmentFromString(seedValues.join("\n"))}
1343
1515
  ];
1344
1516
 
1345
- // TODO: Call getProgramDerivedAddress with seedBytes and programAddress
1346
- throw UnimplementedError('PDA derivation not yet implemented');
1517
+ return getProgramDerivedAddress(
1518
+ programAddress: programAddress,
1519
+ seeds: seedValues,
1520
+ );
1347
1521
  }`);
1348
- return mergeFragments(parts, (cs) => cs.join("\n"));
1522
+ const result = mergeFragments(parts, (cs) => cs.join("\n"));
1523
+ for (const manifest of seedManifests) {
1524
+ result.imports.mergeWith(manifest.encoder.imports);
1525
+ result.imports.mergeWith(manifest.type.imports);
1526
+ }
1527
+ return result;
1349
1528
  }
1350
1529
  function bytesValueToDart(hex) {
1351
1530
  const clean = hex.replace(/^0x/, "");
@@ -1360,15 +1539,25 @@ function bytesValueToDart(hex) {
1360
1539
  function getProgramPageFragment(node, scope) {
1361
1540
  const name = node.name;
1362
1541
  const addressConstName = scope.nameApi.programAddressConstant(name);
1363
- const parts = [
1364
- fragment`// Auto-generated. Do not edit.
1542
+ const wellKnownName = WELL_KNOWN_ADDRESSES.get(node.publicKey);
1543
+ const addressDeclaration = wellKnownName ? addressConstName === wellKnownName ? fragment`// Auto-generated. Do not edit.
1544
+ // ignore_for_file: type=lint
1545
+
1546
+ /// The address of the ${fragmentFromString(pascalCase(name))} program.
1547
+ export 'package:solana_kit_addresses/solana_kit_addresses.dart' show ${fragmentFromString(wellKnownName)};` : fragment`${use(wellKnownName, "solanaAddresses")}
1548
+
1549
+ // Auto-generated. Do not edit.
1550
+ // ignore_for_file: type=lint
1551
+
1552
+ /// The address of the ${fragmentFromString(pascalCase(name))} program.
1553
+ const ${fragmentFromString(addressConstName)} = ${fragmentFromString(wellKnownName)};` : fragment`// Auto-generated. Do not edit.
1365
1554
  // ignore_for_file: type=lint
1366
1555
 
1367
1556
  ${use("Address", "solanaAddresses")}
1368
1557
 
1369
1558
  /// The address of the ${fragmentFromString(pascalCase(name))} program.
1370
- const ${fragmentFromString(addressConstName)} = Address('${fragmentFromString(node.publicKey)}');`
1371
- ];
1559
+ const ${fragmentFromString(addressConstName)} = Address('${fragmentFromString(node.publicKey)}');`;
1560
+ const parts = [addressDeclaration];
1372
1561
  const accounts = node.accounts ?? [];
1373
1562
  if (accounts.length > 0) {
1374
1563
  const accountVariants = accounts.map((acc) => ` ${camelCase(acc.name)},`).join("\n");
@@ -1466,6 +1655,8 @@ function getDataEnumPageFragment(node, scope) {
1466
1655
  const variantClasses = [];
1467
1656
  const encoderVariants = [];
1468
1657
  const decoderVariants = [];
1658
+ const encodeCases = [];
1659
+ const decodeCases = [];
1469
1660
  const allVariantManifests = [];
1470
1661
  for (let i = 0; i < enumNode.variants.length; i++) {
1471
1662
  const variant = enumNode.variants[i];
@@ -1491,8 +1682,10 @@ function getDataEnumPageFragment(node, scope) {
1491
1682
  `(${i}, getStructEncoder(<(String, Encoder<Object?>)>[]))`
1492
1683
  );
1493
1684
  decoderVariants.push(
1494
- `(${i}, getStructDecoder(<(String, Decoder<Object?>)>[]).map((_) => const ${variantClassName}()))`
1685
+ `(${i}, transformDecoder<Map<String, Object?>, Map<String, Object?>>(getStructDecoder(<(String, Decoder<Object?>)>[]), (Map<String, Object?> map, Uint8List bytes, int offset) => <String, Object?>{}))`
1495
1686
  );
1687
+ encodeCases.push(`${variantClassName}() => <String, Object?>{'__kind': ${i}},`);
1688
+ decodeCases.push(`case ${i}: return const ${variantClassName}();`);
1496
1689
  } else if (variant.kind === "enumStructVariantTypeNode") {
1497
1690
  const resolvedStruct = resolveNestedTypeNode(variant.struct);
1498
1691
  const fields = resolvedStruct.fields;
@@ -1507,13 +1700,16 @@ function getDataEnumPageFragment(node, scope) {
1507
1700
  return ` final ${manifest.type.content} ${camelCase(f.name)};`;
1508
1701
  }).join("\n");
1509
1702
  const ctorParams = fields.map((f) => ` required this.${camelCase(f.name)},`).join("\n");
1510
- const eqChecks = fields.map((f) => `${camelCase(f.name)} == other.${camelCase(f.name)}`).join(" &&\n ");
1511
- const hashFields = fields.map((f) => camelCase(f.name)).join(", ");
1703
+ const ctorSignature = fields.length === 0 ? ` const ${variantClassName}();` : ` const ${variantClassName}({
1704
+ ${ctorParams}
1705
+ });`;
1706
+ const eqChecks = fields.length === 0 ? "true" : fields.map((f) => `${camelCase(f.name)} == other.${camelCase(f.name)}`).join(" &&\n ");
1707
+ const hashExpression = getHashExpression(
1708
+ fields.map((f) => camelCase(f.name))
1709
+ );
1512
1710
  const toStringFields = fields.map((f) => `${camelCase(f.name)}: $${camelCase(f.name)}`).join(", ");
1513
1711
  variantClasses.push(`final class ${variantClassName} extends ${typeName} {
1514
- const ${variantClassName}({
1515
- ${ctorParams}
1516
- });
1712
+ ${ctorSignature}
1517
1713
 
1518
1714
  ${fieldDecls}
1519
1715
 
@@ -1524,7 +1720,7 @@ ${fieldDecls}
1524
1720
  ${eqChecks};
1525
1721
 
1526
1722
  @override
1527
- int get hashCode => Object.hash(${hashFields});
1723
+ int get hashCode => ${hashExpression};
1528
1724
 
1529
1725
  @override
1530
1726
  String toString() => '${typeName}.${variantClassName}(${toStringFields})';
@@ -1543,15 +1739,19 @@ ${fieldDecls}
1543
1739
  return `${camelCase(f.name)}: ${accessor} as ${typeStr}`;
1544
1740
  }
1545
1741
  ).join(", ");
1546
- const toMapFields = fields.map(
1742
+ fields.map(
1547
1743
  (f) => `'${f.name}': value.${camelCase(f.name)}`
1548
1744
  ).join(", ");
1549
1745
  encoderVariants.push(
1550
- `(${i}, transformEncoder(getStructEncoder([${encFields}]), (${variantClassName} value) => <String, Object?>{${toMapFields}}))`
1746
+ `(${i}, getStructEncoder([${encFields}]))`
1551
1747
  );
1552
1748
  decoderVariants.push(
1553
- `(${i}, transformDecoder(getStructDecoder([${decFields}]), (Map<String, Object?> map, Uint8List bytes, int offset) => ${variantClassName}(${fromMapFields})))`
1749
+ `(${i}, transformDecoder<Map<String, Object?>, Map<String, Object?>>(getStructDecoder([${decFields}]), (Map<String, Object?> map, Uint8List bytes, int offset) => map))`
1554
1750
  );
1751
+ const structPattern = fields.length === 0 ? "" : fields.map((f) => `${camelCase(f.name)}: final ${camelCase(f.name)}`).join(", ");
1752
+ const structMapEntries = fields.length === 0 ? "" : `, ${fields.map((f) => `'${f.name}': ${camelCase(f.name)}`).join(", ")}`;
1753
+ encodeCases.push(`${variantClassName}(${structPattern}) => <String, Object?>{'__kind': ${i}${structMapEntries}},`);
1754
+ decodeCases.push(`case ${i}: return ${variantClassName}(${fromMapFields.replace(/\n/g, " ").replace(/,$/, "")});`);
1555
1755
  } else if (variant.kind === "enumTupleVariantTypeNode") {
1556
1756
  const resolvedTuple = resolveNestedTypeNode(variant.tuple);
1557
1757
  const items = resolvedTuple.items;
@@ -1575,11 +1775,13 @@ ${fieldDecls}
1575
1775
  String toString() => '${typeName}.${variantClassName}($value)';
1576
1776
  }`);
1577
1777
  encoderVariants.push(
1578
- `(${i}, transformEncoder(${manifest.encoder.content}, (${variantClassName} value) => value.value))`
1778
+ `(${i}, transformEncoder<${manifest.type.content}, Map<String, Object?>>(${manifest.encoder.content}, (Map<String, Object?> map) => map['value']! as ${manifest.type.content}))`
1579
1779
  );
1580
1780
  decoderVariants.push(
1581
- `(${i}, transformDecoder(${manifest.decoder.content}, (${manifest.type.content} value, Uint8List bytes, int offset) => ${variantClassName}(value)))`
1781
+ `(${i}, transformDecoder<${manifest.type.content}, Map<String, Object?>>(${manifest.decoder.content}, (${manifest.type.content} value, Uint8List bytes, int offset) => <String, Object?>{'value': value}))`
1582
1782
  );
1783
+ encodeCases.push(`${variantClassName}(value: final value) => <String, Object?>{'__kind': ${i}, 'value': value},`);
1784
+ decodeCases.push(`case ${i}: return ${variantClassName}(map['value']! as ${manifest.type.content});`);
1583
1785
  }
1584
1786
  }
1585
1787
  }
@@ -1593,7 +1795,6 @@ ${fieldDecls}
1593
1795
  // ignore_for_file: type=lint
1594
1796
 
1595
1797
  ${use("Uint8List", "dartTypedData")}
1596
- ${use("immutable", "meta")}
1597
1798
  ${use("Encoder", "solanaCodecsCore")}
1598
1799
  ${use("Decoder", "solanaCodecsCore")}
1599
1800
  ${use("Codec", "solanaCodecsCore")}
@@ -1614,15 +1815,28 @@ sealed class ${fragmentFromString(typeName)} {
1614
1815
  ${fragmentFromString(variantClasses.join("\n\n"))}
1615
1816
 
1616
1817
  Encoder<${fragmentFromString(typeName)}> ${fragmentFromString(encoderName)}() {
1617
- return getDiscriminatedUnionEncoder([
1618
- ${fragmentFromString(encoderVariants.join(",\n "))},
1619
- ], size: get${fragmentFromString(sizeCodecName)}Encoder());
1818
+ return transformEncoder<Map<String, Object?>, ${fragmentFromString(typeName)}>(
1819
+ getDiscriminatedUnionEncoder([
1820
+ ${fragmentFromString(encoderVariants.join(",\n "))},
1821
+ ], size: get${fragmentFromString(sizeCodecName)}Encoder()),
1822
+ (${fragmentFromString(typeName)} value) => switch (value) {
1823
+ ${fragmentFromString(encodeCases.join("\n "))}
1824
+ },
1825
+ );
1620
1826
  }
1621
1827
 
1622
1828
  Decoder<${fragmentFromString(typeName)}> ${fragmentFromString(decoderName)}() {
1623
- return getDiscriminatedUnionDecoder([
1624
- ${fragmentFromString(decoderVariants.join(",\n "))},
1625
- ], size: get${fragmentFromString(sizeCodecName)}Decoder());
1829
+ return transformDecoder<Map<String, Object?>, ${fragmentFromString(typeName)}>(
1830
+ getDiscriminatedUnionDecoder([
1831
+ ${fragmentFromString(decoderVariants.join(",\n "))},
1832
+ ], size: get${fragmentFromString(sizeCodecName)}Decoder()),
1833
+ (Map<String, Object?> map, Uint8List bytes, int offset) {
1834
+ switch (map['__kind']) {
1835
+ ${fragmentFromString(decodeCases.join("\n "))}
1836
+ }
1837
+ throw StateError('Unsupported ${typeName} discriminator: \${map['__kind']}');
1838
+ },
1839
+ );
1626
1840
  }
1627
1841
 
1628
1842
  Codec<${fragmentFromString(typeName)}, ${fragmentFromString(typeName)}> ${fragmentFromString(codecName)}() {
@@ -1649,10 +1863,13 @@ function getStructPageFragment(node, scope) {
1649
1863
  return ` final ${manifest.type.content} ${camelCase(f.name)};`;
1650
1864
  }).join("\n");
1651
1865
  const ctorParams = fields.map((f) => ` required this.${camelCase(f.name)},`).join("\n");
1866
+ const ctorSignature = fields.length === 0 ? ` const ${typeName}();` : ` const ${typeName}({
1867
+ ${ctorParams}
1868
+ });`;
1652
1869
  const eqChecks = fields.length === 0 ? "true" : fields.map(
1653
1870
  (f) => `${camelCase(f.name)} == other.${camelCase(f.name)}`
1654
1871
  ).join(" &&\n ");
1655
- const hashFields = fields.map((f) => camelCase(f.name)).join(", ");
1872
+ fields.length === 0 ? "runtimeType" : fields.map((f) => camelCase(f.name)).join(", ");
1656
1873
  const toStringFields = fields.map(
1657
1874
  (f) => `${camelCase(f.name)}: $${camelCase(f.name)}`
1658
1875
  ).join(", ");
@@ -1690,9 +1907,7 @@ ${use("getStructDecoder", "solanaCodecsDataStructures")}
1690
1907
 
1691
1908
  @immutable
1692
1909
  class ${fragmentFromString(typeName)} {
1693
- const ${fragmentFromString(typeName)}({
1694
- ${fragmentFromString(ctorParams)}
1695
- });
1910
+ ${fragmentFromString(ctorSignature)}
1696
1911
 
1697
1912
  ${fragmentFromString(fieldDecls)}
1698
1913
 
@@ -1704,7 +1919,7 @@ ${fragmentFromString(fieldDecls)}
1704
1919
  ${fragmentFromString(eqChecks)};
1705
1920
 
1706
1921
  @override
1707
- int get hashCode => Object.hash(${fragmentFromString(hashFields)});
1922
+ int get hashCode => ${fragmentFromString(getHashExpression(fields.map((f) => camelCase(f.name))))};
1708
1923
 
1709
1924
  @override
1710
1925
  String toString() => '${fragmentFromString(typeName)}(${fragmentFromString(toStringFields)})';
@@ -1775,6 +1990,11 @@ Codec<${fragmentFromString(typeName)}, ${fragmentFromString(typeName)}> ${fragme
1775
1990
  return combineCodec(${fragmentFromString(encoderName)}(), ${fragmentFromString(decoderName)}());
1776
1991
  }`;
1777
1992
  }
1993
+ function getHashExpression(fields) {
1994
+ if (fields.length === 0) return "runtimeType.hashCode";
1995
+ if (fields.length === 1) return `${fields[0]}.hashCode`;
1996
+ return `Object.hash(${fields.join(", ")})`;
1997
+ }
1778
1998
  function getNumberCodecName2(format) {
1779
1999
  switch (format) {
1780
2000
  case "u8":
@@ -1843,7 +2063,9 @@ function getRenderMapVisitor(options = {}) {
1843
2063
  visit(node.program, self),
1844
2064
  ...node.additionalPrograms.map((p) => visit(p, self))
1845
2065
  ];
1846
- return mergeRenderMaps(programMaps);
2066
+ const mergedProgramMap = mergeRenderMaps(programMaps);
2067
+ const categoryMaps = getSharedCategoryIndexMaps(mergedProgramMap);
2068
+ return mergeRenderMaps([mergedProgramMap, ...categoryMaps]);
1847
2069
  },
1848
2070
  visitProgram(node, { self }) {
1849
2071
  const programName = node.name;
@@ -1890,66 +2112,12 @@ function getRenderMapVisitor(options = {}) {
1890
2112
  maps.push(errorMap);
1891
2113
  }
1892
2114
  const categories = [];
1893
- if (accountFiles.length > 0) {
1894
- categories.push("accounts");
1895
- let indexMap = createRenderMap();
1896
- indexMap = addToRenderMap(
1897
- indexMap,
1898
- "accounts/accounts.dart",
1899
- getIndexPageFragment(accountFiles)
1900
- );
1901
- maps.push(indexMap);
1902
- }
1903
- if (instructionFiles.length > 0) {
1904
- categories.push("instructions");
1905
- let indexMap = createRenderMap();
1906
- indexMap = addToRenderMap(
1907
- indexMap,
1908
- "instructions/instructions.dart",
1909
- getIndexPageFragment(instructionFiles)
1910
- );
1911
- maps.push(indexMap);
1912
- }
1913
- if (typeFiles.length > 0) {
1914
- categories.push("types");
1915
- let indexMap = createRenderMap();
1916
- indexMap = addToRenderMap(
1917
- indexMap,
1918
- "types/types.dart",
1919
- getIndexPageFragment(typeFiles)
1920
- );
1921
- maps.push(indexMap);
1922
- }
1923
- if (pdaFiles.length > 0) {
1924
- categories.push("pdas");
1925
- let indexMap = createRenderMap();
1926
- indexMap = addToRenderMap(
1927
- indexMap,
1928
- "pdas/pdas.dart",
1929
- getIndexPageFragment(pdaFiles)
1930
- );
1931
- maps.push(indexMap);
1932
- }
1933
- if (errorFiles.length > 0) {
1934
- categories.push("errors");
1935
- let indexMap = createRenderMap();
1936
- indexMap = addToRenderMap(
1937
- indexMap,
1938
- "errors/errors.dart",
1939
- getIndexPageFragment(errorFiles)
1940
- );
1941
- maps.push(indexMap);
1942
- }
1943
- if (programFiles.length > 0) {
1944
- categories.push("programs");
1945
- let indexMap = createRenderMap();
1946
- indexMap = addToRenderMap(
1947
- indexMap,
1948
- "programs/programs.dart",
1949
- getIndexPageFragment(programFiles)
1950
- );
1951
- maps.push(indexMap);
1952
- }
2115
+ if (accountFiles.length > 0) categories.push("accounts");
2116
+ if (instructionFiles.length > 0) categories.push("instructions");
2117
+ if (typeFiles.length > 0) categories.push("types");
2118
+ if (pdaFiles.length > 0) categories.push("pdas");
2119
+ if (errorFiles.length > 0) categories.push("errors");
2120
+ if (programFiles.length > 0) categories.push("programs");
1953
2121
  if (categories.length > 0) {
1954
2122
  let rootMap = createRenderMap();
1955
2123
  rootMap = addToRenderMap(
@@ -1994,6 +2162,41 @@ function getRenderMapVisitor(options = {}) {
1994
2162
  (v) => recordLinkablesOnFirstVisitVisitor(v, linkables)
1995
2163
  );
1996
2164
  }
2165
+ function getSharedCategoryIndexMaps(renderMap) {
2166
+ const categories = /* @__PURE__ */ new Map();
2167
+ const categoryIndexNames = {
2168
+ accounts: "accounts.dart",
2169
+ instructions: "instructions.dart",
2170
+ types: "types.dart",
2171
+ pdas: "pdas.dart",
2172
+ errors: "errors.dart",
2173
+ programs: "programs.dart"
2174
+ };
2175
+ for (const renderPath of renderMap.keys()) {
2176
+ const match = renderPath.match(
2177
+ /^(accounts|instructions|types|pdas|errors|programs)\/([^/]+\.dart)$/
2178
+ );
2179
+ if (!match) continue;
2180
+ const [, category, fileName] = match;
2181
+ if (fileName === categoryIndexNames[category]) continue;
2182
+ if (!categories.has(category)) {
2183
+ categories.set(category, /* @__PURE__ */ new Set());
2184
+ }
2185
+ categories.get(category).add(fileName);
2186
+ }
2187
+ const maps = [];
2188
+ for (const [category, files] of categories.entries()) {
2189
+ const sortedFiles = [...files].sort();
2190
+ let map = createRenderMap();
2191
+ map = addToRenderMap(
2192
+ map,
2193
+ `${category}/${categoryIndexNames[category]}`,
2194
+ getIndexPageFragment(sortedFiles)
2195
+ );
2196
+ maps.push(map);
2197
+ }
2198
+ return maps;
2199
+ }
1997
2200
 
1998
2201
  // src/visitors/renderVisitor.ts
1999
2202
  function renderVisitor(outputDir, options = {}) {