codama-renderers-dart 0.5.1 → 0.5.4

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.
Files changed (37) hide show
  1. package/{readme.md → README.md} +16 -43
  2. package/dist/index.browser.cjs +345 -124
  3. package/dist/index.browser.cjs.map +1 -1
  4. package/dist/index.browser.js +346 -125
  5. package/dist/index.browser.js.map +1 -1
  6. package/dist/index.node.cjs +345 -124
  7. package/dist/index.node.cjs.map +1 -1
  8. package/dist/index.node.js +346 -125
  9. package/dist/index.node.js.map +1 -1
  10. package/dist/index.react-native.js +346 -125
  11. package/dist/index.react-native.js.map +1 -1
  12. package/dist/types/fragments/accountPage.d.ts.map +1 -1
  13. package/dist/types/fragments/errorPage.d.ts.map +1 -1
  14. package/dist/types/fragments/instructionPage.d.ts.map +1 -1
  15. package/dist/types/fragments/pdaPage.d.ts.map +1 -1
  16. package/dist/types/fragments/programPage.d.ts.map +1 -1
  17. package/dist/types/utils/nameTransformers.d.ts +5 -0
  18. package/dist/types/utils/nameTransformers.d.ts.map +1 -1
  19. package/dist/types/utils/valueNodes.d.ts +4 -0
  20. package/dist/types/utils/valueNodes.d.ts.map +1 -1
  21. package/dist/types/visitors/getRenderMapVisitor.d.ts.map +1 -1
  22. package/dist/types/visitors/getTypeManifestVisitor.d.ts +1 -1
  23. package/dist/types/visitors/getTypeManifestVisitor.d.ts.map +1 -1
  24. package/dist/types/visitors/renderVisitor.d.ts.map +1 -1
  25. package/package.json +7 -7
  26. package/src/fragments/accountPage.ts +2 -1
  27. package/src/fragments/errorPage.ts +8 -5
  28. package/src/fragments/instructionPage.ts +54 -17
  29. package/src/fragments/pdaPage.ts +7 -14
  30. package/src/fragments/programPage.ts +3 -2
  31. package/src/fragments/typePage.ts +102 -19
  32. package/src/utils/formatCode.ts +2 -2
  33. package/src/utils/nameTransformers.ts +39 -1
  34. package/src/utils/valueNodes.ts +24 -1
  35. package/src/visitors/getRenderMapVisitor.ts +30 -5
  36. package/src/visitors/getTypeManifestVisitor.ts +123 -81
  37. package/src/visitors/renderVisitor.ts +11 -13
@@ -12,7 +12,7 @@ var errors = require('@codama/errors');
12
12
 
13
13
  // src/visitors/renderVisitor.ts
14
14
  function formatDartDirectory(dir) {
15
- child_process.execSync(`dart format "${dir}"`, {
15
+ child_process.execFileSync("dart", ["format", "--", dir], {
16
16
  encoding: "utf-8",
17
17
  timeout: 6e4,
18
18
  stdio: ["pipe", "pipe", "pipe"]
@@ -82,13 +82,89 @@ function setEmptyCollection(record, key) {
82
82
 
83
83
  // src/utils/nameTransformers.ts
84
84
  function pascalCase(str) {
85
+ assertSafeName(str);
85
86
  return str.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^(.)/, (c) => c.toUpperCase());
86
87
  }
88
+ var DART_RESERVED_WORDS = /* @__PURE__ */ new Set([
89
+ "abstract",
90
+ "as",
91
+ "assert",
92
+ "async",
93
+ "augment",
94
+ "await",
95
+ "base",
96
+ "break",
97
+ "case",
98
+ "catch",
99
+ "class",
100
+ "const",
101
+ "continue",
102
+ "covariant",
103
+ "default",
104
+ "deferred",
105
+ "do",
106
+ "dynamic",
107
+ "else",
108
+ "enum",
109
+ "export",
110
+ "extends",
111
+ "extension",
112
+ "external",
113
+ "factory",
114
+ "false",
115
+ "final",
116
+ "finally",
117
+ "for",
118
+ "get",
119
+ "hide",
120
+ "if",
121
+ "implements",
122
+ "import",
123
+ "in",
124
+ "interface",
125
+ "is",
126
+ "late",
127
+ "library",
128
+ "mixin",
129
+ "native",
130
+ "new",
131
+ "null",
132
+ "of",
133
+ "on",
134
+ "operator",
135
+ "part",
136
+ "required",
137
+ "rethrow",
138
+ "return",
139
+ "sealed",
140
+ "set",
141
+ "show",
142
+ "static",
143
+ "super",
144
+ "switch",
145
+ "sync",
146
+ "this",
147
+ "throw",
148
+ "true",
149
+ "try",
150
+ "typedef",
151
+ "var",
152
+ "void",
153
+ "when",
154
+ "while",
155
+ "with",
156
+ "yield"
157
+ ]);
158
+ function escapeDartIdentifier(name) {
159
+ return DART_RESERVED_WORDS.has(name) ? `${name}_` : name;
160
+ }
87
161
  function camelCase(str) {
88
162
  const pascal = pascalCase(str);
89
- return pascal.charAt(0).toLowerCase() + pascal.slice(1);
163
+ const cased = pascal.charAt(0).toLowerCase() + pascal.slice(1);
164
+ return escapeDartIdentifier(cased);
90
165
  }
91
166
  function snakeCase(str) {
167
+ assertSafeName(str);
92
168
  return str.replace(/([A-Z])/g, "_$1").replace(/[-\s]+/g, "_").replace(/^_/, "").replace(/_+/g, "_").toLowerCase();
93
169
  }
94
170
  function createDartNameApi() {
@@ -114,6 +190,11 @@ function createDartNameApi() {
114
190
  discriminatorConstant: (name) => `${camelCase(name)}Discriminator`
115
191
  };
116
192
  }
193
+ function assertSafeName(name) {
194
+ if (!/^[a-zA-Z0-9_ -]*$/.test(name)) {
195
+ throw new Error(`Invalid Dart node name: ${JSON.stringify(name)}`);
196
+ }
197
+ }
117
198
 
118
199
  // src/utils/codecs.ts
119
200
  function bytesToDartHexList(bytes) {
@@ -324,10 +405,19 @@ var WELL_KNOWN_ADDRESSES = /* @__PURE__ */ new Map([
324
405
  function getDartValueFragment(value, dartType) {
325
406
  switch (value.kind) {
326
407
  case "numberValueNode":
408
+ if (typeof value.number !== "number" || !Number.isFinite(value.number)) {
409
+ throw new Error("Invalid Dart number: expected a finite number");
410
+ }
411
+ if (dartType === "BigInt" && !Number.isSafeInteger(value.number)) {
412
+ throw new Error("Invalid Dart BigInt: expected a safe integer");
413
+ }
327
414
  return fragmentFromString(
328
415
  dartType === "BigInt" ? `BigInt.from(${value.number})` : String(value.number)
329
416
  );
330
417
  case "booleanValueNode":
418
+ if (typeof value.boolean !== "boolean") {
419
+ throw new Error("Invalid Dart boolean: expected a boolean");
420
+ }
331
421
  return fragmentFromString(String(value.boolean));
332
422
  case "stringValueNode":
333
423
  return fragmentFromString(toDartStringLiteral(value.string));
@@ -371,6 +461,12 @@ function isConstDartValueNode(value) {
371
461
  function toDartStringLiteral(value) {
372
462
  return `'${value.replaceAll("\\", "\\\\").replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t").replaceAll("\b", "\\b").replaceAll("\f", "\\f").replaceAll("$", "\\$").replaceAll("'", "\\'")}'`;
373
463
  }
464
+ function getNonNegativeInteger(value) {
465
+ if (!Number.isSafeInteger(value) || value < 0) {
466
+ throw new Error("Expected a non-negative safe integer value");
467
+ }
468
+ return String(value);
469
+ }
374
470
 
375
471
  // src/utils/discriminators.ts
376
472
  function getDiscriminatorValidationFragment(node, scope) {
@@ -571,23 +667,12 @@ function getTypeManifestVisitor(input) {
571
667
  }
572
668
  );
573
669
  return visitorsCore.extendVisitor(baseVisitor, {
574
- visitNumberType(node, { self }) {
575
- const { format, endian } = node;
576
- const endianSuffix = endian === "be" ? ", NumberCodecConfig(endian: Endian.big)" : "";
577
- const endianDecoderSuffix = endian === "be" ? ", NumberCodecConfig(endian: Endian.big)" : "";
578
- const dartType = getNumberDartType(format);
579
- const codecName = getNumberCodecName(format);
670
+ visitNumberType(node) {
580
671
  return {
581
672
  // `BigInt` and other number types live in `dart:core` and need no import.
582
- type: fragmentFromString(dartType),
583
- encoder: fragment`${use(
584
- `get${codecName}Encoder`,
585
- "solanaCodecsNumbers"
586
- )}(${endianSuffix ? fragmentFromString(endianSuffix.slice(2)) : fragmentFromString("")})`,
587
- decoder: fragment`${use(
588
- `get${codecName}Decoder`,
589
- "solanaCodecsNumbers"
590
- )}(${endianDecoderSuffix ? fragmentFromString(endianDecoderSuffix.slice(2)) : fragmentFromString("")})`,
673
+ type: fragmentFromString(getNumberDartType(node.format)),
674
+ encoder: getNumberCodecExpression(node, "Encoder"),
675
+ decoder: getNumberCodecExpression(node, "Decoder"),
591
676
  value: emptyTypeManifest().value,
592
677
  isEnum: false
593
678
  };
@@ -674,7 +759,7 @@ function getTypeManifestVisitor(input) {
674
759
  encoder: fragment`${use(
675
760
  "getArrayEncoder",
676
761
  "solanaCodecsDataStructures"
677
- )}<${itemType}>(${itemEncoder}${encoderSizeExpr})`,
762
+ )}(${itemEncoder}${encoderSizeExpr})`,
678
763
  decoder: fragment`${use(
679
764
  "getArrayDecoder",
680
765
  "solanaCodecsDataStructures"
@@ -730,18 +815,28 @@ function getTypeManifestVisitor(input) {
730
815
  typeFragment.imports.mergeWith(m.type.imports);
731
816
  }
732
817
  const encoderListStr = encoderList.map((e) => e.content).join(", ");
818
+ const decoderListStr = decoderList.map((d) => d.content).join(", ");
819
+ let tupleEncoderFn = "getTupleEncoder";
820
+ let tupleDecoderFn = "getTupleDecoder";
821
+ let tupleEncoderArgs = `[${encoderListStr}]`;
822
+ let tupleDecoderArgs = `[${decoderListStr}]`;
823
+ if (itemManifests.length === 2) {
824
+ tupleEncoderFn = "getTuple2Encoder";
825
+ tupleDecoderFn = "getTuple2Decoder";
826
+ tupleEncoderArgs = encoderListStr;
827
+ tupleDecoderArgs = decoderListStr;
828
+ }
733
829
  const encoderFrag = fragment`${use(
734
- "getTupleEncoder",
830
+ tupleEncoderFn,
735
831
  "solanaCodecsDataStructures"
736
- )}([${fragmentFromString(encoderListStr)}])`;
832
+ )}(${tupleEncoderArgs})`;
737
833
  for (const e of encoderList) {
738
834
  encoderFrag.imports.mergeWith(e.imports);
739
835
  }
740
- const decoderListStr = decoderList.map((d) => d.content).join(", ");
741
836
  const decoderFrag = fragment`${use(
742
- "getTupleDecoder",
837
+ tupleDecoderFn,
743
838
  "solanaCodecsDataStructures"
744
- )}([${fragmentFromString(decoderListStr)}])`;
839
+ )}(${tupleDecoderArgs})`;
745
840
  for (const d of decoderList) {
746
841
  decoderFrag.imports.mergeWith(d.imports);
747
842
  }
@@ -758,7 +853,7 @@ function getTypeManifestVisitor(input) {
758
853
  const fields = nodeFields.map((f) => visitorsCore.visit(f, self));
759
854
  const fieldNames = nodeFields.map((f) => f.name);
760
855
  const typeStr = "Map<String, Object?>";
761
- const encoderFields = fields.map((f, i) => `('${fieldNames[i]}', ${f.encoder.content})`).join(", ");
856
+ const encoderFields = fields.map((f, i) => `(${toDartStringLiteral(fieldNames[i])}, ${f.encoder.content})`).join(", ");
762
857
  const encoderFrag = fragment`${use(
763
858
  "getStructEncoder",
764
859
  "solanaCodecsDataStructures"
@@ -766,7 +861,7 @@ function getTypeManifestVisitor(input) {
766
861
  for (const f of fields) {
767
862
  encoderFrag.imports.mergeWith(f.encoder.imports);
768
863
  }
769
- const decoderFields = fields.map((f, i) => `('${fieldNames[i]}', ${f.decoder.content})`).join(", ");
864
+ const decoderFields = fields.map((f, i) => `(${toDartStringLiteral(fieldNames[i])}, ${f.decoder.content})`).join(", ");
770
865
  const decoderFrag = fragment`${use(
771
866
  "getStructDecoder",
772
867
  "solanaCodecsDataStructures"
@@ -820,19 +915,22 @@ function getTypeManifestVisitor(input) {
820
915
  const itemManifest = visitorsCore.visit(node.item, self);
821
916
  const itemTypeStr = itemManifest.type.content;
822
917
  const resolvedPrefix = nodes.resolveNestedTypeNode(node.prefix);
823
- const prefixExpr = resolvedPrefix.format !== "u8" ? `, prefix: ${getNumberCodecExpression(resolvedPrefix.format, "Encoder")}` : "";
824
- const prefixDecoderExpr = resolvedPrefix.format !== "u8" ? `, prefix: ${getNumberCodecExpression(resolvedPrefix.format, "Decoder")}` : "";
918
+ const prefixExpr = resolvedPrefix.format !== "u8" ? fragment`, prefix: ${getNumberCodecExpression(resolvedPrefix, "Encoder")}` : "";
919
+ const prefixDecoderExpr = resolvedPrefix.format !== "u8" ? fragment`, prefix: ${getNumberCodecExpression(resolvedPrefix, "Decoder")}` : "";
825
920
  const noneValueExpr = node.fixed ? ", noneValue: const ZeroesNoneValue()" : "";
826
921
  return {
827
922
  type: fragment`${itemManifest.type}?`,
828
923
  encoder: fragment`${use(
829
924
  "getNullableEncoder",
830
925
  "solanaCodecsDataStructures"
831
- )}<${fragmentFromString(itemTypeStr)}>(${itemManifest.encoder}${fragmentFromString(prefixExpr)}${fragmentFromString(noneValueExpr)})`,
926
+ )}<${fragmentFromString(itemTypeStr)}>(${use(
927
+ "transformEncoder",
928
+ "solanaCodecsCore"
929
+ )}(${itemManifest.encoder}, (${fragmentFromString(itemTypeStr)} value) => value)${prefixExpr}${fragmentFromString(noneValueExpr)})`,
832
930
  decoder: fragment`${use(
833
931
  "getNullableDecoder",
834
932
  "solanaCodecsDataStructures"
835
- )}<${fragmentFromString(itemTypeStr)}>(${itemManifest.decoder}${fragmentFromString(prefixDecoderExpr)}${fragmentFromString(noneValueExpr)})`,
933
+ )}<${fragmentFromString(itemTypeStr)}>(${itemManifest.decoder}${prefixDecoderExpr}${fragmentFromString(noneValueExpr)})`,
836
934
  value: emptyTypeManifest().value,
837
935
  isEnum: false
838
936
  };
@@ -878,11 +976,11 @@ function getTypeManifestVisitor(input) {
878
976
  encoder: fragment`${use(
879
977
  "fixEncoderSize",
880
978
  "solanaCodecsCore"
881
- )}(${innerManifest.encoder}, ${fragmentFromString(String(node.size))}, allowTruncation: false)`,
979
+ )}(${innerManifest.encoder}, ${fragmentFromString(getNonNegativeInteger(node.size))}, allowTruncation: false)`,
882
980
  decoder: fragment`${use(
883
981
  "fixDecoderSize",
884
982
  "solanaCodecsCore"
885
- )}(${innerManifest.decoder}, ${fragmentFromString(String(node.size))})`,
983
+ )}(${innerManifest.decoder}, ${fragmentFromString(getNonNegativeInteger(node.size))})`,
886
984
  value: innerManifest.value,
887
985
  isEnum: innerManifest.isEnum
888
986
  };
@@ -892,10 +990,10 @@ function getTypeManifestVisitor(input) {
892
990
  const bigIntFormats = /* @__PURE__ */ new Set(["u64", "u128", "i64", "i128"]);
893
991
  let prefixManifest;
894
992
  if (node.prefix.kind === "numberTypeNode" && bigIntFormats.has(node.prefix.format)) {
895
- const codecName = getNumberCodecName(node.prefix.format);
993
+ const numberManifest = visitorsCore.visit(node.prefix, self);
896
994
  prefixManifest = {
897
- encoder: fragment`transformEncoder(${use(`get${codecName}Encoder`, "solanaCodecsNumbers")}(), (size) => BigInt.from(size))`,
898
- decoder: fragment`transformDecoder(${use(`get${codecName}Decoder`, "solanaCodecsNumbers")}(), (size, _, __) => size.toInt())`
995
+ encoder: fragment`${use("transformEncoder", "solanaCodecsCore")}(${numberManifest.encoder}, (size) => BigInt.from(size))`,
996
+ decoder: fragment`${use("transformDecoder", "solanaCodecsCore")}(${numberManifest.decoder}, (size, _, __) => size.toInt())`
899
997
  };
900
998
  } else {
901
999
  prefixManifest = visitorsCore.visit(node.prefix, self);
@@ -986,10 +1084,10 @@ function getTypeManifestVisitor(input) {
986
1084
  };
987
1085
  },
988
1086
  visitPreOffsetType(node, { self }) {
989
- return visitorsCore.visit(node.type, self);
1087
+ return getOffsetManifest(node, self);
990
1088
  },
991
1089
  visitPostOffsetType(node, { self }) {
992
- return visitorsCore.visit(node.type, self);
1090
+ return getOffsetManifest(node, self);
993
1091
  },
994
1092
  visitSolAmountType(node, { self }) {
995
1093
  return visitorsCore.visit(node.number, self);
@@ -1078,16 +1176,17 @@ function getNumberCodecName(format) {
1078
1176
  return "U8";
1079
1177
  }
1080
1178
  }
1081
- function getNumberCodecExpression(format, codecType) {
1082
- const name = getNumberCodecName(format);
1083
- return `get${name}${codecType}()`;
1179
+ function getNumberCodecExpression(node, codecType) {
1180
+ const name = getNumberCodecName(node.format);
1181
+ const config = node.endian === "be" ? fragment`${use("NumberCodecConfig", "solanaCodecsNumbers")}(endian: ${use("Endian", "dartTypedData")}.big)` : "";
1182
+ return fragment`${use(`get${name}${codecType}`, "solanaCodecsNumbers")}(${config})`;
1084
1183
  }
1085
1184
  function getArraySizeExpression(node, codecType) {
1086
1185
  if (!("count" in node) || !node.count) return "";
1087
1186
  const count = node.count;
1088
1187
  switch (count.kind) {
1089
1188
  case "fixedCountNode":
1090
- return `, size: FixedArraySize(${count.value})`;
1189
+ return `, size: FixedArraySize(${getNonNegativeInteger(count.value)})`;
1091
1190
  case "remainderCountNode":
1092
1191
  return ", size: RemainderArraySize()";
1093
1192
  case "prefixedCountNode": {
@@ -1095,7 +1194,7 @@ function getArraySizeExpression(node, codecType) {
1095
1194
  if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
1096
1195
  return "";
1097
1196
  }
1098
- return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix.format, codecType)})`;
1197
+ return fragment`, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix, codecType)})`;
1099
1198
  }
1100
1199
  default:
1101
1200
  return "";
@@ -1106,7 +1205,7 @@ function getMapSizeExpression(node, codecType) {
1106
1205
  const count = node.count;
1107
1206
  switch (count.kind) {
1108
1207
  case "fixedCountNode":
1109
- return `, size: FixedArraySize(${count.value})`;
1208
+ return `, size: FixedArraySize(${getNonNegativeInteger(count.value)})`;
1110
1209
  case "remainderCountNode":
1111
1210
  return ", size: RemainderArraySize()";
1112
1211
  case "prefixedCountNode": {
@@ -1114,7 +1213,7 @@ function getMapSizeExpression(node, codecType) {
1114
1213
  if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
1115
1214
  return "";
1116
1215
  }
1117
- return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix.format, codecType)})`;
1216
+ return fragment`, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix, codecType)})`;
1118
1217
  }
1119
1218
  default:
1120
1219
  return "";
@@ -1125,7 +1224,7 @@ function getSetSizeExpression(node, codecType) {
1125
1224
  const count = node.count;
1126
1225
  switch (count.kind) {
1127
1226
  case "fixedCountNode":
1128
- return `, size: FixedArraySize(${count.value})`;
1227
+ return `, size: FixedArraySize(${getNonNegativeInteger(count.value)})`;
1129
1228
  case "remainderCountNode":
1130
1229
  return ", size: RemainderArraySize()";
1131
1230
  case "prefixedCountNode": {
@@ -1133,7 +1232,7 @@ function getSetSizeExpression(node, codecType) {
1133
1232
  if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
1134
1233
  return "";
1135
1234
  }
1136
- return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix.format, codecType)})`;
1235
+ return fragment`, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix, codecType)})`;
1137
1236
  }
1138
1237
  default:
1139
1238
  return "";
@@ -1144,36 +1243,61 @@ function getHiddenAffixManifest(node, self) {
1144
1243
  const manifest = visitorsCore.visit(node, self);
1145
1244
  return { encoder: manifest.encoder, decoder: manifest.decoder };
1146
1245
  }
1147
- const constantBytes = getConstantBytesExpression(node);
1246
+ const constantBytes = getConstantBytesExpression(node, self);
1148
1247
  return {
1149
1248
  encoder: fragment`${use("getConstantEncoder", "solanaCodecsDataStructures")}(${constantBytes})`,
1150
1249
  decoder: fragment`${use("getConstantDecoder", "solanaCodecsDataStructures")}(${constantBytes})`
1151
1250
  };
1152
1251
  }
1153
- function getConstantBytesExpression(node) {
1154
- const valueNode = node.value;
1155
- const typeNode = node.type;
1156
- if (valueNode?.kind !== "numberValueNode") {
1252
+ function getConstantBytesExpression(node, self) {
1253
+ if (node.value.kind !== "numberValueNode") {
1157
1254
  throw new Error(
1158
- `Unsupported hidden affix constant value kind: ${valueNode?.kind}`
1255
+ `Unsupported hidden affix constant value kind: ${node.value.kind}`
1159
1256
  );
1160
1257
  }
1161
- const value = Number(valueNode.number);
1162
- if (typeNode?.kind === "numberTypeNode") {
1163
- return fragment`${use(`get${getNumberCodecName(typeNode.format)}Encoder`, "solanaCodecsNumbers")}().encode(${value})`;
1258
+ const manifest = visitorsCore.visit(node.type, self);
1259
+ if (!["int", "BigInt", "double"].includes(manifest.type.content)) {
1260
+ throw new Error(`Unsupported hidden affix constant type kind: ${node.type.kind}`);
1164
1261
  }
1165
- if (typeNode?.kind === "preOffsetTypeNode") {
1166
- const inner = typeNode.type;
1167
- if (inner?.kind !== "numberTypeNode") {
1168
- throw new Error(
1169
- `Unsupported hidden affix pre-offset inner type kind: ${inner?.kind}`
1170
- );
1171
- }
1172
- return fragment`${use("padLeftEncoder", "solanaCodecsCore")}(${use(`get${getNumberCodecName(inner.format)}Encoder`, "solanaCodecsNumbers")}(), ${typeNode.offset}).encode(${value})`;
1262
+ const value = getDartValueFragment(node.value, manifest.type.content);
1263
+ return fragment`${manifest.encoder}.encode(${value})`;
1264
+ }
1265
+ function getOffsetManifest(node, self) {
1266
+ if (!Number.isSafeInteger(node.offset)) {
1267
+ throw new Error("Expected a safe integer for a codec offset");
1173
1268
  }
1174
- throw new Error(
1175
- `Unsupported hidden affix constant type kind: ${typeNode?.kind}`
1176
- );
1269
+ const manifest = visitorsCore.visit(node.type, self);
1270
+ const pre = node.kind === "preOffsetTypeNode";
1271
+ const key = pre ? "preOffset" : "postOffset";
1272
+ if (node.strategy === "padded") {
1273
+ const side = pre ? "Left" : "Right";
1274
+ const offset = getNonNegativeInteger(node.offset);
1275
+ return {
1276
+ ...manifest,
1277
+ encoder: fragment`${use(`pad${side}Encoder`, "solanaCodecsCore")}(${manifest.encoder}, ${offset})`,
1278
+ decoder: fragment`${use(`pad${side}Decoder`, "solanaCodecsCore")}(${manifest.decoder}, ${offset})`
1279
+ };
1280
+ }
1281
+ let expression;
1282
+ switch (node.strategy) {
1283
+ case "absolute":
1284
+ expression = node.offset < 0 ? `scope.wrapBytes(${node.offset})` : String(node.offset);
1285
+ break;
1286
+ case "relative":
1287
+ expression = `scope.${key} + ${node.offset}`;
1288
+ break;
1289
+ case "preOffset":
1290
+ expression = `scope.preOffset + ${node.offset}`;
1291
+ break;
1292
+ default:
1293
+ throw new Error(`Unsupported offset strategy: ${node.strategy}`);
1294
+ }
1295
+ const config = fragment`${use("OffsetConfig", "solanaCodecsCore")}(${key}: (scope) => ${expression})`;
1296
+ return {
1297
+ ...manifest,
1298
+ encoder: fragment`${use("offsetEncoder", "solanaCodecsCore")}(${manifest.encoder}, ${config})`,
1299
+ decoder: fragment`${use("offsetDecoder", "solanaCodecsCore")}(${manifest.decoder}, ${config})`
1300
+ };
1177
1301
  }
1178
1302
 
1179
1303
  // src/fragments/discriminatorConstants.ts
@@ -1306,7 +1430,7 @@ ${ctorParams}
1306
1430
  const decodeFnName = scope.nameApi.accountDecodeFunction(name);
1307
1431
  const sizeFragment = node.size != null ? fragment`
1308
1432
  /// The size of the [${fragmentFromString(typeName)}] account data in bytes.
1309
- const int ${fragmentFromString(scope.nameApi.accountSizeConstant(name))} = ${fragmentFromString(String(node.size))};` : emptyFragment();
1433
+ const int ${fragmentFromString(scope.nameApi.accountSizeConstant(name))} = ${fragmentFromString(getNonNegativeInteger(node.size))};` : emptyFragment();
1310
1434
  const discFragment = getDiscriminatorConstantsFragment(node, scope);
1311
1435
  const discriminatorValidation = getDiscriminatorValidationFragment(
1312
1436
  node,
@@ -1420,9 +1544,10 @@ function getErrorPageFragment(programNode, scope) {
1420
1544
  programName,
1421
1545
  err.name
1422
1546
  );
1547
+ getNonNegativeInteger(err.code);
1423
1548
  const hexCode = `0x${err.code.toString(16)}`;
1424
- const docs = err.docs?.length ? err.docs.map((d) => `/// ${d}`).join("\n") + "\n" : "";
1425
- const message = err.message ? `/// Message: "${err.message}"
1549
+ const docs = err.docs?.length ? err.docs.map(toDartDocComment).join("\n") + "\n" : "";
1550
+ const message = err.message ? `${toDartDocComment(`Message: "${err.message}"`)}
1426
1551
  ` : "";
1427
1552
  return `${docs}${message}const int ${constName} = ${hexCode}; // ${err.code}`;
1428
1553
  }).join("\n\n");
@@ -1432,7 +1557,7 @@ function getErrorPageFragment(programNode, scope) {
1432
1557
  err.name
1433
1558
  );
1434
1559
  const message = err.message ?? err.name;
1435
- return ` ${constName}: '${escapeString(message)}',`;
1560
+ return ` ${constName}: ${toDartStringLiteral(message)},`;
1436
1561
  }).join("\n");
1437
1562
  const errorMessageFnName = scope.nameApi.errorMessageFunction(programName);
1438
1563
  const isErrorFnName = `is${pascalCase(programName)}Error`;
@@ -1458,8 +1583,8 @@ bool ${fragmentFromString(isErrorFnName)}(int code) {
1458
1583
  return _${fragmentFromString(camelCase(programName))}ErrorMessages.containsKey(code);
1459
1584
  }`;
1460
1585
  }
1461
- function escapeString(str) {
1462
- return str.replace(/'/g, "\\'").replace(/\n/g, "\\n");
1586
+ function toDartDocComment(value) {
1587
+ return value.split(/\r\n?|\n/).map((line) => `/// ${line}`).join("\n");
1463
1588
  }
1464
1589
 
1465
1590
  // src/fragments/indexPage.ts
@@ -1585,10 +1710,28 @@ ${dataCtorInitializers}` : ""};`;
1585
1710
  const dataEncoderName = `get${typeName}InstructionDataEncoder`;
1586
1711
  const dataDecoderName = `get${typeName}InstructionDataDecoder`;
1587
1712
  const dataCodecName = `get${typeName}InstructionDataCodec`;
1588
- const argNames = new Set(args.map((a) => camelCase(a.name)));
1589
- const accountNames = new Set(accounts.map((a) => camelCase(a.name)));
1590
- const hasProgramAddressCollision = argNames.has("programAddress") || accountNames.has("programAddress");
1591
- const instrProgramParam = hasProgramAddressCollision ? "instructionProgramAddress" : "programAddress";
1713
+ const usedNames = /* @__PURE__ */ new Set([
1714
+ ...args.map((arg) => camelCase(arg.name)),
1715
+ ...accounts.map((account) => camelCase(account.name))
1716
+ ]);
1717
+ const instrProgramParam = reserveName(
1718
+ usedNames.has("programAddress") ? "instructionProgramAddress" : "programAddress",
1719
+ usedNames
1720
+ );
1721
+ const instructionDataLocal = reserveName("instructionData", usedNames);
1722
+ const signerParams = /* @__PURE__ */ new Map();
1723
+ for (const account of accounts) {
1724
+ if (account.isSigner === "either") {
1725
+ signerParams.set(
1726
+ account,
1727
+ reserveName(`${camelCase(account.name)}IsSigner`, usedNames)
1728
+ );
1729
+ }
1730
+ }
1731
+ const signerParamDeclarations = [...signerParams.values()].map((parameter) => ` bool ${parameter} = true,`).join("\n");
1732
+ const signerParamDocs = [...signerParams.entries()].map(
1733
+ ([account, parameter]) => `/// Set [${parameter}] to false when [${camelCase(account.name)}] does not sign (for example, a multisig authority).`
1734
+ ).join("\n");
1592
1735
  const accountParams = accounts.map((acc) => {
1593
1736
  const fieldName = camelCase(acc.name);
1594
1737
  const isRequired = !(acc.isOptional ?? false);
@@ -1608,7 +1751,7 @@ ${dataCtorInitializers}` : ""};`;
1608
1751
  }).join("\n");
1609
1752
  const accountMetas = accounts.map((acc) => {
1610
1753
  const fieldName = camelCase(acc.name);
1611
- const role = getAccountRole(acc);
1754
+ const role = getAccountRole(acc, signerParams.get(acc));
1612
1755
  const isOptional = acc.isOptional ?? false;
1613
1756
  if (isOptional) {
1614
1757
  if (optionalAccountStrategy === "omitted") {
@@ -1691,13 +1834,15 @@ Codec<${fragmentFromString(dataClassName)}, ${fragmentFromString(dataClassName)}
1691
1834
  return combineCodec(${fragmentFromString(dataEncoderName)}(), ${fragmentFromString(dataDecoderName)}());
1692
1835
  }`);
1693
1836
  parts.push(fragment`
1694
- /// Creates a [${fragmentFromString(typeName)}] instruction.
1837
+ /// Creates a [${fragmentFromString(typeName)}] instruction.${fragmentFromString(signerParamDocs ? `
1838
+ ${signerParamDocs}` : "")}
1695
1839
  Instruction ${fragmentFromString(instrFnName)}({
1696
1840
  required Address ${fragmentFromString(instrProgramParam)},
1697
1841
  ${fragmentFromString(accountParams)}
1698
- ${fragmentFromString(argParams)}
1842
+ ${fragmentFromString(argParams)}${fragmentFromString(signerParamDeclarations ? `
1843
+ ${signerParamDeclarations}` : "")}
1699
1844
  }) {
1700
- final instructionData = ${fragmentFromString(dataClassName)}(
1845
+ final ${instructionDataLocal} = ${fragmentFromString(dataClassName)}(
1701
1846
  ${fragmentFromString(dataConstruction)}
1702
1847
  );
1703
1848
 
@@ -1706,7 +1851,7 @@ ${fragmentFromString(dataConstruction)}
1706
1851
  accounts: [
1707
1852
  ${fragmentFromString(accountMetas)}
1708
1853
  ],
1709
- data: ${fragmentFromString(dataEncoderName)}().encode(instructionData),
1854
+ data: ${fragmentFromString(dataEncoderName)}().encode(${instructionDataLocal}),
1710
1855
  );
1711
1856
  }`);
1712
1857
  parts.push(fragment`
@@ -1725,13 +1870,20 @@ ${fragmentFromString(dataClassName)} ${fragmentFromString(parseFnName)}(Instruct
1725
1870
  }
1726
1871
  return result;
1727
1872
  }
1728
- function getAccountRole(acc) {
1729
- const isSigner = acc.isSigner === true || acc.isSigner === "either";
1730
- const isWritable = acc.isWritable ?? false;
1731
- if (isSigner && isWritable) return "AccountRole.writableSigner";
1732
- if (isSigner) return "AccountRole.readonlySigner";
1733
- if (isWritable) return "AccountRole.writable";
1734
- return "AccountRole.readonly";
1873
+ function reserveName(preferredName, usedNames) {
1874
+ let name = preferredName;
1875
+ while (usedNames.has(name)) {
1876
+ name = `${name}_`;
1877
+ }
1878
+ usedNames.add(name);
1879
+ return name;
1880
+ }
1881
+ function getAccountRole(acc, signerParameter) {
1882
+ const role = acc.isWritable ? "AccountRole.writable" : "AccountRole.readonly";
1883
+ if (signerParameter) {
1884
+ return `${signerParameter} ? ${role}Signer : ${role}`;
1885
+ }
1886
+ return acc.isSigner === true ? `${role}Signer` : role;
1735
1887
  }
1736
1888
  function isDiscriminatorArg(arg, node) {
1737
1889
  const discriminators = node.discriminators ?? [];
@@ -1784,9 +1936,9 @@ function getPdaPageFragment(node, scope) {
1784
1936
  for (const seed of seeds) {
1785
1937
  if (seed.kind === "constantPdaSeedNode") {
1786
1938
  if (seed.value.kind === "bytesValueNode") {
1787
- seedValues.push(` Uint8List.fromList(${bytesValueToDart(seed.value.data)}),`);
1939
+ seedValues.push(` ${getDartValueFragment(seed.value).content},`);
1788
1940
  } else if (seed.value.kind === "stringValueNode") {
1789
- seedValues.push(` '${seed.value.string}',`);
1941
+ seedValues.push(` ${toDartStringLiteral(seed.value.string)},`);
1790
1942
  } else if (seed.value.kind === "publicKeyValueNode") {
1791
1943
  const wellKnownSeedName = WELL_KNOWN_ADDRESSES.get(seed.value.publicKey);
1792
1944
  if (wellKnownSeedName) {
@@ -1796,13 +1948,14 @@ function getPdaPageFragment(node, scope) {
1796
1948
  );
1797
1949
  } else {
1798
1950
  seedValues.push(
1799
- ` getAddressEncoder().encode(Address('${seed.value.publicKey}')),`
1951
+ ` getAddressEncoder().encode(Address(${toDartStringLiteral(seed.value.publicKey)})),`
1800
1952
  );
1801
1953
  }
1802
1954
  } else if (seed.value.kind === "numberValueNode") {
1803
1955
  const manifest = visitorsCore.visit(seed.type, scope.typeManifestVisitor);
1804
1956
  seedManifests.push(manifest);
1805
- seedValues.push(` ${manifest.encoder.content}.encode(${seed.value.number}),`);
1957
+ const value = getDartValueFragment(seed.value, manifest.type.content);
1958
+ seedValues.push(` ${manifest.encoder.content}.encode(${value.content}),`);
1806
1959
  }
1807
1960
  } else if (seed.kind === "variablePdaSeedNode") {
1808
1961
  const manifest = visitorsCore.visit(seed.type, scope.typeManifestVisitor);
@@ -1816,7 +1969,7 @@ function getPdaPageFragment(node, scope) {
1816
1969
  if (wellKnownProgramName) {
1817
1970
  use(wellKnownProgramName, "solanaAddresses");
1818
1971
  }
1819
- const programIdParam = node.programId ? wellKnownProgramName ? `Address programAddress = ${wellKnownProgramName}` : `Address programAddress = const Address('${node.programId}')` : "required Address programAddress";
1972
+ const programIdParam = node.programId ? wellKnownProgramName ? `Address programAddress = ${wellKnownProgramName}` : `Address programAddress = const Address(${toDartStringLiteral(node.programId)})` : "required Address programAddress";
1820
1973
  const typedDataImport = hasByteSeeds ? use("Uint8List", "dartTypedData") : fragmentFromString("");
1821
1974
  const metaImport = hasSeedsClass ? use("immutable", "meta") : fragmentFromString("");
1822
1975
  const parts = [
@@ -1863,14 +2016,6 @@ ${fragmentFromString(seedValues.join("\n"))}
1863
2016
  }
1864
2017
  return result;
1865
2018
  }
1866
- function bytesValueToDart(hex) {
1867
- const clean = hex.replace(/^0x/, "");
1868
- const pairs = [];
1869
- for (let i = 0; i < clean.length; i += 2) {
1870
- pairs.push(`0x${clean.slice(i, i + 2)}`);
1871
- }
1872
- return `[${pairs.join(", ")}]`;
1873
- }
1874
2019
  function getProgramPageFragment(node, scope) {
1875
2020
  const name = node.name;
1876
2021
  const addressConstName = scope.nameApi.programAddressConstant(name);
@@ -1891,7 +2036,7 @@ const ${fragmentFromString(addressConstName)} = ${fragmentFromString(wellKnownNa
1891
2036
  ${use("Address", "solanaAddresses")}
1892
2037
 
1893
2038
  /// The address of the ${fragmentFromString(pascalCase(name))} program.
1894
- const ${fragmentFromString(addressConstName)} = Address('${fragmentFromString(node.publicKey)}');`;
2039
+ const ${fragmentFromString(addressConstName)} = Address(${fragmentFromString(toDartStringLiteral(node.publicKey))});`;
1895
2040
  const parts = [addressDeclaration];
1896
2041
  const accounts = node.accounts ?? [];
1897
2042
  if (accounts.length > 0) {
@@ -2032,7 +2177,7 @@ function getValueFragment(value, dartType) {
2032
2177
  case "booleanValueNode":
2033
2178
  return fragmentFromString(String(value.boolean));
2034
2179
  case "stringValueNode":
2035
- return fragmentFromString(`'${value.string.replaceAll("'", "\\'")}'`);
2180
+ return fragmentFromString(toDartStringLiteral(value.string));
2036
2181
  case "bytesValueNode": {
2037
2182
  const clean = value.data.replace(/^0x/, "");
2038
2183
  const bytes = clean.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)) ?? [];
@@ -2130,6 +2275,7 @@ function getDataEnumPageFragment(node, variants, scope) {
2130
2275
  const decoderVariants = [];
2131
2276
  const encodeCases = [];
2132
2277
  const decodeCases = [];
2278
+ let pageHasListEnums = false;
2133
2279
  const allVariantManifests = [];
2134
2280
  for (let i = 0; i < variants.length; i++) {
2135
2281
  const variant = variants[i];
@@ -2177,10 +2323,14 @@ function getDataEnumPageFragment(node, variants, scope) {
2177
2323
  const ctorSignature = fields.length === 0 ? ` const ${variantClassName}();` : ` const ${variantClassName}({
2178
2324
  ${ctorParams}
2179
2325
  });`;
2180
- const eqChecks = fields.length === 0 ? "true" : fields.map((f) => `${camelCase(f.name)} == other.${camelCase(f.name)}`).join(" &&\n ");
2181
- const hashExpression = getHashExpression(
2182
- fields.map((f) => camelCase(f.name))
2183
- );
2326
+ const eqChecks = fields.length === 0 ? "true" : fields.map((f, index) => {
2327
+ const fieldName = camelCase(f.name);
2328
+ return isListManifest(fieldManifests[index].manifest) ? `_listEquals(${fieldName}, other.${fieldName})` : `${fieldName} == other.${fieldName}`;
2329
+ }).join(" &&\n ");
2330
+ const hashExpression = getValueHashExpression(fields, fieldManifests);
2331
+ if (fieldManifests.some(({ manifest }) => isListManifest(manifest))) {
2332
+ pageHasListEnums = true;
2333
+ }
2184
2334
  const toStringFields = fields.map((f) => `${camelCase(f.name)}: $${camelCase(f.name)}`).join(", ");
2185
2335
  variantClasses.push(`final class ${variantClassName} extends ${typeName} {
2186
2336
  ${ctorSignature}
@@ -2286,7 +2436,7 @@ sealed class ${fragmentFromString(typeName)} {
2286
2436
  const ${fragmentFromString(typeName)}();
2287
2437
  }
2288
2438
 
2289
- ${fragmentFromString(variantClasses.join("\n\n"))}
2439
+ ${fragmentFromString(variantClasses.join("\n\n"))}${pageHasListEnums ? LIST_VALUE_HELPERS : ""}
2290
2440
 
2291
2441
  Encoder<${fragmentFromString(typeName)}> ${fragmentFromString(encoderName)}() {
2292
2442
  return transformEncoder<Map<String, Object?>, ${fragmentFromString(typeName)}>(
@@ -2329,6 +2479,7 @@ function getStructPageFragment(node, scope) {
2329
2479
  const structNode = node.type;
2330
2480
  if (structNode.kind !== "structTypeNode") return emptyFragment();
2331
2481
  const fields = structNode.fields ?? [];
2482
+ let pageHasListStructs = false;
2332
2483
  const fieldManifests = fields.map((f) => ({
2333
2484
  field: f,
2334
2485
  manifest: visitorsCore.visit(f.type, scope.typeManifestVisitor)
@@ -2340,10 +2491,13 @@ function getStructPageFragment(node, scope) {
2340
2491
  const ctorSignature = fields.length === 0 ? ` const ${typeName}();` : ` const ${typeName}({
2341
2492
  ${ctorParams}
2342
2493
  });`;
2343
- const eqChecks = fields.length === 0 ? "true" : fields.map(
2344
- (f) => `${camelCase(f.name)} == other.${camelCase(f.name)}`
2345
- ).join(" &&\n ");
2346
- fields.length === 0 ? "runtimeType" : fields.map((f) => camelCase(f.name)).join(", ");
2494
+ const eqChecks = fields.length === 0 ? "true" : fieldManifests.map(({ field: f, manifest }, index) => {
2495
+ const fieldName = camelCase(f.name);
2496
+ return isListManifest(manifest) ? `_listEquals(${fieldName}, other.${fieldName})` : `${fieldName} == other.${fieldName}`;
2497
+ }).join(" &&\n ");
2498
+ if (fieldManifests.some(({ manifest }) => isListManifest(manifest))) {
2499
+ pageHasListStructs = true;
2500
+ }
2347
2501
  const toStringFields = fields.map(
2348
2502
  (f) => `${camelCase(f.name)}: $${camelCase(f.name)}`
2349
2503
  ).join(", ");
@@ -2393,12 +2547,14 @@ ${fragmentFromString(fieldDecls)}
2393
2547
  ${fragmentFromString(eqChecks)};
2394
2548
 
2395
2549
  @override
2396
- int get hashCode => ${fragmentFromString(getHashExpression(fields.map((f) => camelCase(f.name))))};
2550
+ int get hashCode => ${fragmentFromString(
2551
+ getValueHashExpression(fields, fieldManifests)
2552
+ )};
2397
2553
 
2398
2554
  @override
2399
2555
  String toString() => '${fragmentFromString(typeName)}(${fragmentFromString(toStringFields)})';
2400
2556
  }
2401
-
2557
+ ${pageHasListStructs ? LIST_VALUE_HELPERS : ""}
2402
2558
  Encoder<${fragmentFromString(typeName)}> ${fragmentFromString(encoderName)}() {
2403
2559
  final structEncoder = getStructEncoder(<(String, Encoder<Object?>)>[
2404
2560
  ${fragmentFromString(encFields)}
@@ -2464,6 +2620,56 @@ Codec<${fragmentFromString(typeName)}, ${fragmentFromString(typeName)}> ${fragme
2464
2620
  return combineCodec(${fragmentFromString(encoderName)}(), ${fragmentFromString(decoderName)}());
2465
2621
  }`;
2466
2622
  }
2623
+ function isListManifest(manifest) {
2624
+ return manifest.type.content.startsWith("List<") || manifest.type.content === "Uint8List";
2625
+ }
2626
+ var LIST_VALUE_HELPERS = `
2627
+ bool _listEquals<T>(List<T>? a, List<T>? b) {
2628
+ if (identical(a, b)) return true;
2629
+ if (a == null || b == null) return a == b;
2630
+ if (a.length != b.length) return false;
2631
+ for (var i = 0; i < a.length; i++) {
2632
+ final left = a[i];
2633
+ final right = b[i];
2634
+ if (left is List<Object?> && right is List<Object?>) {
2635
+ if (!_listEquals(left, right)) return false;
2636
+ } else if (left != right) {
2637
+ return false;
2638
+ }
2639
+ }
2640
+ return true;
2641
+ }
2642
+
2643
+ Object? _deepHash(Object? value) {
2644
+ if (value is List<Object?>) {
2645
+ return Object.hashAll(value.map(_deepHash));
2646
+ }
2647
+ return value;
2648
+ }
2649
+
2650
+ int _listHashCode<T>(List<T>? a) {
2651
+ if (a == null) return 0;
2652
+ return Object.hashAll(a.map(_deepHash));
2653
+ }
2654
+ `;
2655
+ function getValueHashExpression(fields, fieldManifests) {
2656
+ const fieldNames = fields.map(
2657
+ (f) => camelCase(f.name)
2658
+ );
2659
+ const hasListFields = fieldManifests.some(
2660
+ ({ manifest }) => isListManifest(manifest)
2661
+ );
2662
+ if (!hasListFields) {
2663
+ return getHashExpression(fieldNames);
2664
+ }
2665
+ if (fieldNames.length === 1) {
2666
+ return `_listHashCode(${fieldNames[0]})`;
2667
+ }
2668
+ const hashArgs = fields.map(
2669
+ (f, index) => isListManifest(fieldManifests[index].manifest) ? `_listHashCode(${camelCase(f.name)})` : camelCase(f.name)
2670
+ );
2671
+ return `Object.hash(${hashArgs.join(", ")})`;
2672
+ }
2467
2673
  function getHashExpression(fields) {
2468
2674
  if (fields.length === 0) return "runtimeType.hashCode";
2469
2675
  if (fields.length === 1) return `${fields[0]}.hashCode`;
@@ -2537,9 +2743,12 @@ function getRenderMapVisitor(options = {}) {
2537
2743
  visitorsCore.visit(node.program, self),
2538
2744
  ...(node.additionalPrograms ?? []).map((p) => visitorsCore.visit(p, self))
2539
2745
  ];
2540
- const mergedProgramMap = renderersCore.mergeRenderMaps(programMaps);
2746
+ const mergedProgramMap = mergeRenderMapsWithoutCollisions(programMaps);
2541
2747
  const categoryMaps = getSharedCategoryIndexMaps(mergedProgramMap);
2542
- return renderersCore.mergeRenderMaps([mergedProgramMap, ...categoryMaps]);
2748
+ return mergeRenderMapsWithoutCollisions([
2749
+ mergedProgramMap,
2750
+ ...categoryMaps
2751
+ ]);
2543
2752
  },
2544
2753
  visitProgram(node, { self }) {
2545
2754
  const programName = node.name;
@@ -2601,7 +2810,7 @@ function getRenderMapVisitor(options = {}) {
2601
2810
  );
2602
2811
  maps.push(rootMap);
2603
2812
  }
2604
- return renderersCore.mergeRenderMaps(maps);
2813
+ return mergeRenderMapsWithoutCollisions(maps);
2605
2814
  },
2606
2815
  visitAccount(node, { self }) {
2607
2816
  const fileName = `${snakeCase(node.name)}.dart`;
@@ -2636,6 +2845,18 @@ function getRenderMapVisitor(options = {}) {
2636
2845
  (v) => visitorsCore.recordLinkablesOnFirstVisitVisitor(v, linkables)
2637
2846
  );
2638
2847
  }
2848
+ function mergeRenderMapsWithoutCollisions(maps) {
2849
+ const paths = /* @__PURE__ */ new Set();
2850
+ for (const map of maps) {
2851
+ for (const path of map.keys()) {
2852
+ if (paths.has(path)) {
2853
+ throw new Error(`Duplicate generated path "${path}"`);
2854
+ }
2855
+ paths.add(path);
2856
+ }
2857
+ }
2858
+ return renderersCore.mergeRenderMaps(maps);
2859
+ }
2639
2860
  function getSharedCategoryIndexMaps(renderMap) {
2640
2861
  const categories = /* @__PURE__ */ new Map();
2641
2862
  const categoryIndexNames = {
@@ -2652,7 +2873,6 @@ function getSharedCategoryIndexMaps(renderMap) {
2652
2873
  );
2653
2874
  if (!match) continue;
2654
2875
  const [, category, fileName] = match;
2655
- if (fileName === categoryIndexNames[category]) continue;
2656
2876
  if (!categories.has(category)) {
2657
2877
  categories.set(category, /* @__PURE__ */ new Set());
2658
2878
  }
@@ -2682,9 +2902,6 @@ function renderVisitor(outputDir, options = {}) {
2682
2902
  } = options;
2683
2903
  return visitorsCore.rootNodeVisitor((root) => {
2684
2904
  const normalizedRoot = normalizeRootNode(root);
2685
- if (deleteFolderBeforeRendering && fs.existsSync(outputDir)) {
2686
- renderersCore.deleteDirectory(outputDir);
2687
- }
2688
2905
  const renderMap = visitorsCore.visit(
2689
2906
  normalizedRoot,
2690
2907
  getRenderMapVisitor({ nameApi, dependencyMap })
@@ -2696,12 +2913,9 @@ function renderVisitor(outputDir, options = {}) {
2696
2913
  typePathMap[`definedType:${match[1]}`] = renderPath;
2697
2914
  }
2698
2915
  }
2916
+ const files = [];
2699
2917
  for (const [filePath, frag] of renderMap.entries()) {
2700
2918
  const fullPath = path.join(outputDir, filePath);
2701
- const dir = path.dirname(fullPath);
2702
- if (!fs.existsSync(dir)) {
2703
- fs.mkdirSync(dir, { recursive: true });
2704
- }
2705
2919
  const fileDir = path.dirname(filePath);
2706
2920
  const internalMap = {};
2707
2921
  for (const [key, typePath] of Object.entries(typePathMap)) {
@@ -2716,6 +2930,13 @@ function renderVisitor(outputDir, options = {}) {
2716
2930
  internalMap[key] = rel;
2717
2931
  }
2718
2932
  const content = resolveFragmentContent(frag, dependencyMap ?? {}, internalMap);
2933
+ files.push([fullPath, content]);
2934
+ }
2935
+ if (deleteFolderBeforeRendering && fs.existsSync(outputDir)) {
2936
+ renderersCore.deleteDirectory(outputDir);
2937
+ }
2938
+ for (const [fullPath, content] of files) {
2939
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
2719
2940
  fs.writeFileSync(fullPath, content, "utf-8");
2720
2941
  }
2721
2942
  if (formatCode) {