codama-renderers-dart 0.5.0 → 0.5.3

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 (48) hide show
  1. package/{readme.md → README.md} +38 -42
  2. package/dist/index.browser.cjs +628 -147
  3. package/dist/index.browser.cjs.map +1 -1
  4. package/dist/index.browser.js +628 -148
  5. package/dist/index.browser.js.map +1 -1
  6. package/dist/index.node.cjs +628 -147
  7. package/dist/index.node.cjs.map +1 -1
  8. package/dist/index.node.js +628 -148
  9. package/dist/index.node.js.map +1 -1
  10. package/dist/index.react-native.js +628 -148
  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/instructionPage.d.ts.map +1 -1
  14. package/dist/types/fragments/typePage.d.ts.map +1 -1
  15. package/dist/types/index.d.ts +1 -0
  16. package/dist/types/index.d.ts.map +1 -1
  17. package/dist/types/utils/discriminators.d.ts +10 -0
  18. package/dist/types/utils/discriminators.d.ts.map +1 -0
  19. package/dist/types/utils/exactDecoder.d.ts +17 -0
  20. package/dist/types/utils/exactDecoder.d.ts.map +1 -0
  21. package/dist/types/utils/formatCode.d.ts +1 -1
  22. package/dist/types/utils/formatCode.d.ts.map +1 -1
  23. package/dist/types/utils/index.d.ts +4 -0
  24. package/dist/types/utils/index.d.ts.map +1 -1
  25. package/dist/types/utils/nameTransformers.d.ts +5 -0
  26. package/dist/types/utils/nameTransformers.d.ts.map +1 -1
  27. package/dist/types/utils/normalizeRootNode.d.ts +11 -0
  28. package/dist/types/utils/normalizeRootNode.d.ts.map +1 -0
  29. package/dist/types/utils/valueNodes.d.ts +16 -0
  30. package/dist/types/utils/valueNodes.d.ts.map +1 -0
  31. package/dist/types/visitors/getTypeManifestVisitor.d.ts +1 -1
  32. package/dist/types/visitors/getTypeManifestVisitor.d.ts.map +1 -1
  33. package/dist/types/visitors/renderVisitor.d.ts.map +1 -1
  34. package/package.json +21 -18
  35. package/src/fragments/accountPage.ts +90 -17
  36. package/src/fragments/instructionPage.ts +112 -79
  37. package/src/fragments/typePage.ts +134 -31
  38. package/src/index.ts +1 -0
  39. package/src/utils/discriminators.ts +121 -0
  40. package/src/utils/exactDecoder.ts +82 -0
  41. package/src/utils/formatCode.ts +7 -11
  42. package/src/utils/index.ts +4 -0
  43. package/src/utils/nameTransformers.ts +28 -1
  44. package/src/utils/normalizeRootNode.ts +76 -0
  45. package/src/utils/valueNodes.ts +87 -0
  46. package/src/visitors/getRenderMapVisitor.ts +1 -1
  47. package/src/visitors/getTypeManifestVisitor.ts +37 -14
  48. 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
 
@@ -22,9 +80,83 @@ function formatDartDirectory(dir) {
22
80
  function pascalCase(str) {
23
81
  return str.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^(.)/, (c) => c.toUpperCase());
24
82
  }
83
+ var DART_RESERVED_WORDS = /* @__PURE__ */ new Set([
84
+ "abstract",
85
+ "as",
86
+ "assert",
87
+ "async",
88
+ "augment",
89
+ "await",
90
+ "base",
91
+ "break",
92
+ "case",
93
+ "catch",
94
+ "class",
95
+ "const",
96
+ "continue",
97
+ "covariant",
98
+ "default",
99
+ "deferred",
100
+ "do",
101
+ "dynamic",
102
+ "else",
103
+ "enum",
104
+ "export",
105
+ "extends",
106
+ "extension",
107
+ "external",
108
+ "factory",
109
+ "false",
110
+ "final",
111
+ "finally",
112
+ "for",
113
+ "get",
114
+ "hide",
115
+ "if",
116
+ "implements",
117
+ "import",
118
+ "in",
119
+ "interface",
120
+ "is",
121
+ "late",
122
+ "library",
123
+ "mixin",
124
+ "native",
125
+ "new",
126
+ "null",
127
+ "of",
128
+ "on",
129
+ "operator",
130
+ "part",
131
+ "required",
132
+ "rethrow",
133
+ "return",
134
+ "sealed",
135
+ "set",
136
+ "show",
137
+ "static",
138
+ "super",
139
+ "switch",
140
+ "sync",
141
+ "this",
142
+ "throw",
143
+ "true",
144
+ "try",
145
+ "typedef",
146
+ "var",
147
+ "void",
148
+ "when",
149
+ "while",
150
+ "with",
151
+ "yield"
152
+ ]);
153
+ function escapeDartIdentifier(name) {
154
+ return DART_RESERVED_WORDS.has(name) ? `${name}_` : name;
155
+ }
25
156
  function camelCase(str) {
26
157
  const pascal = pascalCase(str);
27
- return pascal.charAt(0).toLowerCase() + pascal.slice(1);
158
+ const cased = pascal.charAt(0).toLowerCase() + pascal.slice(1);
159
+ return escapeDartIdentifier(cased);
28
160
  }
29
161
  function snakeCase(str) {
30
162
  return str.replace(/([A-Z])/g, "_$1").replace(/[-\s]+/g, "_").replace(/^_/, "").replace(/_+/g, "_").toLowerCase();
@@ -204,17 +336,6 @@ function use(typeName, module) {
204
336
  return { content: typeName, imports };
205
337
  }
206
338
 
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
339
  // src/utils/wellKnownAddresses.ts
219
340
  var WELL_KNOWN_ADDRESSES = /* @__PURE__ */ new Map([
220
341
  // Agave / Solana native programs
@@ -269,6 +390,207 @@ var WELL_KNOWN_ADDRESSES = /* @__PURE__ */ new Map([
269
390
  ["Es9vMFrzaCERm6YKXx6bqKr1Rmd9b5v3J8rM5e4rfF4", "usdtAddress"]
270
391
  ]);
271
392
 
393
+ // src/utils/valueNodes.ts
394
+ function getDartValueFragment(value, dartType) {
395
+ switch (value.kind) {
396
+ case "numberValueNode":
397
+ return fragmentFromString(
398
+ dartType === "BigInt" ? `BigInt.from(${value.number})` : String(value.number)
399
+ );
400
+ case "booleanValueNode":
401
+ return fragmentFromString(String(value.boolean));
402
+ case "stringValueNode":
403
+ return fragmentFromString(toDartStringLiteral(value.string));
404
+ case "noneValueNode":
405
+ return fragmentFromString("null");
406
+ case "bytesValueNode": {
407
+ if (value.encoding !== "base16") {
408
+ throw new Error(
409
+ `Unsupported deterministic bytes encoding: ${value.encoding}`
410
+ );
411
+ }
412
+ const clean = value.data.replace(/^0x/, "");
413
+ if (clean.length % 2 !== 0 || !/^[0-9a-f]*$/i.test(clean)) {
414
+ throw new Error(`Invalid hexadecimal bytes value: ${value.data}`);
415
+ }
416
+ const bytes = clean.match(/.{2}/g)?.map((byte) => `0x${byte}`) ?? [];
417
+ return fragment`${use("Uint8List", "dartTypedData")}.fromList([${fragmentFromString(bytes.join(", "))}])`;
418
+ }
419
+ case "publicKeyValueNode": {
420
+ const wellKnownName = WELL_KNOWN_ADDRESSES.get(value.publicKey);
421
+ return wellKnownName ? fragment`${use(wellKnownName, "solanaAddresses")}` : fragment`${use("Address", "solanaAddresses")}(${fragmentFromString(toDartStringLiteral(value.publicKey))})`;
422
+ }
423
+ default:
424
+ throw new Error(
425
+ `Unsupported deterministic Dart value node: ${value.kind}`
426
+ );
427
+ }
428
+ }
429
+ function isConstDartValueNode(value) {
430
+ switch (value.kind) {
431
+ case "numberValueNode":
432
+ case "booleanValueNode":
433
+ case "stringValueNode":
434
+ case "noneValueNode":
435
+ case "publicKeyValueNode":
436
+ return true;
437
+ default:
438
+ return false;
439
+ }
440
+ }
441
+ function toDartStringLiteral(value) {
442
+ return `'${value.replaceAll("\\", "\\\\").replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t").replaceAll("\b", "\\b").replaceAll("\f", "\\f").replaceAll("$", "\\$").replaceAll("'", "\\'")}'`;
443
+ }
444
+
445
+ // src/utils/discriminators.ts
446
+ function getDiscriminatorValidationFragment(node, scope) {
447
+ const discriminators = node.discriminators ?? [];
448
+ if (discriminators.length === 0) return emptyFragment();
449
+ const fields = getDiscriminatorFields(node);
450
+ const fragments = discriminators.map((discriminator) => {
451
+ switch (discriminator.kind) {
452
+ case "constantDiscriminatorNode":
453
+ return getConstantValidationFragment(
454
+ discriminator.constant,
455
+ discriminator.offset,
456
+ scope
457
+ );
458
+ case "fieldDiscriminatorNode": {
459
+ const field = fields.find(
460
+ (candidate) => candidate.name === discriminator.name
461
+ );
462
+ if (field?.defaultValue == null) {
463
+ throw new Error(
464
+ `Field discriminator "${discriminator.name}" on ${node.kind} "${node.name}" must reference a field with a default value.`
465
+ );
466
+ }
467
+ return getConstantValidationFragment(
468
+ {
469
+ type: field.type,
470
+ value: field.defaultValue
471
+ },
472
+ discriminator.offset,
473
+ scope
474
+ );
475
+ }
476
+ case "sizeDiscriminatorNode": {
477
+ if (!Number.isSafeInteger(discriminator.size) || discriminator.size < 0) {
478
+ throw new Error(
479
+ `Discriminator size must be a non-negative safe integer, got ${discriminator.size}.`
480
+ );
481
+ }
482
+ return fragment`if (bytes.length - offset != ${fragmentFromString(String(discriminator.size))}) {
483
+ throw ${use("SolanaError", "solanaErrors")}(
484
+ ${use("SolanaErrorCode", "solanaErrors")}.codecsInvalidByteLength,
485
+ {
486
+ 'codecDescription': '${fragmentFromString(node.name)} discriminator',
487
+ 'expected': ${fragmentFromString(String(discriminator.size))},
488
+ 'bytesLength': bytes.length - offset,
489
+ },
490
+ );
491
+ }`;
492
+ }
493
+ }
494
+ });
495
+ return mergeFragments(fragments, (contents) => contents.join("\n"));
496
+ }
497
+ function getConstantValidationFragment(constant, discriminatorOffset, scope) {
498
+ if (!Number.isSafeInteger(discriminatorOffset) || discriminatorOffset < 0) {
499
+ throw new Error(
500
+ `Discriminator offset must be a non-negative safe integer, got ${discriminatorOffset}.`
501
+ );
502
+ }
503
+ const manifest = visit(constant.type, scope.typeManifestVisitor);
504
+ const value = getDartValueFragment(
505
+ constant.value,
506
+ manifest.type.content
507
+ );
508
+ return fragment`${use("getConstantDecoder", "solanaCodecsDataStructures")}(
509
+ ${manifest.encoder}.encode(${value}),
510
+ ).read(bytes, offset + ${fragmentFromString(String(discriminatorOffset))});`;
511
+ }
512
+ function getDiscriminatorFields(node) {
513
+ if (node.kind === "accountNode") {
514
+ return resolveNestedTypeNode(node.data).fields ?? [];
515
+ }
516
+ return (node.arguments ?? []).map(
517
+ (argument) => ({
518
+ ...argument,
519
+ kind: "structFieldTypeNode"
520
+ })
521
+ );
522
+ }
523
+
524
+ // src/utils/exactDecoder.ts
525
+ function getTopLevelDecoderFragment({
526
+ typeName,
527
+ description,
528
+ discriminatorValidation,
529
+ fromMapFields,
530
+ requireExactConsumption
531
+ }) {
532
+ const validation = discriminatorValidation.content ? ` ${discriminatorValidation.content.replaceAll("\n", "\n ")}
533
+ ` : "";
534
+ const consumptionCheck = requireExactConsumption ? ` if (newOffset != bytes.length) {
535
+ throwInvalidByteLength(newOffset - offset, bytes.length - offset);
536
+ }
537
+ ` : "";
538
+ const fixedSizeCheck = requireExactConsumption ? "bytesLength != structDecoder.fixedSize" : "bytesLength < structDecoder.fixedSize";
539
+ const result = fragment` Never throwInvalidByteLength(int expected, int bytesLength) {
540
+ throw ${use("SolanaError", "solanaErrors")}(
541
+ ${use("SolanaErrorCode", "solanaErrors")}.codecsInvalidByteLength,
542
+ {
543
+ 'codecDescription': '${fragmentFromString(description)}',
544
+ 'expected': expected,
545
+ 'bytesLength': bytesLength,
546
+ },
547
+ );
548
+ }
549
+
550
+ (${fragmentFromString(typeName)}, int) readTopLevel(Uint8List bytes, int offset) {
551
+ ${fragmentFromString(validation)} final (map, newOffset) = structDecoder.read(bytes, offset);
552
+ ${fragmentFromString(consumptionCheck)}
553
+ return (
554
+ ${fragmentFromString(typeName)}(
555
+ ${fragmentFromString(fromMapFields)}
556
+ ),
557
+ newOffset,
558
+ );
559
+ }
560
+
561
+ return switch (structDecoder) {
562
+ ${use("FixedSizeDecoder", "solanaCodecsCore")}<Map<String, Object?>>() =>
563
+ ${use("FixedSizeDecoder", "solanaCodecsCore")}<${fragmentFromString(typeName)}>(
564
+ fixedSize: structDecoder.fixedSize,
565
+ read: (bytes, offset) {
566
+ final bytesLength = bytes.length - offset;
567
+ if (${fragmentFromString(fixedSizeCheck)}) {
568
+ throwInvalidByteLength(structDecoder.fixedSize, bytesLength);
569
+ }
570
+ return readTopLevel(bytes, offset);
571
+ },
572
+ ),
573
+ ${use("VariableSizeDecoder", "solanaCodecsCore")}<Map<String, Object?>>() =>
574
+ ${use("VariableSizeDecoder", "solanaCodecsCore")}<${fragmentFromString(typeName)}>(
575
+ read: readTopLevel,
576
+ maxSize: structDecoder.maxSize,
577
+ ),
578
+ };`;
579
+ result.imports.mergeWith(discriminatorValidation.imports);
580
+ return result;
581
+ }
582
+
583
+ // src/utils/typeManifest.ts
584
+ function emptyTypeManifest() {
585
+ return {
586
+ type: emptyFragment(),
587
+ encoder: emptyFragment(),
588
+ decoder: emptyFragment(),
589
+ value: emptyFragment(),
590
+ isEnum: false
591
+ };
592
+ }
593
+
272
594
  // src/visitors/getTypeManifestVisitor.ts
273
595
  function getTypeManifestVisitor(input) {
274
596
  const { nameApi, linkables, nonScalarEnums = [] } = input;
@@ -412,12 +734,17 @@ function getTypeManifestVisitor(input) {
412
734
  const itemManifest = visit(node.item, self);
413
735
  const encoderSizeExpr = getArraySizeExpression(node, "Encoder");
414
736
  const decoderSizeExpr = getArraySizeExpression(node, "Decoder");
737
+ const itemType = fragmentFromString(itemManifest.type.content);
738
+ const itemEncoder = fragment`${use(
739
+ "transformEncoder",
740
+ "solanaCodecsCore"
741
+ )}(${itemManifest.encoder}, (${itemType} value) => value)`;
415
742
  return {
416
743
  type: fragment`List<${itemManifest.type}>`,
417
744
  encoder: fragment`${use(
418
745
  "getArrayEncoder",
419
746
  "solanaCodecsDataStructures"
420
- )}(${itemManifest.encoder}${encoderSizeExpr})`,
747
+ )}(${itemEncoder}${encoderSizeExpr})`,
421
748
  decoder: fragment`${use(
422
749
  "getArrayDecoder",
423
750
  "solanaCodecsDataStructures"
@@ -464,7 +791,7 @@ function getTypeManifestVisitor(input) {
464
791
  };
465
792
  },
466
793
  visitTupleType(node, { self }) {
467
- const itemManifests = node.items.map((item) => visit(item, self));
794
+ const itemManifests = (node.items ?? []).map((item) => visit(item, self));
468
795
  const typeList = itemManifests.map((m) => m.type.content).join(", ");
469
796
  const encoderList = itemManifests.map((m) => m.encoder);
470
797
  const decoderList = itemManifests.map((m) => m.decoder);
@@ -473,18 +800,28 @@ function getTypeManifestVisitor(input) {
473
800
  typeFragment.imports.mergeWith(m.type.imports);
474
801
  }
475
802
  const encoderListStr = encoderList.map((e) => e.content).join(", ");
803
+ const decoderListStr = decoderList.map((d) => d.content).join(", ");
804
+ let tupleEncoderFn = "getTupleEncoder";
805
+ let tupleDecoderFn = "getTupleDecoder";
806
+ let tupleEncoderArgs = `[${encoderListStr}]`;
807
+ let tupleDecoderArgs = `[${decoderListStr}]`;
808
+ if (itemManifests.length === 2) {
809
+ tupleEncoderFn = "getTuple2Encoder";
810
+ tupleDecoderFn = "getTuple2Decoder";
811
+ tupleEncoderArgs = encoderListStr;
812
+ tupleDecoderArgs = decoderListStr;
813
+ }
476
814
  const encoderFrag = fragment`${use(
477
- "getTupleEncoder",
815
+ tupleEncoderFn,
478
816
  "solanaCodecsDataStructures"
479
- )}([${fragmentFromString(encoderListStr)}])`;
817
+ )}(${tupleEncoderArgs})`;
480
818
  for (const e of encoderList) {
481
819
  encoderFrag.imports.mergeWith(e.imports);
482
820
  }
483
- const decoderListStr = decoderList.map((d) => d.content).join(", ");
484
821
  const decoderFrag = fragment`${use(
485
- "getTupleDecoder",
822
+ tupleDecoderFn,
486
823
  "solanaCodecsDataStructures"
487
- )}([${fragmentFromString(decoderListStr)}])`;
824
+ )}(${tupleDecoderArgs})`;
488
825
  for (const d of decoderList) {
489
826
  decoderFrag.imports.mergeWith(d.imports);
490
827
  }
@@ -497,8 +834,9 @@ function getTypeManifestVisitor(input) {
497
834
  };
498
835
  },
499
836
  visitStructType(node, { self }) {
500
- const fields = node.fields.map((f) => visit(f, self));
501
- const fieldNames = node.fields.map((f) => f.name);
837
+ const nodeFields = node.fields ?? [];
838
+ const fields = nodeFields.map((f) => visit(f, self));
839
+ const fieldNames = nodeFields.map((f) => f.name);
502
840
  const typeStr = "Map<String, Object?>";
503
841
  const encoderFields = fields.map((f, i) => `('${fieldNames[i]}', ${f.encoder.content})`).join(", ");
504
842
  const encoderFrag = fragment`${use(
@@ -528,7 +866,7 @@ function getTypeManifestVisitor(input) {
528
866
  return visit(node.type, self);
529
867
  },
530
868
  visitEnumType(node, { self }) {
531
- const isScalar = node.variants.every(
869
+ const isScalar = (node.variants ?? []).every(
532
870
  (v) => v.kind === "enumEmptyVariantTypeNode"
533
871
  );
534
872
  if (isScalar) {
@@ -570,7 +908,10 @@ function getTypeManifestVisitor(input) {
570
908
  encoder: fragment`${use(
571
909
  "getNullableEncoder",
572
910
  "solanaCodecsDataStructures"
573
- )}<${fragmentFromString(itemTypeStr)}>(${itemManifest.encoder}${fragmentFromString(prefixExpr)}${fragmentFromString(noneValueExpr)})`,
911
+ )}<${fragmentFromString(itemTypeStr)}>(${use(
912
+ "transformEncoder",
913
+ "solanaCodecsCore"
914
+ )}(${itemManifest.encoder}, (${fragmentFromString(itemTypeStr)} value) => value)${fragmentFromString(prefixExpr)}${fragmentFromString(noneValueExpr)})`,
574
915
  decoder: fragment`${use(
575
916
  "getNullableDecoder",
576
917
  "solanaCodecsDataStructures"
@@ -620,7 +961,7 @@ function getTypeManifestVisitor(input) {
620
961
  encoder: fragment`${use(
621
962
  "fixEncoderSize",
622
963
  "solanaCodecsCore"
623
- )}(${innerManifest.encoder}, ${fragmentFromString(String(node.size))})`,
964
+ )}(${innerManifest.encoder}, ${fragmentFromString(String(node.size))}, allowTruncation: false)`,
624
965
  decoder: fragment`${use(
625
966
  "fixDecoderSize",
626
967
  "solanaCodecsCore"
@@ -658,7 +999,7 @@ function getTypeManifestVisitor(input) {
658
999
  },
659
1000
  visitHiddenPrefixType(node, { self }) {
660
1001
  const innerManifest = visit(node.type, self);
661
- const prefixes = node.prefix.map((p) => getHiddenAffixManifest(p, self));
1002
+ const prefixes = (node.prefix ?? []).map((p) => getHiddenAffixManifest(p, self));
662
1003
  const prefixEncoders = prefixes.map((p) => p.encoder.content).join(", ");
663
1004
  const prefixDecoders = prefixes.map((p) => p.decoder.content).join(", ");
664
1005
  const encoderFrag = fragment`${use(
@@ -685,7 +1026,7 @@ function getTypeManifestVisitor(input) {
685
1026
  },
686
1027
  visitHiddenSuffixType(node, { self }) {
687
1028
  const innerManifest = visit(node.type, self);
688
- const suffixes = node.suffix.map((s) => getHiddenAffixManifest(s, self));
1029
+ const suffixes = (node.suffix ?? []).map((s) => getHiddenAffixManifest(s, self));
689
1030
  const suffixEncoders = suffixes.map((s) => s.encoder.content).join(", ");
690
1031
  const suffixDecoders = suffixes.map((s) => s.decoder.content).join(", ");
691
1032
  const encoderFrag = fragment`${use(
@@ -972,19 +1313,52 @@ function hexToBytes(hex) {
972
1313
  function getAccountPageFragment(node, scope) {
973
1314
  const name = node.name;
974
1315
  const typeName = scope.nameApi.dataType(name);
975
- const dataNode = resolveNestedTypeNode(node.data);
976
- const fields = dataNode.fields;
1316
+ const fields = resolveNestedTypeNode(node.data).fields ?? [];
977
1317
  const fieldManifests = fields.map((f) => ({
978
1318
  field: f,
979
1319
  manifest: visit(f.type, scope.typeManifestVisitor)
980
1320
  }));
1321
+ const omittedFields = fields.filter(isOmittedDefaultField);
1322
+ const constructorFields = fields.filter(
1323
+ (field) => !isOmittedDefaultField(field)
1324
+ );
1325
+ const forcedFields = /* @__PURE__ */ new Set([
1326
+ ...omittedFields,
1327
+ ...fields.filter((field) => isFieldDiscriminator(field, node))
1328
+ ]);
1329
+ const fieldDefaults = new Map(
1330
+ fieldManifests.flatMap(({ field, manifest }) => {
1331
+ if (!forcedFields.has(field) || field.defaultValue == null) return [];
1332
+ return [
1333
+ [
1334
+ field,
1335
+ getDartValueFragment(field.defaultValue, manifest.type.content)
1336
+ ]
1337
+ ];
1338
+ })
1339
+ );
1340
+ for (const field of forcedFields) {
1341
+ if (!fieldDefaults.has(field)) {
1342
+ throw new Error(
1343
+ `Forced field "${field.name}" on account "${node.name}" must have a deterministic default value.`
1344
+ );
1345
+ }
1346
+ }
981
1347
  const fieldDecls = fieldManifests.map(({ field: f, manifest }) => {
982
1348
  return ` final ${manifest.type.content} ${camelCase(f.name)};`;
983
1349
  }).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}({
1350
+ const ctorParams = constructorFields.map((f) => ` required this.${camelCase(f.name)},`).join("\n");
1351
+ const ctorInitializers = omittedFields.map((field) => {
1352
+ const fieldName = camelCase(field.name);
1353
+ return `${fieldName} = ${fieldDefaults.get(field).content}`;
1354
+ }).join(",\n ");
1355
+ const ctorKeyword = omittedFields.every(
1356
+ (field) => isConstDartValueNode(field.defaultValue)
1357
+ ) ? "const " : "";
1358
+ const ctorSignature = constructorFields.length === 0 ? `${ctorKeyword}${typeName}()${ctorInitializers ? ` : ${ctorInitializers}` : ""};` : `${ctorKeyword}${typeName}({
986
1359
  ${ctorParams}
987
- });`;
1360
+ })${ctorInitializers ? ` :
1361
+ ${ctorInitializers}` : ""};`;
988
1362
  const eqChecks = fields.length === 0 ? "true" : fields.map(
989
1363
  (f) => `${camelCase(f.name)} == other.${camelCase(f.name)}`
990
1364
  ).join(" &&\n ");
@@ -999,10 +1373,11 @@ ${ctorParams}
999
1373
  const decFields = fieldManifests.map(({ field: f, manifest }) => {
1000
1374
  return ` ('${f.name}', ${manifest.decoder.content}),`;
1001
1375
  }).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 }) => {
1376
+ const toMapFields = fields.map((f) => {
1377
+ const value = forcedFields.has(f) ? fieldDefaults.get(f).content : `value.${camelCase(f.name)}`;
1378
+ return ` '${f.name}': ${value},`;
1379
+ }).join("\n");
1380
+ const fromMapFields = fieldManifests.filter(({ field }) => !isOmittedDefaultField(field)).map(({ field: f, manifest }) => {
1006
1381
  const typeStr = manifest.type.content;
1007
1382
  const isNullable = typeStr.endsWith("?");
1008
1383
  const accessor = isNullable ? `map['${f.name}']` : `map['${f.name}']!`;
@@ -1016,6 +1391,19 @@ ${ctorParams}
1016
1391
  /// The size of the [${fragmentFromString(typeName)}] account data in bytes.
1017
1392
  const int ${fragmentFromString(scope.nameApi.accountSizeConstant(name))} = ${fragmentFromString(String(node.size))};` : emptyFragment();
1018
1393
  const discFragment = getDiscriminatorConstantsFragment(node, scope);
1394
+ const discriminatorValidation = getDiscriminatorValidationFragment(
1395
+ node,
1396
+ scope
1397
+ );
1398
+ const topLevelDecoder = getTopLevelDecoderFragment({
1399
+ typeName,
1400
+ description: `${name} account decoder`,
1401
+ discriminatorValidation,
1402
+ fromMapFields,
1403
+ requireExactConsumption: (node.discriminators ?? []).some(
1404
+ (discriminator) => discriminator.kind === "sizeDiscriminatorNode"
1405
+ )
1406
+ });
1019
1407
  const parts = [
1020
1408
  fragment`// Auto-generated. Do not edit.
1021
1409
  // ignore_for_file: type=lint
@@ -1075,12 +1463,7 @@ Decoder<${fragmentFromString(typeName)}> ${fragmentFromString(decoderName)}() {
1075
1463
  ${fragmentFromString(decFields)}
1076
1464
  ]);
1077
1465
 
1078
- return transformDecoder(
1079
- structDecoder,
1080
- (Map<String, Object?> map, Uint8List bytes, int offset) => ${fragmentFromString(typeName)}(
1081
- ${fragmentFromString(fromMapFields)}
1082
- ),
1083
- );
1466
+ ${topLevelDecoder}
1084
1467
  }
1085
1468
 
1086
1469
  Codec<${fragmentFromString(typeName)}, ${fragmentFromString(typeName)}> ${fragmentFromString(codecName)}() {
@@ -1096,8 +1479,19 @@ Account<${fragmentFromString(typeName)}> ${fragmentFromString(decodeFnName)}(Enc
1096
1479
  result.imports.mergeWith(manifest.decoder.imports);
1097
1480
  result.imports.mergeWith(manifest.type.imports);
1098
1481
  }
1482
+ for (const defaultValue of fieldDefaults.values()) {
1483
+ result.imports.mergeWith(defaultValue.imports);
1484
+ }
1099
1485
  return result;
1100
1486
  }
1487
+ function isOmittedDefaultField(field) {
1488
+ return field.defaultValue != null && field.defaultValueStrategy === "omitted";
1489
+ }
1490
+ function isFieldDiscriminator(field, node) {
1491
+ return (node.discriminators ?? []).some(
1492
+ (discriminator) => discriminator.kind === "fieldDiscriminatorNode" && discriminator.name === field.name
1493
+ );
1494
+ }
1101
1495
 
1102
1496
  // src/fragments/errorPage.ts
1103
1497
  function getErrorPageFragment(programNode, scope) {
@@ -1158,7 +1552,7 @@ function getIndexPageFragment(fileNames, relativePath = "") {
1158
1552
  "// Auto-generated. Do not edit.\n// ignore_for_file: type=lint\n"
1159
1553
  );
1160
1554
  }
1161
- const exports$1 = fileNames.sort().map((f) => {
1555
+ const exports = fileNames.sort().map((f) => {
1162
1556
  const path = relativePath ? `${relativePath}/${f}` : f;
1163
1557
  return `export '${path}';`;
1164
1558
  }).join("\n");
@@ -1166,17 +1560,17 @@ function getIndexPageFragment(fileNames, relativePath = "") {
1166
1560
  `// Auto-generated. Do not edit.
1167
1561
  // ignore_for_file: type=lint
1168
1562
 
1169
- ${exports$1}
1563
+ ${exports}
1170
1564
  `
1171
1565
  );
1172
1566
  }
1173
1567
  function getRootIndexPageFragment(categories) {
1174
- const exports$1 = categories.sort().map((cat) => `export '${cat}/${cat}.dart';`).join("\n");
1568
+ const exports = categories.sort().map((cat) => `export '${cat}/${cat}.dart';`).join("\n");
1175
1569
  return fragmentFromString(
1176
1570
  `// Auto-generated. Do not edit.
1177
1571
  // ignore_for_file: type=lint
1178
1572
 
1179
- ${exports$1}
1573
+ ${exports}
1180
1574
  `
1181
1575
  );
1182
1576
  }
@@ -1186,30 +1580,62 @@ function getInstructionPageFragment(node, scope) {
1186
1580
  const instrFnName = scope.nameApi.instructionFunction(name);
1187
1581
  const parseFnName = scope.nameApi.instructionParseFunction(name);
1188
1582
  const accounts = node.accounts ?? [];
1189
- const args = (node.arguments ?? []).filter(
1190
- (arg) => !isDiscriminatorArg(arg, node)
1191
- );
1192
1583
  const allArgs = node.arguments ?? [];
1584
+ const optionalAccountStrategy = node.optionalAccountStrategy ?? "programId";
1585
+ if (optionalAccountStrategy !== "programId" && optionalAccountStrategy !== "omitted") {
1586
+ throw new Error(
1587
+ `Unsupported optional account strategy on instruction "${node.name}": ${optionalAccountStrategy}`
1588
+ );
1589
+ }
1590
+ const args = allArgs.filter(
1591
+ (arg) => !isDiscriminatorArg(arg, node) && !isOmittedDefaultArgument(arg)
1592
+ );
1193
1593
  const dataClassName = `${typeName}InstructionData`;
1194
- const allArgManifests = allArgs.map((arg) => ({
1594
+ const allArgManifests = (node.arguments ?? []).map((arg) => ({
1195
1595
  arg,
1196
1596
  manifest: visit(arg.type, scope.typeManifestVisitor)
1197
1597
  }));
1598
+ const argDefaultValues = new Map(
1599
+ allArgManifests.flatMap(({ arg, manifest }) => {
1600
+ if (arg.defaultValue == null) return [];
1601
+ const needsRenderedDefault = isOmittedDefaultArgument(arg) || isDiscriminatorArg(arg, node) || arg.defaultValue.kind !== "accountBumpValueNode";
1602
+ if (!needsRenderedDefault) return [];
1603
+ return [
1604
+ [
1605
+ arg,
1606
+ getDartValueFragment(arg.defaultValue, manifest.type.content)
1607
+ ]
1608
+ ];
1609
+ })
1610
+ );
1611
+ for (const arg of allArgs.filter(
1612
+ (candidate) => isDiscriminatorArg(candidate, node)
1613
+ )) {
1614
+ if (!argDefaultValues.has(arg)) {
1615
+ throw new Error(
1616
+ `Field discriminator "${arg.name}" on instruction "${node.name}" must reference a field with a default value.`
1617
+ );
1618
+ }
1619
+ }
1198
1620
  const dataFieldDecls = allArgManifests.map(({ arg, manifest }) => {
1199
1621
  const fieldName = camelCase(arg.name);
1200
1622
  return ` final ${manifest.type.content} ${fieldName};`;
1201
1623
  }).join("\n");
1202
1624
  const dataCtorParams = allArgManifests.map(({ arg, manifest }) => {
1203
1625
  const fieldName = camelCase(arg.name);
1626
+ if (isOmittedDefaultArgument(arg)) return "";
1204
1627
  if (isDiscriminatorArg(arg, node)) {
1205
- return isConstDefaultValue(arg.defaultValue) ? ` this.${fieldName} = ${getDiscriminatorDefault(arg)},` : ` ${manifest.type.content}? ${fieldName},`;
1628
+ return isConstDefaultValue(arg.defaultValue) ? ` this.${fieldName} = ${argDefaultValues.get(arg).content},` : ` ${manifest.type.content}? ${fieldName},`;
1206
1629
  }
1207
1630
  return ` required this.${fieldName},`;
1208
- }).join("\n");
1631
+ }).filter(Boolean).join("\n");
1209
1632
  const dataCtorInitializers = allArgManifests.map(({ arg }) => {
1210
1633
  const fieldName = camelCase(arg.name);
1634
+ if (isOmittedDefaultArgument(arg)) {
1635
+ return ` ${fieldName} = ${argDefaultValues.get(arg).content}`;
1636
+ }
1211
1637
  if (isDiscriminatorArg(arg, node) && !isConstDefaultValue(arg.defaultValue)) {
1212
- return ` ${fieldName} = ${fieldName} ?? ${getDiscriminatorDefault(arg)}`;
1638
+ return ` ${fieldName} = ${fieldName} ?? ${argDefaultValues.get(arg).content}`;
1213
1639
  }
1214
1640
  return null;
1215
1641
  }).filter((value) => value !== null).join(",\n");
@@ -1219,16 +1645,26 @@ function getInstructionPageFragment(node, scope) {
1219
1645
  const decFields = allArgManifests.map(({ arg, manifest }) => {
1220
1646
  return ` ('${arg.name}', ${manifest.decoder.content}),`;
1221
1647
  }).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 }) => {
1648
+ const toMapFields = allArgs.map((arg) => {
1649
+ const value = isOmittedDefaultArgument(arg) || isDiscriminatorArg(arg, node) ? argDefaultValues.get(arg).content : `value.${camelCase(arg.name)}`;
1650
+ return ` '${arg.name}': ${value},`;
1651
+ }).join("\n");
1652
+ const fromMapFields = allArgManifests.filter(({ arg }) => !isOmittedDefaultArgument(arg)).map(({ arg, manifest }) => {
1226
1653
  const typeStr = manifest.type.content;
1227
1654
  const isNullable = typeStr.endsWith("?");
1228
1655
  const accessor = isNullable ? `map['${arg.name}']` : `map['${arg.name}']!`;
1229
1656
  return ` ${camelCase(arg.name)}: ${accessor} as ${typeStr},`;
1230
1657
  }).join("\n");
1231
- const dataCtorKeyword = dataCtorInitializers ? "" : "const ";
1658
+ const dataCtorKeyword = allArgs.every(
1659
+ (arg) => !isOmittedDefaultArgument(arg) || isConstDartValueNode(arg.defaultValue)
1660
+ ) && allArgs.every(
1661
+ (arg) => !isDiscriminatorArg(arg, node) || isConstDefaultValue(arg.defaultValue)
1662
+ ) ? "const " : "";
1663
+ const dataCtorSignature = dataCtorParams ? `${dataCtorKeyword}${dataClassName}({
1664
+ ${dataCtorParams}
1665
+ })${dataCtorInitializers ? ` :
1666
+ ${dataCtorInitializers}` : ""};` : `${dataCtorKeyword}${dataClassName}()${dataCtorInitializers ? ` :
1667
+ ${dataCtorInitializers}` : ""};`;
1232
1668
  const dataEncoderName = `get${typeName}InstructionDataEncoder`;
1233
1669
  const dataDecoderName = `get${typeName}InstructionDataDecoder`;
1234
1670
  const dataCodecName = `get${typeName}InstructionDataCodec`;
@@ -1236,6 +1672,10 @@ function getInstructionPageFragment(node, scope) {
1236
1672
  const accountNames = new Set(accounts.map((a) => camelCase(a.name)));
1237
1673
  const hasProgramAddressCollision = argNames.has("programAddress") || accountNames.has("programAddress");
1238
1674
  const instrProgramParam = hasProgramAddressCollision ? "instructionProgramAddress" : "programAddress";
1675
+ let instructionDataLocal = "instructionData";
1676
+ while (argNames.has(instructionDataLocal) || accountNames.has(instructionDataLocal)) {
1677
+ instructionDataLocal = `${instructionDataLocal}_`;
1678
+ }
1239
1679
  const accountParams = accounts.map((acc) => {
1240
1680
  const fieldName = camelCase(acc.name);
1241
1681
  const isRequired = !(acc.isOptional ?? false);
@@ -1258,20 +1698,33 @@ function getInstructionPageFragment(node, scope) {
1258
1698
  const role = getAccountRole(acc);
1259
1699
  const isOptional = acc.isOptional ?? false;
1260
1700
  if (isOptional) {
1261
- return ` if (${fieldName} != null) AccountMeta(address: ${fieldName}, role: ${role}),`;
1701
+ if (optionalAccountStrategy === "omitted") {
1702
+ return ` if (${fieldName} != null) AccountMeta(address: ${fieldName}, role: ${role}),`;
1703
+ }
1704
+ return ` if (${fieldName} != null) AccountMeta(address: ${fieldName}, role: ${role}) else AccountMeta(address: ${instrProgramParam}, role: AccountRole.readonly),`;
1262
1705
  }
1263
1706
  return ` AccountMeta(address: ${fieldName}, role: ${role}),`;
1264
1707
  }).join("\n");
1265
1708
  const dataConstruction = allArgs.map((arg) => {
1266
1709
  const fieldName = camelCase(arg.name);
1267
- if (isDiscriminatorArg(arg, node)) {
1710
+ if (isOmittedDefaultArgument(arg) || isDiscriminatorArg(arg, node)) {
1268
1711
  return "";
1269
1712
  }
1270
- const typeStr2 = argManifestMap.get(arg)?.type.content;
1271
1713
  const skipDefault = arg.defaultValue?.kind === "accountBumpValueNode";
1272
- return ` ${fieldName}: ${fieldName}${arg.defaultValue != null && !skipDefault ? ` ?? ${getDefaultValue(arg, typeStr2)}` : ""},`;
1714
+ return ` ${fieldName}: ${fieldName}${arg.defaultValue != null && !skipDefault ? ` ?? ${argDefaultValues.get(arg).content}` : ""},`;
1273
1715
  }).filter(Boolean).join("\n");
1274
1716
  const discFragment = getDiscriminatorConstantsFragment(node, scope);
1717
+ const discriminatorValidation = getDiscriminatorValidationFragment(
1718
+ node,
1719
+ scope
1720
+ );
1721
+ const topLevelDecoder = getTopLevelDecoderFragment({
1722
+ typeName: dataClassName,
1723
+ description: `${name} instruction decoder`,
1724
+ discriminatorValidation,
1725
+ fromMapFields,
1726
+ requireExactConsumption: true
1727
+ });
1275
1728
  const parts = [
1276
1729
  fragment`// Auto-generated. Do not edit.
1277
1730
  // ignore_for_file: type=lint
@@ -1295,10 +1748,7 @@ ${use("AccountRole", "solanaInstructions")}`
1295
1748
  parts.push(fragment`
1296
1749
  @immutable
1297
1750
  class ${fragmentFromString(dataClassName)} {
1298
- ${fragmentFromString(dataCtorKeyword)}${fragmentFromString(dataClassName)}({
1299
- ${fragmentFromString(dataCtorParams)}
1300
- })${fragmentFromString(dataCtorInitializers ? ` :
1301
- ${dataCtorInitializers}` : "")};
1751
+ ${fragmentFromString(dataCtorSignature)}
1302
1752
 
1303
1753
  ${fragmentFromString(dataFieldDecls)}
1304
1754
  }`);
@@ -1321,12 +1771,7 @@ Decoder<${fragmentFromString(dataClassName)}> ${fragmentFromString(dataDecoderNa
1321
1771
  ${fragmentFromString(decFields)}
1322
1772
  ]);
1323
1773
 
1324
- return transformDecoder(
1325
- structDecoder,
1326
- (Map<String, Object?> map, Uint8List bytes, int offset) => ${fragmentFromString(dataClassName)}(
1327
- ${fragmentFromString(fromMapFields)}
1328
- ),
1329
- );
1774
+ ${topLevelDecoder}
1330
1775
  }
1331
1776
 
1332
1777
  Codec<${fragmentFromString(dataClassName)}, ${fragmentFromString(dataClassName)}> ${fragmentFromString(dataCodecName)}() {
@@ -1339,7 +1784,7 @@ Instruction ${fragmentFromString(instrFnName)}({
1339
1784
  ${fragmentFromString(accountParams)}
1340
1785
  ${fragmentFromString(argParams)}
1341
1786
  }) {
1342
- final instructionData = ${fragmentFromString(dataClassName)}(
1787
+ final ${instructionDataLocal} = ${fragmentFromString(dataClassName)}(
1343
1788
  ${fragmentFromString(dataConstruction)}
1344
1789
  );
1345
1790
 
@@ -1348,7 +1793,7 @@ ${fragmentFromString(dataConstruction)}
1348
1793
  accounts: [
1349
1794
  ${fragmentFromString(accountMetas)}
1350
1795
  ],
1351
- data: ${fragmentFromString(dataEncoderName)}().encode(instructionData),
1796
+ data: ${fragmentFromString(dataEncoderName)}().encode(${instructionDataLocal}),
1352
1797
  );
1353
1798
  }`);
1354
1799
  parts.push(fragment`
@@ -1362,6 +1807,9 @@ ${fragmentFromString(dataClassName)} ${fragmentFromString(parseFnName)}(Instruct
1362
1807
  result.imports.mergeWith(manifest.decoder.imports);
1363
1808
  result.imports.mergeWith(manifest.type.imports);
1364
1809
  }
1810
+ for (const defaultValue of argDefaultValues.values()) {
1811
+ result.imports.mergeWith(defaultValue.imports);
1812
+ }
1365
1813
  return result;
1366
1814
  }
1367
1815
  function getAccountRole(acc) {
@@ -1378,19 +1826,6 @@ function isDiscriminatorArg(arg, node) {
1378
1826
  (d) => d.kind === "fieldDiscriminatorNode" && d.name === arg.name
1379
1827
  );
1380
1828
  }
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
1829
  function isConstDefaultValue(defaultValue) {
1395
1830
  if (!defaultValue) return false;
1396
1831
  switch (defaultValue.kind) {
@@ -1403,34 +1838,8 @@ function isConstDefaultValue(defaultValue) {
1403
1838
  return false;
1404
1839
  }
1405
1840
  }
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
- }
1841
+ function isOmittedDefaultArgument(arg) {
1842
+ return arg.defaultValue != null && arg.defaultValueStrategy === "omitted";
1434
1843
  }
1435
1844
  function getPdaPageFragment(node, scope) {
1436
1845
  const name = node.name;
@@ -1725,25 +2134,26 @@ function getTypePageFragment(node, scope) {
1725
2134
  scope.nameApi.dataType(name);
1726
2135
  const typeNode = node.type;
1727
2136
  if (typeNode.kind === "enumTypeNode") {
1728
- const isScalar = typeNode.variants.every(
2137
+ const variants = typeNode.variants ?? [];
2138
+ const isScalar = variants.every(
1729
2139
  (v) => v.kind === "enumEmptyVariantTypeNode"
1730
2140
  );
1731
2141
  if (isScalar) {
1732
- return getScalarEnumPageFragment(node, scope);
2142
+ return getScalarEnumPageFragment(node, variants, scope);
1733
2143
  }
1734
- return getDataEnumPageFragment(node, scope);
2144
+ return getDataEnumPageFragment(node, variants, scope);
1735
2145
  }
1736
2146
  if (typeNode.kind === "structTypeNode") {
1737
2147
  return getStructPageFragment(node, scope);
1738
2148
  }
1739
2149
  return getTypeAliasPageFragment(node, scope);
1740
2150
  }
1741
- function getScalarEnumPageFragment(node, scope) {
2151
+ function getScalarEnumPageFragment(node, enumVariants, scope) {
1742
2152
  const name = node.name;
1743
2153
  const typeName = scope.nameApi.dataType(name);
1744
2154
  const enumNode = node.type;
1745
2155
  if (enumNode.kind !== "enumTypeNode") return emptyFragment();
1746
- const variants = enumNode.variants.map((v) => {
2156
+ const variants = enumVariants.map((v) => {
1747
2157
  const variantName = scope.nameApi.enumVariant(v.name);
1748
2158
  return variantName;
1749
2159
  });
@@ -1754,6 +2164,14 @@ function getScalarEnumPageFragment(node, scope) {
1754
2164
  const resolvedSize = resolveNestedTypeNode(enumNode.size);
1755
2165
  const sizeType = resolvedSize.format ?? "u8";
1756
2166
  const sizeCodecName = getNumberCodecName2(sizeType);
2167
+ const isU64 = sizeType === "u64";
2168
+ const encodedVariantIndex = isU64 ? "BigInt.from(value.index)" : "value.index";
2169
+ const decodedVariant = isU64 ? `(BigInt value, Uint8List bytes, int offset) {
2170
+ if (value.isNegative || value >= BigInt.from(${typeName}.values.length)) {
2171
+ throw RangeError('Invalid ${typeName} discriminator: ' + value.toString());
2172
+ }
2173
+ return ${typeName}.values[value.toInt()];
2174
+ }` : `(int value, Uint8List bytes, int offset) => ${typeName}.values[value]`;
1757
2175
  return fragment`// Auto-generated. Do not edit.
1758
2176
  // ignore_for_file: type=lint
1759
2177
 
@@ -1774,14 +2192,14 @@ ${fragmentFromString(variantLines)}
1774
2192
  Encoder<${fragmentFromString(typeName)}> ${fragmentFromString(encoderName)}() {
1775
2193
  return transformEncoder(
1776
2194
  get${fragmentFromString(sizeCodecName)}Encoder(),
1777
- (${fragmentFromString(typeName)} value) => value.index,
2195
+ (${fragmentFromString(typeName)} value) => ${fragmentFromString(encodedVariantIndex)},
1778
2196
  );
1779
2197
  }
1780
2198
 
1781
2199
  Decoder<${fragmentFromString(typeName)}> ${fragmentFromString(decoderName)}() {
1782
2200
  return transformDecoder(
1783
2201
  get${fragmentFromString(sizeCodecName)}Decoder(),
1784
- (int value, Uint8List bytes, int offset) => ${fragmentFromString(typeName)}.values[value],
2202
+ ${fragmentFromString(decodedVariant)},
1785
2203
  );
1786
2204
  }
1787
2205
 
@@ -1789,7 +2207,7 @@ Codec<${fragmentFromString(typeName)}, ${fragmentFromString(typeName)}> ${fragme
1789
2207
  return combineCodec(${fragmentFromString(encoderName)}(), ${fragmentFromString(decoderName)}());
1790
2208
  }`;
1791
2209
  }
1792
- function getDataEnumPageFragment(node, scope) {
2210
+ function getDataEnumPageFragment(node, variants, scope) {
1793
2211
  const name = node.name;
1794
2212
  const typeName = scope.nameApi.dataType(name);
1795
2213
  const enumNode = node.type;
@@ -1799,9 +2217,10 @@ function getDataEnumPageFragment(node, scope) {
1799
2217
  const decoderVariants = [];
1800
2218
  const encodeCases = [];
1801
2219
  const decodeCases = [];
2220
+ let pageHasListEnums = false;
1802
2221
  const allVariantManifests = [];
1803
- for (let i = 0; i < enumNode.variants.length; i++) {
1804
- const variant = enumNode.variants[i];
2222
+ for (let i = 0; i < variants.length; i++) {
2223
+ const variant = variants[i];
1805
2224
  const variantName = pascalCase(variant.name);
1806
2225
  const variantClassName = scope.nameApi.sealedClassVariant(
1807
2226
  name,
@@ -1831,7 +2250,7 @@ function getDataEnumPageFragment(node, scope) {
1831
2250
  decodeCases.push(`case ${i}: return const ${variantClassName}();`);
1832
2251
  } else if (variant.kind === "enumStructVariantTypeNode") {
1833
2252
  const resolvedStruct = resolveNestedTypeNode(variant.struct);
1834
- const fields = resolvedStruct.fields;
2253
+ const fields = resolvedStruct.fields ?? [];
1835
2254
  const fieldManifests = fields.map((f) => ({
1836
2255
  field: f,
1837
2256
  manifest: visit(f.type, scope.typeManifestVisitor)
@@ -1846,10 +2265,14 @@ function getDataEnumPageFragment(node, scope) {
1846
2265
  const ctorSignature = fields.length === 0 ? ` const ${variantClassName}();` : ` const ${variantClassName}({
1847
2266
  ${ctorParams}
1848
2267
  });`;
1849
- const eqChecks = fields.length === 0 ? "true" : fields.map((f) => `${camelCase(f.name)} == other.${camelCase(f.name)}`).join(" &&\n ");
1850
- const hashExpression = getHashExpression(
1851
- fields.map((f) => camelCase(f.name))
1852
- );
2268
+ const eqChecks = fields.length === 0 ? "true" : fields.map((f, index) => {
2269
+ const fieldName = camelCase(f.name);
2270
+ return isListManifest(fieldManifests[index].manifest) ? `_listEquals(${fieldName}, other.${fieldName})` : `${fieldName} == other.${fieldName}`;
2271
+ }).join(" &&\n ");
2272
+ const hashExpression = getValueHashExpression(fields, fieldManifests);
2273
+ if (fieldManifests.some(({ manifest }) => isListManifest(manifest))) {
2274
+ pageHasListEnums = true;
2275
+ }
1853
2276
  const toStringFields = fields.map((f) => `${camelCase(f.name)}: $${camelCase(f.name)}`).join(", ");
1854
2277
  variantClasses.push(`final class ${variantClassName} extends ${typeName} {
1855
2278
  ${ctorSignature}
@@ -1897,7 +2320,7 @@ ${fieldDecls}
1897
2320
  decodeCases.push(`case ${i}: return ${variantClassName}(${fromMapFields.replace(/\n/g, " ").replace(/,$/, "")});`);
1898
2321
  } else if (variant.kind === "enumTupleVariantTypeNode") {
1899
2322
  const resolvedTuple = resolveNestedTypeNode(variant.tuple);
1900
- const items = resolvedTuple.items;
2323
+ const items = resolvedTuple.items ?? [];
1901
2324
  if (items.length === 1) {
1902
2325
  const manifest = visit(items[0], scope.typeManifestVisitor);
1903
2326
  allVariantManifests.push(manifest);
@@ -1955,7 +2378,7 @@ sealed class ${fragmentFromString(typeName)} {
1955
2378
  const ${fragmentFromString(typeName)}();
1956
2379
  }
1957
2380
 
1958
- ${fragmentFromString(variantClasses.join("\n\n"))}
2381
+ ${fragmentFromString(variantClasses.join("\n\n"))}${pageHasListEnums ? LIST_VALUE_HELPERS : ""}
1959
2382
 
1960
2383
  Encoder<${fragmentFromString(typeName)}> ${fragmentFromString(encoderName)}() {
1961
2384
  return transformEncoder<Map<String, Object?>, ${fragmentFromString(typeName)}>(
@@ -1997,7 +2420,8 @@ function getStructPageFragment(node, scope) {
1997
2420
  const typeName = scope.nameApi.dataType(name);
1998
2421
  const structNode = node.type;
1999
2422
  if (structNode.kind !== "structTypeNode") return emptyFragment();
2000
- const fields = structNode.fields;
2423
+ const fields = structNode.fields ?? [];
2424
+ let pageHasListStructs = false;
2001
2425
  const fieldManifests = fields.map((f) => ({
2002
2426
  field: f,
2003
2427
  manifest: visit(f.type, scope.typeManifestVisitor)
@@ -2009,10 +2433,13 @@ function getStructPageFragment(node, scope) {
2009
2433
  const ctorSignature = fields.length === 0 ? ` const ${typeName}();` : ` const ${typeName}({
2010
2434
  ${ctorParams}
2011
2435
  });`;
2012
- const eqChecks = fields.length === 0 ? "true" : fields.map(
2013
- (f) => `${camelCase(f.name)} == other.${camelCase(f.name)}`
2014
- ).join(" &&\n ");
2015
- fields.length === 0 ? "runtimeType" : fields.map((f) => camelCase(f.name)).join(", ");
2436
+ const eqChecks = fields.length === 0 ? "true" : fieldManifests.map(({ field: f, manifest }, index) => {
2437
+ const fieldName = camelCase(f.name);
2438
+ return isListManifest(manifest) ? `_listEquals(${fieldName}, other.${fieldName})` : `${fieldName} == other.${fieldName}`;
2439
+ }).join(" &&\n ");
2440
+ if (fieldManifests.some(({ manifest }) => isListManifest(manifest))) {
2441
+ pageHasListStructs = true;
2442
+ }
2016
2443
  const toStringFields = fields.map(
2017
2444
  (f) => `${camelCase(f.name)}: $${camelCase(f.name)}`
2018
2445
  ).join(", ");
@@ -2062,12 +2489,14 @@ ${fragmentFromString(fieldDecls)}
2062
2489
  ${fragmentFromString(eqChecks)};
2063
2490
 
2064
2491
  @override
2065
- int get hashCode => ${fragmentFromString(getHashExpression(fields.map((f) => camelCase(f.name))))};
2492
+ int get hashCode => ${fragmentFromString(
2493
+ getValueHashExpression(fields, fieldManifests)
2494
+ )};
2066
2495
 
2067
2496
  @override
2068
2497
  String toString() => '${fragmentFromString(typeName)}(${fragmentFromString(toStringFields)})';
2069
2498
  }
2070
-
2499
+ ${pageHasListStructs ? LIST_VALUE_HELPERS : ""}
2071
2500
  Encoder<${fragmentFromString(typeName)}> ${fragmentFromString(encoderName)}() {
2072
2501
  final structEncoder = getStructEncoder(<(String, Encoder<Object?>)>[
2073
2502
  ${fragmentFromString(encFields)}
@@ -2133,6 +2562,56 @@ Codec<${fragmentFromString(typeName)}, ${fragmentFromString(typeName)}> ${fragme
2133
2562
  return combineCodec(${fragmentFromString(encoderName)}(), ${fragmentFromString(decoderName)}());
2134
2563
  }`;
2135
2564
  }
2565
+ function isListManifest(manifest) {
2566
+ return manifest.type.content.startsWith("List<") || manifest.type.content === "Uint8List";
2567
+ }
2568
+ var LIST_VALUE_HELPERS = `
2569
+ bool _listEquals<T>(List<T>? a, List<T>? b) {
2570
+ if (identical(a, b)) return true;
2571
+ if (a == null || b == null) return a == b;
2572
+ if (a.length != b.length) return false;
2573
+ for (var i = 0; i < a.length; i++) {
2574
+ final left = a[i];
2575
+ final right = b[i];
2576
+ if (left is List<Object?> && right is List<Object?>) {
2577
+ if (!_listEquals(left, right)) return false;
2578
+ } else if (left != right) {
2579
+ return false;
2580
+ }
2581
+ }
2582
+ return true;
2583
+ }
2584
+
2585
+ Object? _deepHash(Object? value) {
2586
+ if (value is List<Object?>) {
2587
+ return Object.hashAll(value.map(_deepHash));
2588
+ }
2589
+ return value;
2590
+ }
2591
+
2592
+ int _listHashCode<T>(List<T>? a) {
2593
+ if (a == null) return 0;
2594
+ return Object.hashAll(a.map(_deepHash));
2595
+ }
2596
+ `;
2597
+ function getValueHashExpression(fields, fieldManifests) {
2598
+ const fieldNames = fields.map(
2599
+ (f) => camelCase(f.name)
2600
+ );
2601
+ const hasListFields = fieldManifests.some(
2602
+ ({ manifest }) => isListManifest(manifest)
2603
+ );
2604
+ if (!hasListFields) {
2605
+ return getHashExpression(fieldNames);
2606
+ }
2607
+ if (fieldNames.length === 1) {
2608
+ return `_listHashCode(${fieldNames[0]})`;
2609
+ }
2610
+ const hashArgs = fields.map(
2611
+ (f, index) => isListManifest(fieldManifests[index].manifest) ? `_listHashCode(${camelCase(f.name)})` : camelCase(f.name)
2612
+ );
2613
+ return `Object.hash(${hashArgs.join(", ")})`;
2614
+ }
2136
2615
  function getHashExpression(fields) {
2137
2616
  if (fields.length === 0) return "runtimeType.hashCode";
2138
2617
  if (fields.length === 1) return `${fields[0]}.hashCode`;
@@ -2204,7 +2683,7 @@ function getRenderMapVisitor(options = {}) {
2204
2683
  visitRoot(node, { self }) {
2205
2684
  const programMaps = [
2206
2685
  visit(node.program, self),
2207
- ...node.additionalPrograms.map((p) => visit(p, self))
2686
+ ...(node.additionalPrograms ?? []).map((p) => visit(p, self))
2208
2687
  ];
2209
2688
  const mergedProgramMap = mergeRenderMaps(programMaps);
2210
2689
  const categoryMaps = getSharedCategoryIndexMaps(mergedProgramMap);
@@ -2350,11 +2829,12 @@ function renderVisitor(outputDir, options = {}) {
2350
2829
  dependencyMap
2351
2830
  } = options;
2352
2831
  return rootNodeVisitor((root) => {
2832
+ const normalizedRoot = normalizeRootNode(root);
2353
2833
  if (deleteFolderBeforeRendering && existsSync(outputDir)) {
2354
2834
  deleteDirectory(outputDir);
2355
2835
  }
2356
2836
  const renderMap = visit(
2357
- root,
2837
+ normalizedRoot,
2358
2838
  getRenderMapVisitor({ nameApi, dependencyMap })
2359
2839
  );
2360
2840
  const typePathMap = {};
@@ -2436,6 +2916,6 @@ function cleanFragmentContent(content) {
2436
2916
  // src/index.ts
2437
2917
  var src_default = renderVisitor;
2438
2918
 
2439
- export { DART_EXTERNAL_PACKAGE_MAP, DartImportMap, createDartNameApi, src_default as default, getRenderMapVisitor, getTypeManifestVisitor, renderVisitor };
2919
+ export { DART_EXTERNAL_PACKAGE_MAP, DartImportMap, createDartNameApi, src_default as default, getRenderMapVisitor, getTypeManifestVisitor, normalizeRootNode, renderVisitor };
2440
2920
  //# sourceMappingURL=index.browser.js.map
2441
2921
  //# sourceMappingURL=index.browser.js.map