codama-renderers-dart 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/index.browser.cjs +456 -127
  2. package/dist/index.browser.cjs.map +1 -1
  3. package/dist/index.browser.js +456 -128
  4. package/dist/index.browser.js.map +1 -1
  5. package/dist/index.node.cjs +456 -127
  6. package/dist/index.node.cjs.map +1 -1
  7. package/dist/index.node.js +456 -128
  8. package/dist/index.node.js.map +1 -1
  9. package/dist/index.react-native.js +456 -128
  10. package/dist/index.react-native.js.map +1 -1
  11. package/dist/types/fragments/accountPage.d.ts.map +1 -1
  12. package/dist/types/fragments/instructionPage.d.ts.map +1 -1
  13. package/dist/types/fragments/typePage.d.ts.map +1 -1
  14. package/dist/types/index.d.ts +1 -0
  15. package/dist/types/index.d.ts.map +1 -1
  16. package/dist/types/utils/discriminators.d.ts +10 -0
  17. package/dist/types/utils/discriminators.d.ts.map +1 -0
  18. package/dist/types/utils/exactDecoder.d.ts +17 -0
  19. package/dist/types/utils/exactDecoder.d.ts.map +1 -0
  20. package/dist/types/utils/formatCode.d.ts +1 -1
  21. package/dist/types/utils/formatCode.d.ts.map +1 -1
  22. package/dist/types/utils/index.d.ts +4 -0
  23. package/dist/types/utils/index.d.ts.map +1 -1
  24. package/dist/types/utils/normalizeRootNode.d.ts +11 -0
  25. package/dist/types/utils/normalizeRootNode.d.ts.map +1 -0
  26. package/dist/types/utils/valueNodes.d.ts +16 -0
  27. package/dist/types/utils/valueNodes.d.ts.map +1 -0
  28. package/dist/types/visitors/getTypeManifestVisitor.d.ts +1 -1
  29. package/dist/types/visitors/getTypeManifestVisitor.d.ts.map +1 -1
  30. package/dist/types/visitors/renderVisitor.d.ts.map +1 -1
  31. package/package.json +21 -18
  32. package/readme.md +29 -0
  33. package/src/fragments/accountPage.ts +90 -17
  34. package/src/fragments/instructionPage.ts +103 -77
  35. package/src/fragments/typePage.ts +32 -12
  36. package/src/index.ts +1 -0
  37. package/src/utils/discriminators.ts +121 -0
  38. package/src/utils/exactDecoder.ts +82 -0
  39. package/src/utils/formatCode.ts +7 -11
  40. package/src/utils/index.ts +4 -0
  41. package/src/utils/normalizeRootNode.ts +76 -0
  42. package/src/utils/valueNodes.ts +87 -0
  43. package/src/visitors/getRenderMapVisitor.ts +1 -1
  44. package/src/visitors/getTypeManifestVisitor.ts +14 -8
  45. package/src/visitors/renderVisitor.ts +4 -1
@@ -8,13 +8,71 @@ import { CodamaError, CODAMA_ERROR__UNEXPECTED_NODE_KIND } from '@codama/errors'
8
8
 
9
9
  // src/visitors/renderVisitor.ts
10
10
  function formatDartDirectory(dir) {
11
- try {
12
- execSync(`dart format --fix "${dir}"`, {
13
- encoding: "utf-8",
14
- timeout: 6e4,
15
- stdio: ["pipe", "pipe", "pipe"]
16
- });
17
- } catch {
11
+ execSync(`dart format "${dir}"`, {
12
+ encoding: "utf-8",
13
+ timeout: 6e4,
14
+ stdio: ["pipe", "pipe", "pipe"]
15
+ });
16
+ }
17
+
18
+ // src/utils/normalizeRootNode.ts
19
+ function normalizeRootNode(root) {
20
+ return normalizeNode(root);
21
+ }
22
+ function normalizeNode(value) {
23
+ if (Array.isArray(value)) {
24
+ return value.map(normalizeNode);
25
+ }
26
+ if (!isRecord(value)) {
27
+ return value;
28
+ }
29
+ const normalized = Object.fromEntries(
30
+ Object.entries(value).map(([key, child]) => [key, normalizeNode(child)])
31
+ );
32
+ switch (normalized.kind) {
33
+ case "rootNode":
34
+ setEmptyCollection(normalized, "additionalPrograms");
35
+ break;
36
+ case "programNode":
37
+ setEmptyCollection(normalized, "accounts");
38
+ setEmptyCollection(normalized, "constants");
39
+ setEmptyCollection(normalized, "definedTypes");
40
+ setEmptyCollection(normalized, "errors");
41
+ setEmptyCollection(normalized, "events");
42
+ setEmptyCollection(normalized, "instructions");
43
+ setEmptyCollection(normalized, "pdas");
44
+ break;
45
+ case "instructionNode":
46
+ setEmptyCollection(normalized, "accounts");
47
+ setEmptyCollection(normalized, "arguments");
48
+ break;
49
+ case "pdaNode":
50
+ setEmptyCollection(normalized, "seeds");
51
+ break;
52
+ case "structTypeNode":
53
+ setEmptyCollection(normalized, "fields");
54
+ break;
55
+ case "enumTypeNode":
56
+ setEmptyCollection(normalized, "variants");
57
+ break;
58
+ case "tupleTypeNode":
59
+ setEmptyCollection(normalized, "items");
60
+ break;
61
+ case "hiddenPrefixTypeNode":
62
+ setEmptyCollection(normalized, "prefix");
63
+ break;
64
+ case "hiddenSuffixTypeNode":
65
+ setEmptyCollection(normalized, "suffix");
66
+ break;
67
+ }
68
+ return normalized;
69
+ }
70
+ function isRecord(value) {
71
+ return typeof value === "object" && value !== null;
72
+ }
73
+ function setEmptyCollection(record, key) {
74
+ if (record[key] === void 0) {
75
+ record[key] = [];
18
76
  }
19
77
  }
20
78
 
@@ -204,17 +262,6 @@ function use(typeName, module) {
204
262
  return { content: typeName, imports };
205
263
  }
206
264
 
207
- // src/utils/typeManifest.ts
208
- function emptyTypeManifest() {
209
- return {
210
- type: emptyFragment(),
211
- encoder: emptyFragment(),
212
- decoder: emptyFragment(),
213
- value: emptyFragment(),
214
- isEnum: false
215
- };
216
- }
217
-
218
265
  // src/utils/wellKnownAddresses.ts
219
266
  var WELL_KNOWN_ADDRESSES = /* @__PURE__ */ new Map([
220
267
  // Agave / Solana native programs
@@ -269,6 +316,207 @@ var WELL_KNOWN_ADDRESSES = /* @__PURE__ */ new Map([
269
316
  ["Es9vMFrzaCERm6YKXx6bqKr1Rmd9b5v3J8rM5e4rfF4", "usdtAddress"]
270
317
  ]);
271
318
 
319
+ // src/utils/valueNodes.ts
320
+ function getDartValueFragment(value, dartType) {
321
+ switch (value.kind) {
322
+ case "numberValueNode":
323
+ return fragmentFromString(
324
+ dartType === "BigInt" ? `BigInt.from(${value.number})` : String(value.number)
325
+ );
326
+ case "booleanValueNode":
327
+ return fragmentFromString(String(value.boolean));
328
+ case "stringValueNode":
329
+ return fragmentFromString(toDartStringLiteral(value.string));
330
+ case "noneValueNode":
331
+ return fragmentFromString("null");
332
+ case "bytesValueNode": {
333
+ if (value.encoding !== "base16") {
334
+ throw new Error(
335
+ `Unsupported deterministic bytes encoding: ${value.encoding}`
336
+ );
337
+ }
338
+ const clean = value.data.replace(/^0x/, "");
339
+ if (clean.length % 2 !== 0 || !/^[0-9a-f]*$/i.test(clean)) {
340
+ throw new Error(`Invalid hexadecimal bytes value: ${value.data}`);
341
+ }
342
+ const bytes = clean.match(/.{2}/g)?.map((byte) => `0x${byte}`) ?? [];
343
+ return fragment`${use("Uint8List", "dartTypedData")}.fromList([${fragmentFromString(bytes.join(", "))}])`;
344
+ }
345
+ case "publicKeyValueNode": {
346
+ const wellKnownName = WELL_KNOWN_ADDRESSES.get(value.publicKey);
347
+ return wellKnownName ? fragment`${use(wellKnownName, "solanaAddresses")}` : fragment`${use("Address", "solanaAddresses")}(${fragmentFromString(toDartStringLiteral(value.publicKey))})`;
348
+ }
349
+ default:
350
+ throw new Error(
351
+ `Unsupported deterministic Dart value node: ${value.kind}`
352
+ );
353
+ }
354
+ }
355
+ function isConstDartValueNode(value) {
356
+ switch (value.kind) {
357
+ case "numberValueNode":
358
+ case "booleanValueNode":
359
+ case "stringValueNode":
360
+ case "noneValueNode":
361
+ case "publicKeyValueNode":
362
+ return true;
363
+ default:
364
+ return false;
365
+ }
366
+ }
367
+ function toDartStringLiteral(value) {
368
+ return `'${value.replaceAll("\\", "\\\\").replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t").replaceAll("\b", "\\b").replaceAll("\f", "\\f").replaceAll("$", "\\$").replaceAll("'", "\\'")}'`;
369
+ }
370
+
371
+ // src/utils/discriminators.ts
372
+ function getDiscriminatorValidationFragment(node, scope) {
373
+ const discriminators = node.discriminators ?? [];
374
+ if (discriminators.length === 0) return emptyFragment();
375
+ const fields = getDiscriminatorFields(node);
376
+ const fragments = discriminators.map((discriminator) => {
377
+ switch (discriminator.kind) {
378
+ case "constantDiscriminatorNode":
379
+ return getConstantValidationFragment(
380
+ discriminator.constant,
381
+ discriminator.offset,
382
+ scope
383
+ );
384
+ case "fieldDiscriminatorNode": {
385
+ const field = fields.find(
386
+ (candidate) => candidate.name === discriminator.name
387
+ );
388
+ if (field?.defaultValue == null) {
389
+ throw new Error(
390
+ `Field discriminator "${discriminator.name}" on ${node.kind} "${node.name}" must reference a field with a default value.`
391
+ );
392
+ }
393
+ return getConstantValidationFragment(
394
+ {
395
+ type: field.type,
396
+ value: field.defaultValue
397
+ },
398
+ discriminator.offset,
399
+ scope
400
+ );
401
+ }
402
+ case "sizeDiscriminatorNode": {
403
+ if (!Number.isSafeInteger(discriminator.size) || discriminator.size < 0) {
404
+ throw new Error(
405
+ `Discriminator size must be a non-negative safe integer, got ${discriminator.size}.`
406
+ );
407
+ }
408
+ return fragment`if (bytes.length - offset != ${fragmentFromString(String(discriminator.size))}) {
409
+ throw ${use("SolanaError", "solanaErrors")}(
410
+ ${use("SolanaErrorCode", "solanaErrors")}.codecsInvalidByteLength,
411
+ {
412
+ 'codecDescription': '${fragmentFromString(node.name)} discriminator',
413
+ 'expected': ${fragmentFromString(String(discriminator.size))},
414
+ 'bytesLength': bytes.length - offset,
415
+ },
416
+ );
417
+ }`;
418
+ }
419
+ }
420
+ });
421
+ return mergeFragments(fragments, (contents) => contents.join("\n"));
422
+ }
423
+ function getConstantValidationFragment(constant, discriminatorOffset, scope) {
424
+ if (!Number.isSafeInteger(discriminatorOffset) || discriminatorOffset < 0) {
425
+ throw new Error(
426
+ `Discriminator offset must be a non-negative safe integer, got ${discriminatorOffset}.`
427
+ );
428
+ }
429
+ const manifest = visit(constant.type, scope.typeManifestVisitor);
430
+ const value = getDartValueFragment(
431
+ constant.value,
432
+ manifest.type.content
433
+ );
434
+ return fragment`${use("getConstantDecoder", "solanaCodecsDataStructures")}(
435
+ ${manifest.encoder}.encode(${value}),
436
+ ).read(bytes, offset + ${fragmentFromString(String(discriminatorOffset))});`;
437
+ }
438
+ function getDiscriminatorFields(node) {
439
+ if (node.kind === "accountNode") {
440
+ return resolveNestedTypeNode(node.data).fields ?? [];
441
+ }
442
+ return (node.arguments ?? []).map(
443
+ (argument) => ({
444
+ ...argument,
445
+ kind: "structFieldTypeNode"
446
+ })
447
+ );
448
+ }
449
+
450
+ // src/utils/exactDecoder.ts
451
+ function getTopLevelDecoderFragment({
452
+ typeName,
453
+ description,
454
+ discriminatorValidation,
455
+ fromMapFields,
456
+ requireExactConsumption
457
+ }) {
458
+ const validation = discriminatorValidation.content ? ` ${discriminatorValidation.content.replaceAll("\n", "\n ")}
459
+ ` : "";
460
+ const consumptionCheck = requireExactConsumption ? ` if (newOffset != bytes.length) {
461
+ throwInvalidByteLength(newOffset - offset, bytes.length - offset);
462
+ }
463
+ ` : "";
464
+ const fixedSizeCheck = requireExactConsumption ? "bytesLength != structDecoder.fixedSize" : "bytesLength < structDecoder.fixedSize";
465
+ const result = fragment` Never throwInvalidByteLength(int expected, int bytesLength) {
466
+ throw ${use("SolanaError", "solanaErrors")}(
467
+ ${use("SolanaErrorCode", "solanaErrors")}.codecsInvalidByteLength,
468
+ {
469
+ 'codecDescription': '${fragmentFromString(description)}',
470
+ 'expected': expected,
471
+ 'bytesLength': bytesLength,
472
+ },
473
+ );
474
+ }
475
+
476
+ (${fragmentFromString(typeName)}, int) readTopLevel(Uint8List bytes, int offset) {
477
+ ${fragmentFromString(validation)} final (map, newOffset) = structDecoder.read(bytes, offset);
478
+ ${fragmentFromString(consumptionCheck)}
479
+ return (
480
+ ${fragmentFromString(typeName)}(
481
+ ${fragmentFromString(fromMapFields)}
482
+ ),
483
+ newOffset,
484
+ );
485
+ }
486
+
487
+ return switch (structDecoder) {
488
+ ${use("FixedSizeDecoder", "solanaCodecsCore")}<Map<String, Object?>>() =>
489
+ ${use("FixedSizeDecoder", "solanaCodecsCore")}<${fragmentFromString(typeName)}>(
490
+ fixedSize: structDecoder.fixedSize,
491
+ read: (bytes, offset) {
492
+ final bytesLength = bytes.length - offset;
493
+ if (${fragmentFromString(fixedSizeCheck)}) {
494
+ throwInvalidByteLength(structDecoder.fixedSize, bytesLength);
495
+ }
496
+ return readTopLevel(bytes, offset);
497
+ },
498
+ ),
499
+ ${use("VariableSizeDecoder", "solanaCodecsCore")}<Map<String, Object?>>() =>
500
+ ${use("VariableSizeDecoder", "solanaCodecsCore")}<${fragmentFromString(typeName)}>(
501
+ read: readTopLevel,
502
+ maxSize: structDecoder.maxSize,
503
+ ),
504
+ };`;
505
+ result.imports.mergeWith(discriminatorValidation.imports);
506
+ return result;
507
+ }
508
+
509
+ // src/utils/typeManifest.ts
510
+ function emptyTypeManifest() {
511
+ return {
512
+ type: emptyFragment(),
513
+ encoder: emptyFragment(),
514
+ decoder: emptyFragment(),
515
+ value: emptyFragment(),
516
+ isEnum: false
517
+ };
518
+ }
519
+
272
520
  // src/visitors/getTypeManifestVisitor.ts
273
521
  function getTypeManifestVisitor(input) {
274
522
  const { nameApi, linkables, nonScalarEnums = [] } = input;
@@ -412,12 +660,17 @@ function getTypeManifestVisitor(input) {
412
660
  const itemManifest = visit(node.item, self);
413
661
  const encoderSizeExpr = getArraySizeExpression(node, "Encoder");
414
662
  const decoderSizeExpr = getArraySizeExpression(node, "Decoder");
663
+ const itemType = fragmentFromString(itemManifest.type.content);
664
+ const itemEncoder = fragment`${use(
665
+ "transformEncoder",
666
+ "solanaCodecsCore"
667
+ )}(${itemManifest.encoder}, (${itemType} value) => value)`;
415
668
  return {
416
669
  type: fragment`List<${itemManifest.type}>`,
417
670
  encoder: fragment`${use(
418
671
  "getArrayEncoder",
419
672
  "solanaCodecsDataStructures"
420
- )}(${itemManifest.encoder}${encoderSizeExpr})`,
673
+ )}<${itemType}>(${itemEncoder}${encoderSizeExpr})`,
421
674
  decoder: fragment`${use(
422
675
  "getArrayDecoder",
423
676
  "solanaCodecsDataStructures"
@@ -464,7 +717,7 @@ function getTypeManifestVisitor(input) {
464
717
  };
465
718
  },
466
719
  visitTupleType(node, { self }) {
467
- const itemManifests = node.items.map((item) => visit(item, self));
720
+ const itemManifests = (node.items ?? []).map((item) => visit(item, self));
468
721
  const typeList = itemManifests.map((m) => m.type.content).join(", ");
469
722
  const encoderList = itemManifests.map((m) => m.encoder);
470
723
  const decoderList = itemManifests.map((m) => m.decoder);
@@ -497,8 +750,9 @@ function getTypeManifestVisitor(input) {
497
750
  };
498
751
  },
499
752
  visitStructType(node, { self }) {
500
- const fields = node.fields.map((f) => visit(f, self));
501
- const fieldNames = node.fields.map((f) => f.name);
753
+ const nodeFields = node.fields ?? [];
754
+ const fields = nodeFields.map((f) => visit(f, self));
755
+ const fieldNames = nodeFields.map((f) => f.name);
502
756
  const typeStr = "Map<String, Object?>";
503
757
  const encoderFields = fields.map((f, i) => `('${fieldNames[i]}', ${f.encoder.content})`).join(", ");
504
758
  const encoderFrag = fragment`${use(
@@ -528,7 +782,7 @@ function getTypeManifestVisitor(input) {
528
782
  return visit(node.type, self);
529
783
  },
530
784
  visitEnumType(node, { self }) {
531
- const isScalar = node.variants.every(
785
+ const isScalar = (node.variants ?? []).every(
532
786
  (v) => v.kind === "enumEmptyVariantTypeNode"
533
787
  );
534
788
  if (isScalar) {
@@ -620,7 +874,7 @@ function getTypeManifestVisitor(input) {
620
874
  encoder: fragment`${use(
621
875
  "fixEncoderSize",
622
876
  "solanaCodecsCore"
623
- )}(${innerManifest.encoder}, ${fragmentFromString(String(node.size))})`,
877
+ )}(${innerManifest.encoder}, ${fragmentFromString(String(node.size))}, allowTruncation: false)`,
624
878
  decoder: fragment`${use(
625
879
  "fixDecoderSize",
626
880
  "solanaCodecsCore"
@@ -658,7 +912,7 @@ function getTypeManifestVisitor(input) {
658
912
  },
659
913
  visitHiddenPrefixType(node, { self }) {
660
914
  const innerManifest = visit(node.type, self);
661
- const prefixes = node.prefix.map((p) => getHiddenAffixManifest(p, self));
915
+ const prefixes = (node.prefix ?? []).map((p) => getHiddenAffixManifest(p, self));
662
916
  const prefixEncoders = prefixes.map((p) => p.encoder.content).join(", ");
663
917
  const prefixDecoders = prefixes.map((p) => p.decoder.content).join(", ");
664
918
  const encoderFrag = fragment`${use(
@@ -685,7 +939,7 @@ function getTypeManifestVisitor(input) {
685
939
  },
686
940
  visitHiddenSuffixType(node, { self }) {
687
941
  const innerManifest = visit(node.type, self);
688
- const suffixes = node.suffix.map((s) => getHiddenAffixManifest(s, self));
942
+ const suffixes = (node.suffix ?? []).map((s) => getHiddenAffixManifest(s, self));
689
943
  const suffixEncoders = suffixes.map((s) => s.encoder.content).join(", ");
690
944
  const suffixDecoders = suffixes.map((s) => s.decoder.content).join(", ");
691
945
  const encoderFrag = fragment`${use(
@@ -972,19 +1226,52 @@ function hexToBytes(hex) {
972
1226
  function getAccountPageFragment(node, scope) {
973
1227
  const name = node.name;
974
1228
  const typeName = scope.nameApi.dataType(name);
975
- const dataNode = resolveNestedTypeNode(node.data);
976
- const fields = dataNode.fields;
1229
+ const fields = resolveNestedTypeNode(node.data).fields ?? [];
977
1230
  const fieldManifests = fields.map((f) => ({
978
1231
  field: f,
979
1232
  manifest: visit(f.type, scope.typeManifestVisitor)
980
1233
  }));
1234
+ const omittedFields = fields.filter(isOmittedDefaultField);
1235
+ const constructorFields = fields.filter(
1236
+ (field) => !isOmittedDefaultField(field)
1237
+ );
1238
+ const forcedFields = /* @__PURE__ */ new Set([
1239
+ ...omittedFields,
1240
+ ...fields.filter((field) => isFieldDiscriminator(field, node))
1241
+ ]);
1242
+ const fieldDefaults = new Map(
1243
+ fieldManifests.flatMap(({ field, manifest }) => {
1244
+ if (!forcedFields.has(field) || field.defaultValue == null) return [];
1245
+ return [
1246
+ [
1247
+ field,
1248
+ getDartValueFragment(field.defaultValue, manifest.type.content)
1249
+ ]
1250
+ ];
1251
+ })
1252
+ );
1253
+ for (const field of forcedFields) {
1254
+ if (!fieldDefaults.has(field)) {
1255
+ throw new Error(
1256
+ `Forced field "${field.name}" on account "${node.name}" must have a deterministic default value.`
1257
+ );
1258
+ }
1259
+ }
981
1260
  const fieldDecls = fieldManifests.map(({ field: f, manifest }) => {
982
1261
  return ` final ${manifest.type.content} ${camelCase(f.name)};`;
983
1262
  }).join("\n");
984
- const ctorParams = fields.map((f) => ` required this.${camelCase(f.name)},`).join("\n");
985
- const ctorSignature = fields.length === 0 ? `const ${typeName}();` : `const ${typeName}({
1263
+ const ctorParams = constructorFields.map((f) => ` required this.${camelCase(f.name)},`).join("\n");
1264
+ const ctorInitializers = omittedFields.map((field) => {
1265
+ const fieldName = camelCase(field.name);
1266
+ return `${fieldName} = ${fieldDefaults.get(field).content}`;
1267
+ }).join(",\n ");
1268
+ const ctorKeyword = omittedFields.every(
1269
+ (field) => isConstDartValueNode(field.defaultValue)
1270
+ ) ? "const " : "";
1271
+ const ctorSignature = constructorFields.length === 0 ? `${ctorKeyword}${typeName}()${ctorInitializers ? ` : ${ctorInitializers}` : ""};` : `${ctorKeyword}${typeName}({
986
1272
  ${ctorParams}
987
- });`;
1273
+ })${ctorInitializers ? ` :
1274
+ ${ctorInitializers}` : ""};`;
988
1275
  const eqChecks = fields.length === 0 ? "true" : fields.map(
989
1276
  (f) => `${camelCase(f.name)} == other.${camelCase(f.name)}`
990
1277
  ).join(" &&\n ");
@@ -999,10 +1286,11 @@ ${ctorParams}
999
1286
  const decFields = fieldManifests.map(({ field: f, manifest }) => {
1000
1287
  return ` ('${f.name}', ${manifest.decoder.content}),`;
1001
1288
  }).join("\n");
1002
- const toMapFields = fields.map(
1003
- (f) => ` '${f.name}': value.${camelCase(f.name)},`
1004
- ).join("\n");
1005
- const fromMapFields = fieldManifests.map(({ field: f, manifest }) => {
1289
+ const toMapFields = fields.map((f) => {
1290
+ const value = forcedFields.has(f) ? fieldDefaults.get(f).content : `value.${camelCase(f.name)}`;
1291
+ return ` '${f.name}': ${value},`;
1292
+ }).join("\n");
1293
+ const fromMapFields = fieldManifests.filter(({ field }) => !isOmittedDefaultField(field)).map(({ field: f, manifest }) => {
1006
1294
  const typeStr = manifest.type.content;
1007
1295
  const isNullable = typeStr.endsWith("?");
1008
1296
  const accessor = isNullable ? `map['${f.name}']` : `map['${f.name}']!`;
@@ -1016,6 +1304,19 @@ ${ctorParams}
1016
1304
  /// The size of the [${fragmentFromString(typeName)}] account data in bytes.
1017
1305
  const int ${fragmentFromString(scope.nameApi.accountSizeConstant(name))} = ${fragmentFromString(String(node.size))};` : emptyFragment();
1018
1306
  const discFragment = getDiscriminatorConstantsFragment(node, scope);
1307
+ const discriminatorValidation = getDiscriminatorValidationFragment(
1308
+ node,
1309
+ scope
1310
+ );
1311
+ const topLevelDecoder = getTopLevelDecoderFragment({
1312
+ typeName,
1313
+ description: `${name} account decoder`,
1314
+ discriminatorValidation,
1315
+ fromMapFields,
1316
+ requireExactConsumption: (node.discriminators ?? []).some(
1317
+ (discriminator) => discriminator.kind === "sizeDiscriminatorNode"
1318
+ )
1319
+ });
1019
1320
  const parts = [
1020
1321
  fragment`// Auto-generated. Do not edit.
1021
1322
  // ignore_for_file: type=lint
@@ -1075,12 +1376,7 @@ Decoder<${fragmentFromString(typeName)}> ${fragmentFromString(decoderName)}() {
1075
1376
  ${fragmentFromString(decFields)}
1076
1377
  ]);
1077
1378
 
1078
- return transformDecoder(
1079
- structDecoder,
1080
- (Map<String, Object?> map, Uint8List bytes, int offset) => ${fragmentFromString(typeName)}(
1081
- ${fragmentFromString(fromMapFields)}
1082
- ),
1083
- );
1379
+ ${topLevelDecoder}
1084
1380
  }
1085
1381
 
1086
1382
  Codec<${fragmentFromString(typeName)}, ${fragmentFromString(typeName)}> ${fragmentFromString(codecName)}() {
@@ -1096,8 +1392,19 @@ Account<${fragmentFromString(typeName)}> ${fragmentFromString(decodeFnName)}(Enc
1096
1392
  result.imports.mergeWith(manifest.decoder.imports);
1097
1393
  result.imports.mergeWith(manifest.type.imports);
1098
1394
  }
1395
+ for (const defaultValue of fieldDefaults.values()) {
1396
+ result.imports.mergeWith(defaultValue.imports);
1397
+ }
1099
1398
  return result;
1100
1399
  }
1400
+ function isOmittedDefaultField(field) {
1401
+ return field.defaultValue != null && field.defaultValueStrategy === "omitted";
1402
+ }
1403
+ function isFieldDiscriminator(field, node) {
1404
+ return (node.discriminators ?? []).some(
1405
+ (discriminator) => discriminator.kind === "fieldDiscriminatorNode" && discriminator.name === field.name
1406
+ );
1407
+ }
1101
1408
 
1102
1409
  // src/fragments/errorPage.ts
1103
1410
  function getErrorPageFragment(programNode, scope) {
@@ -1158,7 +1465,7 @@ function getIndexPageFragment(fileNames, relativePath = "") {
1158
1465
  "// Auto-generated. Do not edit.\n// ignore_for_file: type=lint\n"
1159
1466
  );
1160
1467
  }
1161
- const exports$1 = fileNames.sort().map((f) => {
1468
+ const exports = fileNames.sort().map((f) => {
1162
1469
  const path = relativePath ? `${relativePath}/${f}` : f;
1163
1470
  return `export '${path}';`;
1164
1471
  }).join("\n");
@@ -1166,17 +1473,17 @@ function getIndexPageFragment(fileNames, relativePath = "") {
1166
1473
  `// Auto-generated. Do not edit.
1167
1474
  // ignore_for_file: type=lint
1168
1475
 
1169
- ${exports$1}
1476
+ ${exports}
1170
1477
  `
1171
1478
  );
1172
1479
  }
1173
1480
  function getRootIndexPageFragment(categories) {
1174
- const exports$1 = categories.sort().map((cat) => `export '${cat}/${cat}.dart';`).join("\n");
1481
+ const exports = categories.sort().map((cat) => `export '${cat}/${cat}.dart';`).join("\n");
1175
1482
  return fragmentFromString(
1176
1483
  `// Auto-generated. Do not edit.
1177
1484
  // ignore_for_file: type=lint
1178
1485
 
1179
- ${exports$1}
1486
+ ${exports}
1180
1487
  `
1181
1488
  );
1182
1489
  }
@@ -1186,30 +1493,62 @@ function getInstructionPageFragment(node, scope) {
1186
1493
  const instrFnName = scope.nameApi.instructionFunction(name);
1187
1494
  const parseFnName = scope.nameApi.instructionParseFunction(name);
1188
1495
  const accounts = node.accounts ?? [];
1189
- const args = (node.arguments ?? []).filter(
1190
- (arg) => !isDiscriminatorArg(arg, node)
1191
- );
1192
1496
  const allArgs = node.arguments ?? [];
1497
+ const optionalAccountStrategy = node.optionalAccountStrategy ?? "programId";
1498
+ if (optionalAccountStrategy !== "programId" && optionalAccountStrategy !== "omitted") {
1499
+ throw new Error(
1500
+ `Unsupported optional account strategy on instruction "${node.name}": ${optionalAccountStrategy}`
1501
+ );
1502
+ }
1503
+ const args = allArgs.filter(
1504
+ (arg) => !isDiscriminatorArg(arg, node) && !isOmittedDefaultArgument(arg)
1505
+ );
1193
1506
  const dataClassName = `${typeName}InstructionData`;
1194
- const allArgManifests = allArgs.map((arg) => ({
1507
+ const allArgManifests = (node.arguments ?? []).map((arg) => ({
1195
1508
  arg,
1196
1509
  manifest: visit(arg.type, scope.typeManifestVisitor)
1197
1510
  }));
1511
+ const argDefaultValues = new Map(
1512
+ allArgManifests.flatMap(({ arg, manifest }) => {
1513
+ if (arg.defaultValue == null) return [];
1514
+ const needsRenderedDefault = isOmittedDefaultArgument(arg) || isDiscriminatorArg(arg, node) || arg.defaultValue.kind !== "accountBumpValueNode";
1515
+ if (!needsRenderedDefault) return [];
1516
+ return [
1517
+ [
1518
+ arg,
1519
+ getDartValueFragment(arg.defaultValue, manifest.type.content)
1520
+ ]
1521
+ ];
1522
+ })
1523
+ );
1524
+ for (const arg of allArgs.filter(
1525
+ (candidate) => isDiscriminatorArg(candidate, node)
1526
+ )) {
1527
+ if (!argDefaultValues.has(arg)) {
1528
+ throw new Error(
1529
+ `Field discriminator "${arg.name}" on instruction "${node.name}" must reference a field with a default value.`
1530
+ );
1531
+ }
1532
+ }
1198
1533
  const dataFieldDecls = allArgManifests.map(({ arg, manifest }) => {
1199
1534
  const fieldName = camelCase(arg.name);
1200
1535
  return ` final ${manifest.type.content} ${fieldName};`;
1201
1536
  }).join("\n");
1202
1537
  const dataCtorParams = allArgManifests.map(({ arg, manifest }) => {
1203
1538
  const fieldName = camelCase(arg.name);
1539
+ if (isOmittedDefaultArgument(arg)) return "";
1204
1540
  if (isDiscriminatorArg(arg, node)) {
1205
- return isConstDefaultValue(arg.defaultValue) ? ` this.${fieldName} = ${getDiscriminatorDefault(arg)},` : ` ${manifest.type.content}? ${fieldName},`;
1541
+ return isConstDefaultValue(arg.defaultValue) ? ` this.${fieldName} = ${argDefaultValues.get(arg).content},` : ` ${manifest.type.content}? ${fieldName},`;
1206
1542
  }
1207
1543
  return ` required this.${fieldName},`;
1208
- }).join("\n");
1544
+ }).filter(Boolean).join("\n");
1209
1545
  const dataCtorInitializers = allArgManifests.map(({ arg }) => {
1210
1546
  const fieldName = camelCase(arg.name);
1547
+ if (isOmittedDefaultArgument(arg)) {
1548
+ return ` ${fieldName} = ${argDefaultValues.get(arg).content}`;
1549
+ }
1211
1550
  if (isDiscriminatorArg(arg, node) && !isConstDefaultValue(arg.defaultValue)) {
1212
- return ` ${fieldName} = ${fieldName} ?? ${getDiscriminatorDefault(arg)}`;
1551
+ return ` ${fieldName} = ${fieldName} ?? ${argDefaultValues.get(arg).content}`;
1213
1552
  }
1214
1553
  return null;
1215
1554
  }).filter((value) => value !== null).join(",\n");
@@ -1219,16 +1558,26 @@ function getInstructionPageFragment(node, scope) {
1219
1558
  const decFields = allArgManifests.map(({ arg, manifest }) => {
1220
1559
  return ` ('${arg.name}', ${manifest.decoder.content}),`;
1221
1560
  }).join("\n");
1222
- const toMapFields = allArgs.map(
1223
- (arg) => ` '${arg.name}': value.${camelCase(arg.name)},`
1224
- ).join("\n");
1225
- const fromMapFields = allArgManifests.map(({ arg, manifest }) => {
1561
+ const toMapFields = allArgs.map((arg) => {
1562
+ const value = isOmittedDefaultArgument(arg) || isDiscriminatorArg(arg, node) ? argDefaultValues.get(arg).content : `value.${camelCase(arg.name)}`;
1563
+ return ` '${arg.name}': ${value},`;
1564
+ }).join("\n");
1565
+ const fromMapFields = allArgManifests.filter(({ arg }) => !isOmittedDefaultArgument(arg)).map(({ arg, manifest }) => {
1226
1566
  const typeStr = manifest.type.content;
1227
1567
  const isNullable = typeStr.endsWith("?");
1228
1568
  const accessor = isNullable ? `map['${arg.name}']` : `map['${arg.name}']!`;
1229
1569
  return ` ${camelCase(arg.name)}: ${accessor} as ${typeStr},`;
1230
1570
  }).join("\n");
1231
- const dataCtorKeyword = dataCtorInitializers ? "" : "const ";
1571
+ const dataCtorKeyword = allArgs.every(
1572
+ (arg) => !isOmittedDefaultArgument(arg) || isConstDartValueNode(arg.defaultValue)
1573
+ ) && allArgs.every(
1574
+ (arg) => !isDiscriminatorArg(arg, node) || isConstDefaultValue(arg.defaultValue)
1575
+ ) ? "const " : "";
1576
+ const dataCtorSignature = dataCtorParams ? `${dataCtorKeyword}${dataClassName}({
1577
+ ${dataCtorParams}
1578
+ })${dataCtorInitializers ? ` :
1579
+ ${dataCtorInitializers}` : ""};` : `${dataCtorKeyword}${dataClassName}()${dataCtorInitializers ? ` :
1580
+ ${dataCtorInitializers}` : ""};`;
1232
1581
  const dataEncoderName = `get${typeName}InstructionDataEncoder`;
1233
1582
  const dataDecoderName = `get${typeName}InstructionDataDecoder`;
1234
1583
  const dataCodecName = `get${typeName}InstructionDataCodec`;
@@ -1258,20 +1607,33 @@ function getInstructionPageFragment(node, scope) {
1258
1607
  const role = getAccountRole(acc);
1259
1608
  const isOptional = acc.isOptional ?? false;
1260
1609
  if (isOptional) {
1261
- return ` if (${fieldName} != null) AccountMeta(address: ${fieldName}, role: ${role}),`;
1610
+ if (optionalAccountStrategy === "omitted") {
1611
+ return ` if (${fieldName} != null) AccountMeta(address: ${fieldName}, role: ${role}),`;
1612
+ }
1613
+ return ` if (${fieldName} != null) AccountMeta(address: ${fieldName}, role: ${role}) else AccountMeta(address: ${instrProgramParam}, role: AccountRole.readonly),`;
1262
1614
  }
1263
1615
  return ` AccountMeta(address: ${fieldName}, role: ${role}),`;
1264
1616
  }).join("\n");
1265
1617
  const dataConstruction = allArgs.map((arg) => {
1266
1618
  const fieldName = camelCase(arg.name);
1267
- if (isDiscriminatorArg(arg, node)) {
1619
+ if (isOmittedDefaultArgument(arg) || isDiscriminatorArg(arg, node)) {
1268
1620
  return "";
1269
1621
  }
1270
- const typeStr2 = argManifestMap.get(arg)?.type.content;
1271
1622
  const skipDefault = arg.defaultValue?.kind === "accountBumpValueNode";
1272
- return ` ${fieldName}: ${fieldName}${arg.defaultValue != null && !skipDefault ? ` ?? ${getDefaultValue(arg, typeStr2)}` : ""},`;
1623
+ return ` ${fieldName}: ${fieldName}${arg.defaultValue != null && !skipDefault ? ` ?? ${argDefaultValues.get(arg).content}` : ""},`;
1273
1624
  }).filter(Boolean).join("\n");
1274
1625
  const discFragment = getDiscriminatorConstantsFragment(node, scope);
1626
+ const discriminatorValidation = getDiscriminatorValidationFragment(
1627
+ node,
1628
+ scope
1629
+ );
1630
+ const topLevelDecoder = getTopLevelDecoderFragment({
1631
+ typeName: dataClassName,
1632
+ description: `${name} instruction decoder`,
1633
+ discriminatorValidation,
1634
+ fromMapFields,
1635
+ requireExactConsumption: true
1636
+ });
1275
1637
  const parts = [
1276
1638
  fragment`// Auto-generated. Do not edit.
1277
1639
  // ignore_for_file: type=lint
@@ -1295,10 +1657,7 @@ ${use("AccountRole", "solanaInstructions")}`
1295
1657
  parts.push(fragment`
1296
1658
  @immutable
1297
1659
  class ${fragmentFromString(dataClassName)} {
1298
- ${fragmentFromString(dataCtorKeyword)}${fragmentFromString(dataClassName)}({
1299
- ${fragmentFromString(dataCtorParams)}
1300
- })${fragmentFromString(dataCtorInitializers ? ` :
1301
- ${dataCtorInitializers}` : "")};
1660
+ ${fragmentFromString(dataCtorSignature)}
1302
1661
 
1303
1662
  ${fragmentFromString(dataFieldDecls)}
1304
1663
  }`);
@@ -1321,12 +1680,7 @@ Decoder<${fragmentFromString(dataClassName)}> ${fragmentFromString(dataDecoderNa
1321
1680
  ${fragmentFromString(decFields)}
1322
1681
  ]);
1323
1682
 
1324
- return transformDecoder(
1325
- structDecoder,
1326
- (Map<String, Object?> map, Uint8List bytes, int offset) => ${fragmentFromString(dataClassName)}(
1327
- ${fragmentFromString(fromMapFields)}
1328
- ),
1329
- );
1683
+ ${topLevelDecoder}
1330
1684
  }
1331
1685
 
1332
1686
  Codec<${fragmentFromString(dataClassName)}, ${fragmentFromString(dataClassName)}> ${fragmentFromString(dataCodecName)}() {
@@ -1362,6 +1716,9 @@ ${fragmentFromString(dataClassName)} ${fragmentFromString(parseFnName)}(Instruct
1362
1716
  result.imports.mergeWith(manifest.decoder.imports);
1363
1717
  result.imports.mergeWith(manifest.type.imports);
1364
1718
  }
1719
+ for (const defaultValue of argDefaultValues.values()) {
1720
+ result.imports.mergeWith(defaultValue.imports);
1721
+ }
1365
1722
  return result;
1366
1723
  }
1367
1724
  function getAccountRole(acc) {
@@ -1378,19 +1735,6 @@ function isDiscriminatorArg(arg, node) {
1378
1735
  (d) => d.kind === "fieldDiscriminatorNode" && d.name === arg.name
1379
1736
  );
1380
1737
  }
1381
- function getDiscriminatorDefault(arg, _node) {
1382
- if (arg.defaultValue) {
1383
- if (arg.defaultValue.kind === "numberValueNode") {
1384
- return String(arg.defaultValue.number);
1385
- }
1386
- if (arg.defaultValue.kind === "bytesValueNode") {
1387
- const data = arg.defaultValue.data;
1388
- const bytes = data.match(/.{1,2}/g)?.map((b) => parseInt(b, 16)) ?? [];
1389
- return `Uint8List.fromList([${bytes.join(", ")}])`;
1390
- }
1391
- }
1392
- return "0";
1393
- }
1394
1738
  function isConstDefaultValue(defaultValue) {
1395
1739
  if (!defaultValue) return false;
1396
1740
  switch (defaultValue.kind) {
@@ -1403,34 +1747,8 @@ function isConstDefaultValue(defaultValue) {
1403
1747
  return false;
1404
1748
  }
1405
1749
  }
1406
- function getDefaultValue(arg, typeStr) {
1407
- if (!arg.defaultValue) return "null";
1408
- const isBigInt = typeStr === "BigInt";
1409
- const dv = arg.defaultValue;
1410
- switch (dv.kind) {
1411
- case "numberValueNode":
1412
- return isBigInt ? `BigInt.from(${dv.number})` : String(dv.number);
1413
- case "booleanValueNode":
1414
- return String(dv.boolean);
1415
- case "stringValueNode":
1416
- return `'${dv.string}'`;
1417
- case "publicKeyValueNode": {
1418
- const wellKnownName = WELL_KNOWN_ADDRESSES.get(dv.publicKey);
1419
- if (wellKnownName) {
1420
- use(wellKnownName, "solanaAddresses");
1421
- return wellKnownName;
1422
- }
1423
- return `Address('${dv.publicKey}')`;
1424
- }
1425
- case "noneValueNode":
1426
- return "null";
1427
- case "bytesValueNode": {
1428
- const bytes = dv.data.match(/.{1,2}/g)?.map((b) => parseInt(b, 16)) ?? [];
1429
- return `Uint8List.fromList([${bytes.join(", ")}])`;
1430
- }
1431
- default:
1432
- return "null";
1433
- }
1750
+ function isOmittedDefaultArgument(arg) {
1751
+ return arg.defaultValue != null && arg.defaultValueStrategy === "omitted";
1434
1752
  }
1435
1753
  function getPdaPageFragment(node, scope) {
1436
1754
  const name = node.name;
@@ -1725,25 +2043,26 @@ function getTypePageFragment(node, scope) {
1725
2043
  scope.nameApi.dataType(name);
1726
2044
  const typeNode = node.type;
1727
2045
  if (typeNode.kind === "enumTypeNode") {
1728
- const isScalar = typeNode.variants.every(
2046
+ const variants = typeNode.variants ?? [];
2047
+ const isScalar = variants.every(
1729
2048
  (v) => v.kind === "enumEmptyVariantTypeNode"
1730
2049
  );
1731
2050
  if (isScalar) {
1732
- return getScalarEnumPageFragment(node, scope);
2051
+ return getScalarEnumPageFragment(node, variants, scope);
1733
2052
  }
1734
- return getDataEnumPageFragment(node, scope);
2053
+ return getDataEnumPageFragment(node, variants, scope);
1735
2054
  }
1736
2055
  if (typeNode.kind === "structTypeNode") {
1737
2056
  return getStructPageFragment(node, scope);
1738
2057
  }
1739
2058
  return getTypeAliasPageFragment(node, scope);
1740
2059
  }
1741
- function getScalarEnumPageFragment(node, scope) {
2060
+ function getScalarEnumPageFragment(node, enumVariants, scope) {
1742
2061
  const name = node.name;
1743
2062
  const typeName = scope.nameApi.dataType(name);
1744
2063
  const enumNode = node.type;
1745
2064
  if (enumNode.kind !== "enumTypeNode") return emptyFragment();
1746
- const variants = enumNode.variants.map((v) => {
2065
+ const variants = enumVariants.map((v) => {
1747
2066
  const variantName = scope.nameApi.enumVariant(v.name);
1748
2067
  return variantName;
1749
2068
  });
@@ -1754,6 +2073,14 @@ function getScalarEnumPageFragment(node, scope) {
1754
2073
  const resolvedSize = resolveNestedTypeNode(enumNode.size);
1755
2074
  const sizeType = resolvedSize.format ?? "u8";
1756
2075
  const sizeCodecName = getNumberCodecName2(sizeType);
2076
+ const isU64 = sizeType === "u64";
2077
+ const encodedVariantIndex = isU64 ? "BigInt.from(value.index)" : "value.index";
2078
+ const decodedVariant = isU64 ? `(BigInt value, Uint8List bytes, int offset) {
2079
+ if (value.isNegative || value >= BigInt.from(${typeName}.values.length)) {
2080
+ throw RangeError('Invalid ${typeName} discriminator: ' + value.toString());
2081
+ }
2082
+ return ${typeName}.values[value.toInt()];
2083
+ }` : `(int value, Uint8List bytes, int offset) => ${typeName}.values[value]`;
1757
2084
  return fragment`// Auto-generated. Do not edit.
1758
2085
  // ignore_for_file: type=lint
1759
2086
 
@@ -1774,14 +2101,14 @@ ${fragmentFromString(variantLines)}
1774
2101
  Encoder<${fragmentFromString(typeName)}> ${fragmentFromString(encoderName)}() {
1775
2102
  return transformEncoder(
1776
2103
  get${fragmentFromString(sizeCodecName)}Encoder(),
1777
- (${fragmentFromString(typeName)} value) => value.index,
2104
+ (${fragmentFromString(typeName)} value) => ${fragmentFromString(encodedVariantIndex)},
1778
2105
  );
1779
2106
  }
1780
2107
 
1781
2108
  Decoder<${fragmentFromString(typeName)}> ${fragmentFromString(decoderName)}() {
1782
2109
  return transformDecoder(
1783
2110
  get${fragmentFromString(sizeCodecName)}Decoder(),
1784
- (int value, Uint8List bytes, int offset) => ${fragmentFromString(typeName)}.values[value],
2111
+ ${fragmentFromString(decodedVariant)},
1785
2112
  );
1786
2113
  }
1787
2114
 
@@ -1789,7 +2116,7 @@ Codec<${fragmentFromString(typeName)}, ${fragmentFromString(typeName)}> ${fragme
1789
2116
  return combineCodec(${fragmentFromString(encoderName)}(), ${fragmentFromString(decoderName)}());
1790
2117
  }`;
1791
2118
  }
1792
- function getDataEnumPageFragment(node, scope) {
2119
+ function getDataEnumPageFragment(node, variants, scope) {
1793
2120
  const name = node.name;
1794
2121
  const typeName = scope.nameApi.dataType(name);
1795
2122
  const enumNode = node.type;
@@ -1800,8 +2127,8 @@ function getDataEnumPageFragment(node, scope) {
1800
2127
  const encodeCases = [];
1801
2128
  const decodeCases = [];
1802
2129
  const allVariantManifests = [];
1803
- for (let i = 0; i < enumNode.variants.length; i++) {
1804
- const variant = enumNode.variants[i];
2130
+ for (let i = 0; i < variants.length; i++) {
2131
+ const variant = variants[i];
1805
2132
  const variantName = pascalCase(variant.name);
1806
2133
  const variantClassName = scope.nameApi.sealedClassVariant(
1807
2134
  name,
@@ -1831,7 +2158,7 @@ function getDataEnumPageFragment(node, scope) {
1831
2158
  decodeCases.push(`case ${i}: return const ${variantClassName}();`);
1832
2159
  } else if (variant.kind === "enumStructVariantTypeNode") {
1833
2160
  const resolvedStruct = resolveNestedTypeNode(variant.struct);
1834
- const fields = resolvedStruct.fields;
2161
+ const fields = resolvedStruct.fields ?? [];
1835
2162
  const fieldManifests = fields.map((f) => ({
1836
2163
  field: f,
1837
2164
  manifest: visit(f.type, scope.typeManifestVisitor)
@@ -1897,7 +2224,7 @@ ${fieldDecls}
1897
2224
  decodeCases.push(`case ${i}: return ${variantClassName}(${fromMapFields.replace(/\n/g, " ").replace(/,$/, "")});`);
1898
2225
  } else if (variant.kind === "enumTupleVariantTypeNode") {
1899
2226
  const resolvedTuple = resolveNestedTypeNode(variant.tuple);
1900
- const items = resolvedTuple.items;
2227
+ const items = resolvedTuple.items ?? [];
1901
2228
  if (items.length === 1) {
1902
2229
  const manifest = visit(items[0], scope.typeManifestVisitor);
1903
2230
  allVariantManifests.push(manifest);
@@ -1997,7 +2324,7 @@ function getStructPageFragment(node, scope) {
1997
2324
  const typeName = scope.nameApi.dataType(name);
1998
2325
  const structNode = node.type;
1999
2326
  if (structNode.kind !== "structTypeNode") return emptyFragment();
2000
- const fields = structNode.fields;
2327
+ const fields = structNode.fields ?? [];
2001
2328
  const fieldManifests = fields.map((f) => ({
2002
2329
  field: f,
2003
2330
  manifest: visit(f.type, scope.typeManifestVisitor)
@@ -2204,7 +2531,7 @@ function getRenderMapVisitor(options = {}) {
2204
2531
  visitRoot(node, { self }) {
2205
2532
  const programMaps = [
2206
2533
  visit(node.program, self),
2207
- ...node.additionalPrograms.map((p) => visit(p, self))
2534
+ ...(node.additionalPrograms ?? []).map((p) => visit(p, self))
2208
2535
  ];
2209
2536
  const mergedProgramMap = mergeRenderMaps(programMaps);
2210
2537
  const categoryMaps = getSharedCategoryIndexMaps(mergedProgramMap);
@@ -2350,11 +2677,12 @@ function renderVisitor(outputDir, options = {}) {
2350
2677
  dependencyMap
2351
2678
  } = options;
2352
2679
  return rootNodeVisitor((root) => {
2680
+ const normalizedRoot = normalizeRootNode(root);
2353
2681
  if (deleteFolderBeforeRendering && existsSync(outputDir)) {
2354
2682
  deleteDirectory(outputDir);
2355
2683
  }
2356
2684
  const renderMap = visit(
2357
- root,
2685
+ normalizedRoot,
2358
2686
  getRenderMapVisitor({ nameApi, dependencyMap })
2359
2687
  );
2360
2688
  const typePathMap = {};
@@ -2436,6 +2764,6 @@ function cleanFragmentContent(content) {
2436
2764
  // src/index.ts
2437
2765
  var src_default = renderVisitor;
2438
2766
 
2439
- export { DART_EXTERNAL_PACKAGE_MAP, DartImportMap, createDartNameApi, src_default as default, getRenderMapVisitor, getTypeManifestVisitor, renderVisitor };
2767
+ export { DART_EXTERNAL_PACKAGE_MAP, DartImportMap, createDartNameApi, src_default as default, getRenderMapVisitor, getTypeManifestVisitor, normalizeRootNode, renderVisitor };
2440
2768
  //# sourceMappingURL=index.browser.js.map
2441
2769
  //# sourceMappingURL=index.browser.js.map