codama-renderers-dart 0.4.3 → 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 (55) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.browser.cjs +634 -159
  3. package/dist/index.browser.cjs.map +1 -1
  4. package/dist/index.browser.js +635 -161
  5. package/dist/index.browser.js.map +1 -1
  6. package/dist/index.node.cjs +634 -159
  7. package/dist/index.node.cjs.map +1 -1
  8. package/dist/index.node.js +635 -161
  9. package/dist/index.node.js.map +1 -1
  10. package/dist/index.react-native.js +635 -161
  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/programPage.d.ts +1 -1
  15. package/dist/types/fragments/programPage.d.ts.map +1 -1
  16. package/dist/types/fragments/typePage.d.ts.map +1 -1
  17. package/dist/types/index.d.ts +1 -0
  18. package/dist/types/index.d.ts.map +1 -1
  19. package/dist/types/utils/discriminators.d.ts +10 -0
  20. package/dist/types/utils/discriminators.d.ts.map +1 -0
  21. package/dist/types/utils/exactDecoder.d.ts +17 -0
  22. package/dist/types/utils/exactDecoder.d.ts.map +1 -0
  23. package/dist/types/utils/formatCode.d.ts +1 -1
  24. package/dist/types/utils/formatCode.d.ts.map +1 -1
  25. package/dist/types/utils/importMap.d.ts.map +1 -1
  26. package/dist/types/utils/index.d.ts +4 -0
  27. package/dist/types/utils/index.d.ts.map +1 -1
  28. package/dist/types/utils/nameTransformers.d.ts +1 -1
  29. package/dist/types/utils/nameTransformers.d.ts.map +1 -1
  30. package/dist/types/utils/normalizeRootNode.d.ts +11 -0
  31. package/dist/types/utils/normalizeRootNode.d.ts.map +1 -0
  32. package/dist/types/utils/valueNodes.d.ts +16 -0
  33. package/dist/types/utils/valueNodes.d.ts.map +1 -0
  34. package/dist/types/visitors/getTypeManifestVisitor.d.ts +1 -1
  35. package/dist/types/visitors/getTypeManifestVisitor.d.ts.map +1 -1
  36. package/dist/types/visitors/renderVisitor.d.ts.map +1 -1
  37. package/package.json +31 -23
  38. package/readme.md +30 -3
  39. package/src/fragments/accountPage.ts +90 -17
  40. package/src/fragments/instructionPage.ts +109 -72
  41. package/src/fragments/programPage.ts +165 -6
  42. package/src/fragments/typePage.ts +36 -15
  43. package/src/index.ts +1 -0
  44. package/src/utils/discriminators.ts +121 -0
  45. package/src/utils/exactDecoder.ts +82 -0
  46. package/src/utils/formatCode.ts +7 -11
  47. package/src/utils/importMap.ts +16 -3
  48. package/src/utils/index.ts +4 -0
  49. package/src/utils/nameTransformers.ts +3 -2
  50. package/src/utils/normalizeRootNode.ts +76 -0
  51. package/src/utils/valueNodes.ts +87 -0
  52. package/src/utils/wellKnownAddresses.ts +4 -4
  53. package/src/visitors/getRenderMapVisitor.ts +1 -1
  54. package/src/visitors/getTypeManifestVisitor.ts +48 -29
  55. package/src/visitors/renderVisitor.ts +11 -4
@@ -12,13 +12,71 @@ var errors = require('@codama/errors');
12
12
 
13
13
  // src/visitors/renderVisitor.ts
14
14
  function formatDartDirectory(dir) {
15
- try {
16
- child_process.execSync(`dart format --fix "${dir}"`, {
17
- encoding: "utf-8",
18
- timeout: 6e4,
19
- stdio: ["pipe", "pipe", "pipe"]
20
- });
21
- } catch {
15
+ child_process.execSync(`dart format "${dir}"`, {
16
+ encoding: "utf-8",
17
+ timeout: 6e4,
18
+ stdio: ["pipe", "pipe", "pipe"]
19
+ });
20
+ }
21
+
22
+ // src/utils/normalizeRootNode.ts
23
+ function normalizeRootNode(root) {
24
+ return normalizeNode(root);
25
+ }
26
+ function normalizeNode(value) {
27
+ if (Array.isArray(value)) {
28
+ return value.map(normalizeNode);
29
+ }
30
+ if (!isRecord(value)) {
31
+ return value;
32
+ }
33
+ const normalized = Object.fromEntries(
34
+ Object.entries(value).map(([key, child]) => [key, normalizeNode(child)])
35
+ );
36
+ switch (normalized.kind) {
37
+ case "rootNode":
38
+ setEmptyCollection(normalized, "additionalPrograms");
39
+ break;
40
+ case "programNode":
41
+ setEmptyCollection(normalized, "accounts");
42
+ setEmptyCollection(normalized, "constants");
43
+ setEmptyCollection(normalized, "definedTypes");
44
+ setEmptyCollection(normalized, "errors");
45
+ setEmptyCollection(normalized, "events");
46
+ setEmptyCollection(normalized, "instructions");
47
+ setEmptyCollection(normalized, "pdas");
48
+ break;
49
+ case "instructionNode":
50
+ setEmptyCollection(normalized, "accounts");
51
+ setEmptyCollection(normalized, "arguments");
52
+ break;
53
+ case "pdaNode":
54
+ setEmptyCollection(normalized, "seeds");
55
+ break;
56
+ case "structTypeNode":
57
+ setEmptyCollection(normalized, "fields");
58
+ break;
59
+ case "enumTypeNode":
60
+ setEmptyCollection(normalized, "variants");
61
+ break;
62
+ case "tupleTypeNode":
63
+ setEmptyCollection(normalized, "items");
64
+ break;
65
+ case "hiddenPrefixTypeNode":
66
+ setEmptyCollection(normalized, "prefix");
67
+ break;
68
+ case "hiddenSuffixTypeNode":
69
+ setEmptyCollection(normalized, "suffix");
70
+ break;
71
+ }
72
+ return normalized;
73
+ }
74
+ function isRecord(value) {
75
+ return typeof value === "object" && value !== null;
76
+ }
77
+ function setEmptyCollection(record, key) {
78
+ if (record[key] === void 0) {
79
+ record[key] = [];
22
80
  }
23
81
  }
24
82
 
@@ -50,7 +108,7 @@ function createDartNameApi() {
50
108
  errorConstant: (programName, errorName) => `${camelCase(programName)}Error${pascalCase(errorName)}`,
51
109
  errorMessageFunction: (name) => `get${pascalCase(name)}ErrorMessage`,
52
110
  enumVariant: (name) => camelCase(name),
53
- sealedClassVariant: (_parentName, variantName) => pascalCase(variantName),
111
+ sealedClassVariant: (parentName, variantName) => `${pascalCase(parentName)}${pascalCase(variantName)}`,
54
112
  isTypeFunction: (name) => `is${pascalCase(name)}`,
55
113
  fileName: (name) => snakeCase(name),
56
114
  discriminatorConstant: (name) => `${camelCase(name)}Discriminator`
@@ -124,11 +182,16 @@ var DartImportMap = class {
124
182
  const uris = /* @__PURE__ */ new Set();
125
183
  for (const module of this._imports) {
126
184
  if (module in internalMap) {
127
- uris.add(internalMap[module]);
185
+ const uri = internalMap[module];
186
+ if (uri) {
187
+ uris.add(uri);
188
+ }
128
189
  } else if (module in DART_EXTERNAL_PACKAGE_MAP) {
129
190
  uris.add(DART_EXTERNAL_PACKAGE_MAP[module]);
130
- } else {
191
+ } else if (isRawDartImportUri(module)) {
131
192
  uris.add(module);
193
+ } else {
194
+ throw new Error(`Unresolved Dart import module "${module}"`);
132
195
  }
133
196
  }
134
197
  return [...uris].sort((a, b) => {
@@ -157,6 +220,9 @@ var DartImportMap = class {
157
220
  return lines.join("\n");
158
221
  }
159
222
  };
223
+ function isRawDartImportUri(module) {
224
+ return module.startsWith("dart:") || module.startsWith("package:") || module.endsWith(".dart") && !module.includes(":");
225
+ }
160
226
 
161
227
  // src/utils/fragment.ts
162
228
  function fragment(strings, ...values) {
@@ -200,17 +266,6 @@ function use(typeName, module) {
200
266
  return { content: typeName, imports };
201
267
  }
202
268
 
203
- // src/utils/typeManifest.ts
204
- function emptyTypeManifest() {
205
- return {
206
- type: emptyFragment(),
207
- encoder: emptyFragment(),
208
- decoder: emptyFragment(),
209
- value: emptyFragment(),
210
- isEnum: false
211
- };
212
- }
213
-
214
269
  // src/utils/wellKnownAddresses.ts
215
270
  var WELL_KNOWN_ADDRESSES = /* @__PURE__ */ new Map([
216
271
  // Agave / Solana native programs
@@ -248,10 +303,10 @@ var WELL_KNOWN_ADDRESSES = /* @__PURE__ */ new Map([
248
303
  ["Sysvar1111111111111111111111111111111111111", "sysvarOwnerAddress"],
249
304
  // SPL programs
250
305
  ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "tokenProgramAddress"],
251
- ["TokenzQdBNb4qyze1S1U9AHB8MGXmNK1REkTPT5Z3Y", "token2022ProgramAddress"],
252
- ["ATokenGPvbdGVxr1b2hvZbsiqW5xWH25ef7s3c8BnQKu", "associatedTokenProgramAddress"],
253
- ["Memo1UhkJRfRhVq1sR7Y7Nto1s3J2mXTT3L6Wk4K2m6", "memoProgramAddress"],
254
- ["MemoSq4gqABXKKYWVqKkV3m3kb1cTR7F2UMy9r6kFNK", "memoLegacyProgramAddress"],
306
+ ["TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", "token2022ProgramAddress"],
307
+ ["ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", "associatedTokenProgramAddress"],
308
+ ["MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr", "memoProgramAddress"],
309
+ ["Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo", "memoLegacyProgramAddress"],
255
310
  // Metaplex programs
256
311
  ["metaqbxxU9qX8rZXkmUJ7haCrXXz2W1PSUq1Rn3E1od", "tokenMetadataProgramAddress"],
257
312
  ["BGUMAp9V1mQ2KV8t34L5u3gZsXYZaYDBdKkc1xYk1qA1", "mplBubblegumProgramAddress"],
@@ -265,6 +320,207 @@ var WELL_KNOWN_ADDRESSES = /* @__PURE__ */ new Map([
265
320
  ["Es9vMFrzaCERm6YKXx6bqKr1Rmd9b5v3J8rM5e4rfF4", "usdtAddress"]
266
321
  ]);
267
322
 
323
+ // src/utils/valueNodes.ts
324
+ function getDartValueFragment(value, dartType) {
325
+ switch (value.kind) {
326
+ case "numberValueNode":
327
+ return fragmentFromString(
328
+ dartType === "BigInt" ? `BigInt.from(${value.number})` : String(value.number)
329
+ );
330
+ case "booleanValueNode":
331
+ return fragmentFromString(String(value.boolean));
332
+ case "stringValueNode":
333
+ return fragmentFromString(toDartStringLiteral(value.string));
334
+ case "noneValueNode":
335
+ return fragmentFromString("null");
336
+ case "bytesValueNode": {
337
+ if (value.encoding !== "base16") {
338
+ throw new Error(
339
+ `Unsupported deterministic bytes encoding: ${value.encoding}`
340
+ );
341
+ }
342
+ const clean = value.data.replace(/^0x/, "");
343
+ if (clean.length % 2 !== 0 || !/^[0-9a-f]*$/i.test(clean)) {
344
+ throw new Error(`Invalid hexadecimal bytes value: ${value.data}`);
345
+ }
346
+ const bytes = clean.match(/.{2}/g)?.map((byte) => `0x${byte}`) ?? [];
347
+ return fragment`${use("Uint8List", "dartTypedData")}.fromList([${fragmentFromString(bytes.join(", "))}])`;
348
+ }
349
+ case "publicKeyValueNode": {
350
+ const wellKnownName = WELL_KNOWN_ADDRESSES.get(value.publicKey);
351
+ return wellKnownName ? fragment`${use(wellKnownName, "solanaAddresses")}` : fragment`${use("Address", "solanaAddresses")}(${fragmentFromString(toDartStringLiteral(value.publicKey))})`;
352
+ }
353
+ default:
354
+ throw new Error(
355
+ `Unsupported deterministic Dart value node: ${value.kind}`
356
+ );
357
+ }
358
+ }
359
+ function isConstDartValueNode(value) {
360
+ switch (value.kind) {
361
+ case "numberValueNode":
362
+ case "booleanValueNode":
363
+ case "stringValueNode":
364
+ case "noneValueNode":
365
+ case "publicKeyValueNode":
366
+ return true;
367
+ default:
368
+ return false;
369
+ }
370
+ }
371
+ function toDartStringLiteral(value) {
372
+ return `'${value.replaceAll("\\", "\\\\").replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t").replaceAll("\b", "\\b").replaceAll("\f", "\\f").replaceAll("$", "\\$").replaceAll("'", "\\'")}'`;
373
+ }
374
+
375
+ // src/utils/discriminators.ts
376
+ function getDiscriminatorValidationFragment(node, scope) {
377
+ const discriminators = node.discriminators ?? [];
378
+ if (discriminators.length === 0) return emptyFragment();
379
+ const fields = getDiscriminatorFields(node);
380
+ const fragments = discriminators.map((discriminator) => {
381
+ switch (discriminator.kind) {
382
+ case "constantDiscriminatorNode":
383
+ return getConstantValidationFragment(
384
+ discriminator.constant,
385
+ discriminator.offset,
386
+ scope
387
+ );
388
+ case "fieldDiscriminatorNode": {
389
+ const field = fields.find(
390
+ (candidate) => candidate.name === discriminator.name
391
+ );
392
+ if (field?.defaultValue == null) {
393
+ throw new Error(
394
+ `Field discriminator "${discriminator.name}" on ${node.kind} "${node.name}" must reference a field with a default value.`
395
+ );
396
+ }
397
+ return getConstantValidationFragment(
398
+ {
399
+ type: field.type,
400
+ value: field.defaultValue
401
+ },
402
+ discriminator.offset,
403
+ scope
404
+ );
405
+ }
406
+ case "sizeDiscriminatorNode": {
407
+ if (!Number.isSafeInteger(discriminator.size) || discriminator.size < 0) {
408
+ throw new Error(
409
+ `Discriminator size must be a non-negative safe integer, got ${discriminator.size}.`
410
+ );
411
+ }
412
+ return fragment`if (bytes.length - offset != ${fragmentFromString(String(discriminator.size))}) {
413
+ throw ${use("SolanaError", "solanaErrors")}(
414
+ ${use("SolanaErrorCode", "solanaErrors")}.codecsInvalidByteLength,
415
+ {
416
+ 'codecDescription': '${fragmentFromString(node.name)} discriminator',
417
+ 'expected': ${fragmentFromString(String(discriminator.size))},
418
+ 'bytesLength': bytes.length - offset,
419
+ },
420
+ );
421
+ }`;
422
+ }
423
+ }
424
+ });
425
+ return mergeFragments(fragments, (contents) => contents.join("\n"));
426
+ }
427
+ function getConstantValidationFragment(constant, discriminatorOffset, scope) {
428
+ if (!Number.isSafeInteger(discriminatorOffset) || discriminatorOffset < 0) {
429
+ throw new Error(
430
+ `Discriminator offset must be a non-negative safe integer, got ${discriminatorOffset}.`
431
+ );
432
+ }
433
+ const manifest = visitorsCore.visit(constant.type, scope.typeManifestVisitor);
434
+ const value = getDartValueFragment(
435
+ constant.value,
436
+ manifest.type.content
437
+ );
438
+ return fragment`${use("getConstantDecoder", "solanaCodecsDataStructures")}(
439
+ ${manifest.encoder}.encode(${value}),
440
+ ).read(bytes, offset + ${fragmentFromString(String(discriminatorOffset))});`;
441
+ }
442
+ function getDiscriminatorFields(node) {
443
+ if (node.kind === "accountNode") {
444
+ return nodes.resolveNestedTypeNode(node.data).fields ?? [];
445
+ }
446
+ return (node.arguments ?? []).map(
447
+ (argument) => ({
448
+ ...argument,
449
+ kind: "structFieldTypeNode"
450
+ })
451
+ );
452
+ }
453
+
454
+ // src/utils/exactDecoder.ts
455
+ function getTopLevelDecoderFragment({
456
+ typeName,
457
+ description,
458
+ discriminatorValidation,
459
+ fromMapFields,
460
+ requireExactConsumption
461
+ }) {
462
+ const validation = discriminatorValidation.content ? ` ${discriminatorValidation.content.replaceAll("\n", "\n ")}
463
+ ` : "";
464
+ const consumptionCheck = requireExactConsumption ? ` if (newOffset != bytes.length) {
465
+ throwInvalidByteLength(newOffset - offset, bytes.length - offset);
466
+ }
467
+ ` : "";
468
+ const fixedSizeCheck = requireExactConsumption ? "bytesLength != structDecoder.fixedSize" : "bytesLength < structDecoder.fixedSize";
469
+ const result = fragment` Never throwInvalidByteLength(int expected, int bytesLength) {
470
+ throw ${use("SolanaError", "solanaErrors")}(
471
+ ${use("SolanaErrorCode", "solanaErrors")}.codecsInvalidByteLength,
472
+ {
473
+ 'codecDescription': '${fragmentFromString(description)}',
474
+ 'expected': expected,
475
+ 'bytesLength': bytesLength,
476
+ },
477
+ );
478
+ }
479
+
480
+ (${fragmentFromString(typeName)}, int) readTopLevel(Uint8List bytes, int offset) {
481
+ ${fragmentFromString(validation)} final (map, newOffset) = structDecoder.read(bytes, offset);
482
+ ${fragmentFromString(consumptionCheck)}
483
+ return (
484
+ ${fragmentFromString(typeName)}(
485
+ ${fragmentFromString(fromMapFields)}
486
+ ),
487
+ newOffset,
488
+ );
489
+ }
490
+
491
+ return switch (structDecoder) {
492
+ ${use("FixedSizeDecoder", "solanaCodecsCore")}<Map<String, Object?>>() =>
493
+ ${use("FixedSizeDecoder", "solanaCodecsCore")}<${fragmentFromString(typeName)}>(
494
+ fixedSize: structDecoder.fixedSize,
495
+ read: (bytes, offset) {
496
+ final bytesLength = bytes.length - offset;
497
+ if (${fragmentFromString(fixedSizeCheck)}) {
498
+ throwInvalidByteLength(structDecoder.fixedSize, bytesLength);
499
+ }
500
+ return readTopLevel(bytes, offset);
501
+ },
502
+ ),
503
+ ${use("VariableSizeDecoder", "solanaCodecsCore")}<Map<String, Object?>>() =>
504
+ ${use("VariableSizeDecoder", "solanaCodecsCore")}<${fragmentFromString(typeName)}>(
505
+ read: readTopLevel,
506
+ maxSize: structDecoder.maxSize,
507
+ ),
508
+ };`;
509
+ result.imports.mergeWith(discriminatorValidation.imports);
510
+ return result;
511
+ }
512
+
513
+ // src/utils/typeManifest.ts
514
+ function emptyTypeManifest() {
515
+ return {
516
+ type: emptyFragment(),
517
+ encoder: emptyFragment(),
518
+ decoder: emptyFragment(),
519
+ value: emptyFragment(),
520
+ isEnum: false
521
+ };
522
+ }
523
+
268
524
  // src/visitors/getTypeManifestVisitor.ts
269
525
  function getTypeManifestVisitor(input) {
270
526
  const { nameApi, linkables, nonScalarEnums = [] } = input;
@@ -406,17 +662,23 @@ function getTypeManifestVisitor(input) {
406
662
  },
407
663
  visitArrayType(node, { self }) {
408
664
  const itemManifest = visitorsCore.visit(node.item, self);
409
- const sizeExpr = getArraySizeExpression(node);
665
+ const encoderSizeExpr = getArraySizeExpression(node, "Encoder");
666
+ const decoderSizeExpr = getArraySizeExpression(node, "Decoder");
667
+ const itemType = fragmentFromString(itemManifest.type.content);
668
+ const itemEncoder = fragment`${use(
669
+ "transformEncoder",
670
+ "solanaCodecsCore"
671
+ )}(${itemManifest.encoder}, (${itemType} value) => value)`;
410
672
  return {
411
673
  type: fragment`List<${itemManifest.type}>`,
412
674
  encoder: fragment`${use(
413
675
  "getArrayEncoder",
414
676
  "solanaCodecsDataStructures"
415
- )}(${itemManifest.encoder}${sizeExpr})`,
677
+ )}<${itemType}>(${itemEncoder}${encoderSizeExpr})`,
416
678
  decoder: fragment`${use(
417
679
  "getArrayDecoder",
418
680
  "solanaCodecsDataStructures"
419
- )}(${itemManifest.decoder}${sizeExpr})`,
681
+ )}(${itemManifest.decoder}${decoderSizeExpr})`,
420
682
  value: emptyTypeManifest().value,
421
683
  isEnum: false
422
684
  };
@@ -424,40 +686,42 @@ function getTypeManifestVisitor(input) {
424
686
  visitMapType(node, { self }) {
425
687
  const keyManifest = visitorsCore.visit(node.key, self);
426
688
  const valueManifest = visitorsCore.visit(node.value, self);
427
- const sizeExpr = getMapSizeExpression(node);
689
+ const encoderSizeExpr = getMapSizeExpression(node, "Encoder");
690
+ const decoderSizeExpr = getMapSizeExpression(node, "Decoder");
428
691
  return {
429
692
  type: fragment`Map<${keyManifest.type}, ${valueManifest.type}>`,
430
693
  encoder: fragment`${use(
431
694
  "getMapEncoder",
432
695
  "solanaCodecsDataStructures"
433
- )}(${keyManifest.encoder}, ${valueManifest.encoder}${sizeExpr})`,
696
+ )}(${keyManifest.encoder}, ${valueManifest.encoder}${encoderSizeExpr})`,
434
697
  decoder: fragment`${use(
435
698
  "getMapDecoder",
436
699
  "solanaCodecsDataStructures"
437
- )}(${keyManifest.decoder}, ${valueManifest.decoder}${sizeExpr})`,
700
+ )}(${keyManifest.decoder}, ${valueManifest.decoder}${decoderSizeExpr})`,
438
701
  value: emptyTypeManifest().value,
439
702
  isEnum: false
440
703
  };
441
704
  },
442
705
  visitSetType(node, { self }) {
443
706
  const itemManifest = visitorsCore.visit(node.item, self);
444
- const sizeExpr = getSetSizeExpression(node);
707
+ const encoderSizeExpr = getSetSizeExpression(node, "Encoder");
708
+ const decoderSizeExpr = getSetSizeExpression(node, "Decoder");
445
709
  return {
446
710
  type: fragment`Set<${itemManifest.type}>`,
447
711
  encoder: fragment`${use(
448
712
  "getSetEncoder",
449
713
  "solanaCodecsDataStructures"
450
- )}(${itemManifest.encoder}${sizeExpr})`,
714
+ )}(${itemManifest.encoder}${encoderSizeExpr})`,
451
715
  decoder: fragment`${use(
452
716
  "getSetDecoder",
453
717
  "solanaCodecsDataStructures"
454
- )}(${itemManifest.decoder}${sizeExpr})`,
718
+ )}(${itemManifest.decoder}${decoderSizeExpr})`,
455
719
  value: emptyTypeManifest().value,
456
720
  isEnum: false
457
721
  };
458
722
  },
459
723
  visitTupleType(node, { self }) {
460
- const itemManifests = node.items.map((item) => visitorsCore.visit(item, self));
724
+ const itemManifests = (node.items ?? []).map((item) => visitorsCore.visit(item, self));
461
725
  const typeList = itemManifests.map((m) => m.type.content).join(", ");
462
726
  const encoderList = itemManifests.map((m) => m.encoder);
463
727
  const decoderList = itemManifests.map((m) => m.decoder);
@@ -490,8 +754,9 @@ function getTypeManifestVisitor(input) {
490
754
  };
491
755
  },
492
756
  visitStructType(node, { self }) {
493
- const fields = node.fields.map((f) => visitorsCore.visit(f, self));
494
- const fieldNames = node.fields.map((f) => f.name);
757
+ const nodeFields = node.fields ?? [];
758
+ const fields = nodeFields.map((f) => visitorsCore.visit(f, self));
759
+ const fieldNames = nodeFields.map((f) => f.name);
495
760
  const typeStr = "Map<String, Object?>";
496
761
  const encoderFields = fields.map((f, i) => `('${fieldNames[i]}', ${f.encoder.content})`).join(", ");
497
762
  const encoderFrag = fragment`${use(
@@ -521,7 +786,7 @@ function getTypeManifestVisitor(input) {
521
786
  return visitorsCore.visit(node.type, self);
522
787
  },
523
788
  visitEnumType(node, { self }) {
524
- const isScalar = node.variants.every(
789
+ const isScalar = (node.variants ?? []).every(
525
790
  (v) => v.kind === "enumEmptyVariantTypeNode"
526
791
  );
527
792
  if (isScalar) {
@@ -613,7 +878,7 @@ function getTypeManifestVisitor(input) {
613
878
  encoder: fragment`${use(
614
879
  "fixEncoderSize",
615
880
  "solanaCodecsCore"
616
- )}(${innerManifest.encoder}, ${fragmentFromString(String(node.size))})`,
881
+ )}(${innerManifest.encoder}, ${fragmentFromString(String(node.size))}, allowTruncation: false)`,
617
882
  decoder: fragment`${use(
618
883
  "fixDecoderSize",
619
884
  "solanaCodecsCore"
@@ -627,9 +892,10 @@ function getTypeManifestVisitor(input) {
627
892
  const bigIntFormats = /* @__PURE__ */ new Set(["u64", "u128", "i64", "i128"]);
628
893
  let prefixManifest;
629
894
  if (node.prefix.kind === "numberTypeNode" && bigIntFormats.has(node.prefix.format)) {
895
+ const codecName = getNumberCodecName(node.prefix.format);
630
896
  prefixManifest = {
631
- encoder: fragment`${use("getU32Encoder", "solanaCodecsNumbers")}()`,
632
- decoder: fragment`${use("getU32Decoder", "solanaCodecsNumbers")}()`
897
+ encoder: fragment`transformEncoder(${use(`get${codecName}Encoder`, "solanaCodecsNumbers")}(), (size) => BigInt.from(size))`,
898
+ decoder: fragment`transformDecoder(${use(`get${codecName}Decoder`, "solanaCodecsNumbers")}(), (size, _, __) => size.toInt())`
633
899
  };
634
900
  } else {
635
901
  prefixManifest = visitorsCore.visit(node.prefix, self);
@@ -650,7 +916,7 @@ function getTypeManifestVisitor(input) {
650
916
  },
651
917
  visitHiddenPrefixType(node, { self }) {
652
918
  const innerManifest = visitorsCore.visit(node.type, self);
653
- const prefixes = node.prefix.map((p) => getHiddenAffixManifest(p, self));
919
+ const prefixes = (node.prefix ?? []).map((p) => getHiddenAffixManifest(p, self));
654
920
  const prefixEncoders = prefixes.map((p) => p.encoder.content).join(", ");
655
921
  const prefixDecoders = prefixes.map((p) => p.decoder.content).join(", ");
656
922
  const encoderFrag = fragment`${use(
@@ -677,7 +943,7 @@ function getTypeManifestVisitor(input) {
677
943
  },
678
944
  visitHiddenSuffixType(node, { self }) {
679
945
  const innerManifest = visitorsCore.visit(node.type, self);
680
- const suffixes = node.suffix.map((s) => getHiddenAffixManifest(s, self));
946
+ const suffixes = (node.suffix ?? []).map((s) => getHiddenAffixManifest(s, self));
681
947
  const suffixEncoders = suffixes.map((s) => s.encoder.content).join(", ");
682
948
  const suffixDecoders = suffixes.map((s) => s.decoder.content).join(", ");
683
949
  const encoderFrag = fragment`${use(
@@ -816,7 +1082,7 @@ function getNumberCodecExpression(format, codecType) {
816
1082
  const name = getNumberCodecName(format);
817
1083
  return `get${name}${codecType}()`;
818
1084
  }
819
- function getArraySizeExpression(node) {
1085
+ function getArraySizeExpression(node, codecType) {
820
1086
  if (!("count" in node) || !node.count) return "";
821
1087
  const count = node.count;
822
1088
  switch (count.kind) {
@@ -829,13 +1095,13 @@ function getArraySizeExpression(node) {
829
1095
  if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
830
1096
  return "";
831
1097
  }
832
- return `, size: PrefixedArraySize(get${getNumberCodecName(resolvedPrefix.format)}Encoder())`;
1098
+ return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix.format, codecType)})`;
833
1099
  }
834
1100
  default:
835
1101
  return "";
836
1102
  }
837
1103
  }
838
- function getMapSizeExpression(node) {
1104
+ function getMapSizeExpression(node, codecType) {
839
1105
  if (!("count" in node) || !node.count) return "";
840
1106
  const count = node.count;
841
1107
  switch (count.kind) {
@@ -848,13 +1114,13 @@ function getMapSizeExpression(node) {
848
1114
  if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
849
1115
  return "";
850
1116
  }
851
- return `, size: PrefixedArraySize(get${getNumberCodecName(resolvedPrefix.format)}Encoder())`;
1117
+ return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix.format, codecType)})`;
852
1118
  }
853
1119
  default:
854
1120
  return "";
855
1121
  }
856
1122
  }
857
- function getSetSizeExpression(node) {
1123
+ function getSetSizeExpression(node, codecType) {
858
1124
  if (!("count" in node) || !node.count) return "";
859
1125
  const count = node.count;
860
1126
  switch (count.kind) {
@@ -867,7 +1133,7 @@ function getSetSizeExpression(node) {
867
1133
  if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
868
1134
  return "";
869
1135
  }
870
- return `, size: PrefixedArraySize(get${getNumberCodecName(resolvedPrefix.format)}Encoder())`;
1136
+ return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix.format, codecType)})`;
871
1137
  }
872
1138
  default:
873
1139
  return "";
@@ -964,19 +1230,52 @@ function hexToBytes(hex) {
964
1230
  function getAccountPageFragment(node, scope) {
965
1231
  const name = node.name;
966
1232
  const typeName = scope.nameApi.dataType(name);
967
- const dataNode = nodes.resolveNestedTypeNode(node.data);
968
- const fields = dataNode.fields;
1233
+ const fields = nodes.resolveNestedTypeNode(node.data).fields ?? [];
969
1234
  const fieldManifests = fields.map((f) => ({
970
1235
  field: f,
971
1236
  manifest: visitorsCore.visit(f.type, scope.typeManifestVisitor)
972
1237
  }));
1238
+ const omittedFields = fields.filter(isOmittedDefaultField);
1239
+ const constructorFields = fields.filter(
1240
+ (field) => !isOmittedDefaultField(field)
1241
+ );
1242
+ const forcedFields = /* @__PURE__ */ new Set([
1243
+ ...omittedFields,
1244
+ ...fields.filter((field) => isFieldDiscriminator(field, node))
1245
+ ]);
1246
+ const fieldDefaults = new Map(
1247
+ fieldManifests.flatMap(({ field, manifest }) => {
1248
+ if (!forcedFields.has(field) || field.defaultValue == null) return [];
1249
+ return [
1250
+ [
1251
+ field,
1252
+ getDartValueFragment(field.defaultValue, manifest.type.content)
1253
+ ]
1254
+ ];
1255
+ })
1256
+ );
1257
+ for (const field of forcedFields) {
1258
+ if (!fieldDefaults.has(field)) {
1259
+ throw new Error(
1260
+ `Forced field "${field.name}" on account "${node.name}" must have a deterministic default value.`
1261
+ );
1262
+ }
1263
+ }
973
1264
  const fieldDecls = fieldManifests.map(({ field: f, manifest }) => {
974
1265
  return ` final ${manifest.type.content} ${camelCase(f.name)};`;
975
1266
  }).join("\n");
976
- const ctorParams = fields.map((f) => ` required this.${camelCase(f.name)},`).join("\n");
977
- const ctorSignature = fields.length === 0 ? `const ${typeName}();` : `const ${typeName}({
1267
+ const ctorParams = constructorFields.map((f) => ` required this.${camelCase(f.name)},`).join("\n");
1268
+ const ctorInitializers = omittedFields.map((field) => {
1269
+ const fieldName = camelCase(field.name);
1270
+ return `${fieldName} = ${fieldDefaults.get(field).content}`;
1271
+ }).join(",\n ");
1272
+ const ctorKeyword = omittedFields.every(
1273
+ (field) => isConstDartValueNode(field.defaultValue)
1274
+ ) ? "const " : "";
1275
+ const ctorSignature = constructorFields.length === 0 ? `${ctorKeyword}${typeName}()${ctorInitializers ? ` : ${ctorInitializers}` : ""};` : `${ctorKeyword}${typeName}({
978
1276
  ${ctorParams}
979
- });`;
1277
+ })${ctorInitializers ? ` :
1278
+ ${ctorInitializers}` : ""};`;
980
1279
  const eqChecks = fields.length === 0 ? "true" : fields.map(
981
1280
  (f) => `${camelCase(f.name)} == other.${camelCase(f.name)}`
982
1281
  ).join(" &&\n ");
@@ -991,10 +1290,11 @@ ${ctorParams}
991
1290
  const decFields = fieldManifests.map(({ field: f, manifest }) => {
992
1291
  return ` ('${f.name}', ${manifest.decoder.content}),`;
993
1292
  }).join("\n");
994
- const toMapFields = fields.map(
995
- (f) => ` '${f.name}': value.${camelCase(f.name)},`
996
- ).join("\n");
997
- const fromMapFields = fieldManifests.map(({ field: f, manifest }) => {
1293
+ const toMapFields = fields.map((f) => {
1294
+ const value = forcedFields.has(f) ? fieldDefaults.get(f).content : `value.${camelCase(f.name)}`;
1295
+ return ` '${f.name}': ${value},`;
1296
+ }).join("\n");
1297
+ const fromMapFields = fieldManifests.filter(({ field }) => !isOmittedDefaultField(field)).map(({ field: f, manifest }) => {
998
1298
  const typeStr = manifest.type.content;
999
1299
  const isNullable = typeStr.endsWith("?");
1000
1300
  const accessor = isNullable ? `map['${f.name}']` : `map['${f.name}']!`;
@@ -1008,6 +1308,19 @@ ${ctorParams}
1008
1308
  /// The size of the [${fragmentFromString(typeName)}] account data in bytes.
1009
1309
  const int ${fragmentFromString(scope.nameApi.accountSizeConstant(name))} = ${fragmentFromString(String(node.size))};` : emptyFragment();
1010
1310
  const discFragment = getDiscriminatorConstantsFragment(node, scope);
1311
+ const discriminatorValidation = getDiscriminatorValidationFragment(
1312
+ node,
1313
+ scope
1314
+ );
1315
+ const topLevelDecoder = getTopLevelDecoderFragment({
1316
+ typeName,
1317
+ description: `${name} account decoder`,
1318
+ discriminatorValidation,
1319
+ fromMapFields,
1320
+ requireExactConsumption: (node.discriminators ?? []).some(
1321
+ (discriminator) => discriminator.kind === "sizeDiscriminatorNode"
1322
+ )
1323
+ });
1011
1324
  const parts = [
1012
1325
  fragment`// Auto-generated. Do not edit.
1013
1326
  // ignore_for_file: type=lint
@@ -1067,12 +1380,7 @@ Decoder<${fragmentFromString(typeName)}> ${fragmentFromString(decoderName)}() {
1067
1380
  ${fragmentFromString(decFields)}
1068
1381
  ]);
1069
1382
 
1070
- return transformDecoder(
1071
- structDecoder,
1072
- (Map<String, Object?> map, Uint8List bytes, int offset) => ${fragmentFromString(typeName)}(
1073
- ${fragmentFromString(fromMapFields)}
1074
- ),
1075
- );
1383
+ ${topLevelDecoder}
1076
1384
  }
1077
1385
 
1078
1386
  Codec<${fragmentFromString(typeName)}, ${fragmentFromString(typeName)}> ${fragmentFromString(codecName)}() {
@@ -1088,8 +1396,19 @@ Account<${fragmentFromString(typeName)}> ${fragmentFromString(decodeFnName)}(Enc
1088
1396
  result.imports.mergeWith(manifest.decoder.imports);
1089
1397
  result.imports.mergeWith(manifest.type.imports);
1090
1398
  }
1399
+ for (const defaultValue of fieldDefaults.values()) {
1400
+ result.imports.mergeWith(defaultValue.imports);
1401
+ }
1091
1402
  return result;
1092
1403
  }
1404
+ function isOmittedDefaultField(field) {
1405
+ return field.defaultValue != null && field.defaultValueStrategy === "omitted";
1406
+ }
1407
+ function isFieldDiscriminator(field, node) {
1408
+ return (node.discriminators ?? []).some(
1409
+ (discriminator) => discriminator.kind === "fieldDiscriminatorNode" && discriminator.name === field.name
1410
+ );
1411
+ }
1093
1412
 
1094
1413
  // src/fragments/errorPage.ts
1095
1414
  function getErrorPageFragment(programNode, scope) {
@@ -1150,7 +1469,7 @@ function getIndexPageFragment(fileNames, relativePath = "") {
1150
1469
  "// Auto-generated. Do not edit.\n// ignore_for_file: type=lint\n"
1151
1470
  );
1152
1471
  }
1153
- const exports$1 = fileNames.sort().map((f) => {
1472
+ const exports = fileNames.sort().map((f) => {
1154
1473
  const path = relativePath ? `${relativePath}/${f}` : f;
1155
1474
  return `export '${path}';`;
1156
1475
  }).join("\n");
@@ -1158,17 +1477,17 @@ function getIndexPageFragment(fileNames, relativePath = "") {
1158
1477
  `// Auto-generated. Do not edit.
1159
1478
  // ignore_for_file: type=lint
1160
1479
 
1161
- ${exports$1}
1480
+ ${exports}
1162
1481
  `
1163
1482
  );
1164
1483
  }
1165
1484
  function getRootIndexPageFragment(categories) {
1166
- const exports$1 = categories.sort().map((cat) => `export '${cat}/${cat}.dart';`).join("\n");
1485
+ const exports = categories.sort().map((cat) => `export '${cat}/${cat}.dart';`).join("\n");
1167
1486
  return fragmentFromString(
1168
1487
  `// Auto-generated. Do not edit.
1169
1488
  // ignore_for_file: type=lint
1170
1489
 
1171
- ${exports$1}
1490
+ ${exports}
1172
1491
  `
1173
1492
  );
1174
1493
  }
@@ -1178,30 +1497,62 @@ function getInstructionPageFragment(node, scope) {
1178
1497
  const instrFnName = scope.nameApi.instructionFunction(name);
1179
1498
  const parseFnName = scope.nameApi.instructionParseFunction(name);
1180
1499
  const accounts = node.accounts ?? [];
1181
- const args = (node.arguments ?? []).filter(
1182
- (arg) => !isDiscriminatorArg(arg, node)
1183
- );
1184
1500
  const allArgs = node.arguments ?? [];
1501
+ const optionalAccountStrategy = node.optionalAccountStrategy ?? "programId";
1502
+ if (optionalAccountStrategy !== "programId" && optionalAccountStrategy !== "omitted") {
1503
+ throw new Error(
1504
+ `Unsupported optional account strategy on instruction "${node.name}": ${optionalAccountStrategy}`
1505
+ );
1506
+ }
1507
+ const args = allArgs.filter(
1508
+ (arg) => !isDiscriminatorArg(arg, node) && !isOmittedDefaultArgument(arg)
1509
+ );
1185
1510
  const dataClassName = `${typeName}InstructionData`;
1186
- const allArgManifests = allArgs.map((arg) => ({
1511
+ const allArgManifests = (node.arguments ?? []).map((arg) => ({
1187
1512
  arg,
1188
1513
  manifest: visitorsCore.visit(arg.type, scope.typeManifestVisitor)
1189
1514
  }));
1515
+ const argDefaultValues = new Map(
1516
+ allArgManifests.flatMap(({ arg, manifest }) => {
1517
+ if (arg.defaultValue == null) return [];
1518
+ const needsRenderedDefault = isOmittedDefaultArgument(arg) || isDiscriminatorArg(arg, node) || arg.defaultValue.kind !== "accountBumpValueNode";
1519
+ if (!needsRenderedDefault) return [];
1520
+ return [
1521
+ [
1522
+ arg,
1523
+ getDartValueFragment(arg.defaultValue, manifest.type.content)
1524
+ ]
1525
+ ];
1526
+ })
1527
+ );
1528
+ for (const arg of allArgs.filter(
1529
+ (candidate) => isDiscriminatorArg(candidate, node)
1530
+ )) {
1531
+ if (!argDefaultValues.has(arg)) {
1532
+ throw new Error(
1533
+ `Field discriminator "${arg.name}" on instruction "${node.name}" must reference a field with a default value.`
1534
+ );
1535
+ }
1536
+ }
1190
1537
  const dataFieldDecls = allArgManifests.map(({ arg, manifest }) => {
1191
1538
  const fieldName = camelCase(arg.name);
1192
1539
  return ` final ${manifest.type.content} ${fieldName};`;
1193
1540
  }).join("\n");
1194
1541
  const dataCtorParams = allArgManifests.map(({ arg, manifest }) => {
1195
1542
  const fieldName = camelCase(arg.name);
1543
+ if (isOmittedDefaultArgument(arg)) return "";
1196
1544
  if (isDiscriminatorArg(arg, node)) {
1197
- return isConstDefaultValue(arg.defaultValue) ? ` this.${fieldName} = ${getDiscriminatorDefault(arg)},` : ` ${manifest.type.content}? ${fieldName},`;
1545
+ return isConstDefaultValue(arg.defaultValue) ? ` this.${fieldName} = ${argDefaultValues.get(arg).content},` : ` ${manifest.type.content}? ${fieldName},`;
1198
1546
  }
1199
1547
  return ` required this.${fieldName},`;
1200
- }).join("\n");
1548
+ }).filter(Boolean).join("\n");
1201
1549
  const dataCtorInitializers = allArgManifests.map(({ arg }) => {
1202
1550
  const fieldName = camelCase(arg.name);
1551
+ if (isOmittedDefaultArgument(arg)) {
1552
+ return ` ${fieldName} = ${argDefaultValues.get(arg).content}`;
1553
+ }
1203
1554
  if (isDiscriminatorArg(arg, node) && !isConstDefaultValue(arg.defaultValue)) {
1204
- return ` ${fieldName} = ${fieldName} ?? ${getDiscriminatorDefault(arg)}`;
1555
+ return ` ${fieldName} = ${fieldName} ?? ${argDefaultValues.get(arg).content}`;
1205
1556
  }
1206
1557
  return null;
1207
1558
  }).filter((value) => value !== null).join(",\n");
@@ -1211,16 +1562,26 @@ function getInstructionPageFragment(node, scope) {
1211
1562
  const decFields = allArgManifests.map(({ arg, manifest }) => {
1212
1563
  return ` ('${arg.name}', ${manifest.decoder.content}),`;
1213
1564
  }).join("\n");
1214
- const toMapFields = allArgs.map(
1215
- (arg) => ` '${arg.name}': value.${camelCase(arg.name)},`
1216
- ).join("\n");
1217
- const fromMapFields = allArgManifests.map(({ arg, manifest }) => {
1565
+ const toMapFields = allArgs.map((arg) => {
1566
+ const value = isOmittedDefaultArgument(arg) || isDiscriminatorArg(arg, node) ? argDefaultValues.get(arg).content : `value.${camelCase(arg.name)}`;
1567
+ return ` '${arg.name}': ${value},`;
1568
+ }).join("\n");
1569
+ const fromMapFields = allArgManifests.filter(({ arg }) => !isOmittedDefaultArgument(arg)).map(({ arg, manifest }) => {
1218
1570
  const typeStr = manifest.type.content;
1219
1571
  const isNullable = typeStr.endsWith("?");
1220
1572
  const accessor = isNullable ? `map['${arg.name}']` : `map['${arg.name}']!`;
1221
1573
  return ` ${camelCase(arg.name)}: ${accessor} as ${typeStr},`;
1222
1574
  }).join("\n");
1223
- const dataCtorKeyword = dataCtorInitializers ? "" : "const ";
1575
+ const dataCtorKeyword = allArgs.every(
1576
+ (arg) => !isOmittedDefaultArgument(arg) || isConstDartValueNode(arg.defaultValue)
1577
+ ) && allArgs.every(
1578
+ (arg) => !isDiscriminatorArg(arg, node) || isConstDefaultValue(arg.defaultValue)
1579
+ ) ? "const " : "";
1580
+ const dataCtorSignature = dataCtorParams ? `${dataCtorKeyword}${dataClassName}({
1581
+ ${dataCtorParams}
1582
+ })${dataCtorInitializers ? ` :
1583
+ ${dataCtorInitializers}` : ""};` : `${dataCtorKeyword}${dataClassName}()${dataCtorInitializers ? ` :
1584
+ ${dataCtorInitializers}` : ""};`;
1224
1585
  const dataEncoderName = `get${typeName}InstructionDataEncoder`;
1225
1586
  const dataDecoderName = `get${typeName}InstructionDataDecoder`;
1226
1587
  const dataCodecName = `get${typeName}InstructionDataCodec`;
@@ -1238,7 +1599,7 @@ function getInstructionPageFragment(node, scope) {
1238
1599
  const manifest = argManifestMap.get(arg);
1239
1600
  const fieldName = camelCase(arg.name);
1240
1601
  const typeStr = manifest.type.content;
1241
- const hasDefault = arg.defaultValue != null;
1602
+ const hasDefault = arg.defaultValue != null && arg.defaultValue.kind !== "accountBumpValueNode";
1242
1603
  if (hasDefault) {
1243
1604
  const nullableType = typeStr.endsWith("?") ? typeStr : `${typeStr}?`;
1244
1605
  return ` ${nullableType} ${fieldName},`;
@@ -1250,18 +1611,33 @@ function getInstructionPageFragment(node, scope) {
1250
1611
  const role = getAccountRole(acc);
1251
1612
  const isOptional = acc.isOptional ?? false;
1252
1613
  if (isOptional) {
1253
- return ` if (${fieldName} != null) AccountMeta(address: ${fieldName}, role: ${role}),`;
1614
+ if (optionalAccountStrategy === "omitted") {
1615
+ return ` if (${fieldName} != null) AccountMeta(address: ${fieldName}, role: ${role}),`;
1616
+ }
1617
+ return ` if (${fieldName} != null) AccountMeta(address: ${fieldName}, role: ${role}) else AccountMeta(address: ${instrProgramParam}, role: AccountRole.readonly),`;
1254
1618
  }
1255
1619
  return ` AccountMeta(address: ${fieldName}, role: ${role}),`;
1256
1620
  }).join("\n");
1257
1621
  const dataConstruction = allArgs.map((arg) => {
1258
1622
  const fieldName = camelCase(arg.name);
1259
- if (isDiscriminatorArg(arg, node)) {
1623
+ if (isOmittedDefaultArgument(arg) || isDiscriminatorArg(arg, node)) {
1260
1624
  return "";
1261
1625
  }
1262
- return ` ${fieldName}: ${fieldName}${arg.defaultValue != null ? ` ?? ${getDefaultValue(arg)}` : ""},`;
1626
+ const skipDefault = arg.defaultValue?.kind === "accountBumpValueNode";
1627
+ return ` ${fieldName}: ${fieldName}${arg.defaultValue != null && !skipDefault ? ` ?? ${argDefaultValues.get(arg).content}` : ""},`;
1263
1628
  }).filter(Boolean).join("\n");
1264
1629
  const discFragment = getDiscriminatorConstantsFragment(node, scope);
1630
+ const discriminatorValidation = getDiscriminatorValidationFragment(
1631
+ node,
1632
+ scope
1633
+ );
1634
+ const topLevelDecoder = getTopLevelDecoderFragment({
1635
+ typeName: dataClassName,
1636
+ description: `${name} instruction decoder`,
1637
+ discriminatorValidation,
1638
+ fromMapFields,
1639
+ requireExactConsumption: true
1640
+ });
1265
1641
  const parts = [
1266
1642
  fragment`// Auto-generated. Do not edit.
1267
1643
  // ignore_for_file: type=lint
@@ -1285,10 +1661,7 @@ ${use("AccountRole", "solanaInstructions")}`
1285
1661
  parts.push(fragment`
1286
1662
  @immutable
1287
1663
  class ${fragmentFromString(dataClassName)} {
1288
- ${fragmentFromString(dataCtorKeyword)}${fragmentFromString(dataClassName)}({
1289
- ${fragmentFromString(dataCtorParams)}
1290
- })${fragmentFromString(dataCtorInitializers ? ` :
1291
- ${dataCtorInitializers}` : "")};
1664
+ ${fragmentFromString(dataCtorSignature)}
1292
1665
 
1293
1666
  ${fragmentFromString(dataFieldDecls)}
1294
1667
  }`);
@@ -1311,12 +1684,7 @@ Decoder<${fragmentFromString(dataClassName)}> ${fragmentFromString(dataDecoderNa
1311
1684
  ${fragmentFromString(decFields)}
1312
1685
  ]);
1313
1686
 
1314
- return transformDecoder(
1315
- structDecoder,
1316
- (Map<String, Object?> map, Uint8List bytes, int offset) => ${fragmentFromString(dataClassName)}(
1317
- ${fragmentFromString(fromMapFields)}
1318
- ),
1319
- );
1687
+ ${topLevelDecoder}
1320
1688
  }
1321
1689
 
1322
1690
  Codec<${fragmentFromString(dataClassName)}, ${fragmentFromString(dataClassName)}> ${fragmentFromString(dataCodecName)}() {
@@ -1352,6 +1720,9 @@ ${fragmentFromString(dataClassName)} ${fragmentFromString(parseFnName)}(Instruct
1352
1720
  result.imports.mergeWith(manifest.decoder.imports);
1353
1721
  result.imports.mergeWith(manifest.type.imports);
1354
1722
  }
1723
+ for (const defaultValue of argDefaultValues.values()) {
1724
+ result.imports.mergeWith(defaultValue.imports);
1725
+ }
1355
1726
  return result;
1356
1727
  }
1357
1728
  function getAccountRole(acc) {
@@ -1368,19 +1739,6 @@ function isDiscriminatorArg(arg, node) {
1368
1739
  (d) => d.kind === "fieldDiscriminatorNode" && d.name === arg.name
1369
1740
  );
1370
1741
  }
1371
- function getDiscriminatorDefault(arg, _node) {
1372
- if (arg.defaultValue) {
1373
- if (arg.defaultValue.kind === "numberValueNode") {
1374
- return String(arg.defaultValue.number);
1375
- }
1376
- if (arg.defaultValue.kind === "bytesValueNode") {
1377
- const data = arg.defaultValue.data;
1378
- const bytes = data.match(/.{1,2}/g)?.map((b) => parseInt(b, 16)) ?? [];
1379
- return `Uint8List.fromList([${bytes.join(", ")}])`;
1380
- }
1381
- }
1382
- return "0";
1383
- }
1384
1742
  function isConstDefaultValue(defaultValue) {
1385
1743
  if (!defaultValue) return false;
1386
1744
  switch (defaultValue.kind) {
@@ -1393,33 +1751,8 @@ function isConstDefaultValue(defaultValue) {
1393
1751
  return false;
1394
1752
  }
1395
1753
  }
1396
- function getDefaultValue(arg) {
1397
- if (!arg.defaultValue) return "null";
1398
- const dv = arg.defaultValue;
1399
- switch (dv.kind) {
1400
- case "numberValueNode":
1401
- return String(dv.number);
1402
- case "booleanValueNode":
1403
- return String(dv.boolean);
1404
- case "stringValueNode":
1405
- return `'${dv.string}'`;
1406
- case "publicKeyValueNode": {
1407
- const wellKnownName = WELL_KNOWN_ADDRESSES.get(dv.publicKey);
1408
- if (wellKnownName) {
1409
- use(wellKnownName, "solanaAddresses");
1410
- return wellKnownName;
1411
- }
1412
- return `Address('${dv.publicKey}')`;
1413
- }
1414
- case "noneValueNode":
1415
- return "null";
1416
- case "bytesValueNode": {
1417
- const bytes = dv.data.match(/.{1,2}/g)?.map((b) => parseInt(b, 16)) ?? [];
1418
- return `Uint8List.fromList([${bytes.join(", ")}])`;
1419
- }
1420
- default:
1421
- return "null";
1422
- }
1754
+ function isOmittedDefaultArgument(arg) {
1755
+ return arg.defaultValue != null && arg.defaultValueStrategy === "omitted";
1423
1756
  }
1424
1757
  function getPdaPageFragment(node, scope) {
1425
1758
  const name = node.name;
@@ -1538,8 +1871,6 @@ function bytesValueToDart(hex) {
1538
1871
  }
1539
1872
  return `[${pairs.join(", ")}]`;
1540
1873
  }
1541
-
1542
- // src/fragments/programPage.ts
1543
1874
  function getProgramPageFragment(node, scope) {
1544
1875
  const name = node.name;
1545
1876
  const addressConstName = scope.nameApi.programAddressConstant(name);
@@ -1571,41 +1902,171 @@ enum ${fragmentFromString(pascalCase(name))}Account {
1571
1902
  ${fragmentFromString(accountVariants)}
1572
1903
  }`);
1573
1904
  }
1574
- const instructions = node.instructions ?? [];
1905
+ const instructions = nodes.getAllInstructionsWithSubs(node);
1575
1906
  if (instructions.length > 0) {
1907
+ const programName = pascalCase(name);
1908
+ const instructionEnum = `${programName}Instruction`;
1576
1909
  const instrVariants = instructions.map((instr) => ` ${camelCase(instr.name)},`).join("\n");
1577
1910
  parts.push(fragment`
1578
- /// Known instructions for the ${fragmentFromString(pascalCase(name))} program.
1579
- enum ${fragmentFromString(pascalCase(name))}Instruction {
1911
+ /// Known instructions for the ${fragmentFromString(programName)} program.
1912
+ enum ${fragmentFromString(instructionEnum)} {
1580
1913
  ${fragmentFromString(instrVariants)}
1581
1914
  }`);
1915
+ const identifiableInstructions = instructions.map((instruction) => ({
1916
+ instruction,
1917
+ condition: getInstructionDiscriminatorCondition(instruction, scope)
1918
+ })).filter(({ condition }) => condition !== null);
1919
+ if (identifiableInstructions.length > 0) {
1920
+ const identifyBranches = identifiableInstructions.map(
1921
+ ({ instruction, condition }) => fragment` if (${condition}) {
1922
+ return ${fragmentFromString(instructionEnum)}.${fragmentFromString(camelCase(instruction.name))};
1923
+ }`
1924
+ );
1925
+ const parsedBase = `Parsed${programName}Instruction`;
1926
+ const parsedClasses = instructions.map((instruction) => {
1927
+ const instructionName = pascalCase(instruction.name);
1928
+ const variant = camelCase(instruction.name);
1929
+ const parsedClass = `Parsed${instructionName}`;
1930
+ const dataClass = `${instructionName}InstructionData`;
1931
+ return fragment`/// A parsed ${fragmentFromString(instructionName)} instruction.
1932
+ final class ${fragmentFromString(parsedClass)} extends ${fragmentFromString(parsedBase)} {
1933
+ const ${fragmentFromString(parsedClass)}({required this.data})
1934
+ : super(${fragmentFromString(instructionEnum)}.${fragmentFromString(variant)});
1935
+
1936
+ final ${use(dataClass, "../instructions/instructions.dart")} data;
1937
+ }`;
1938
+ });
1939
+ const parseBranches = instructions.map((instruction) => {
1940
+ const instructionName = pascalCase(instruction.name);
1941
+ const variant = camelCase(instruction.name);
1942
+ const parsedClass = `Parsed${instructionName}`;
1943
+ const parseFunction = scope.nameApi.instructionParseFunction(
1944
+ instruction.name
1945
+ );
1946
+ return fragment` ${fragmentFromString(instructionEnum)}.${fragmentFromString(variant)} => ${fragmentFromString(parsedClass)}(
1947
+ data: ${use(parseFunction, "../instructions/instructions.dart")}(instruction),
1948
+ ),`;
1949
+ });
1950
+ parts.push(fragment`
1951
+ /// Identifies the type of a ${fragmentFromString(programName)} instruction.
1952
+ ${fragmentFromString(instructionEnum)} identify${fragmentFromString(programName)}Instruction(
1953
+ ${use("Uint8List", "dartTypedData")} data,
1954
+ ) {
1955
+ ${mergeFragments(identifyBranches, (cs) => cs.join("\n"))}
1956
+
1957
+ throw ${use("SolanaError", "solanaErrors")}(
1958
+ ${use("SolanaErrorCode", "solanaErrors")}.programClientsFailedToIdentifyInstruction,
1959
+ {
1960
+ 'instructionData': data,
1961
+ 'programName': '${fragmentFromString(name)}',
1962
+ },
1963
+ );
1964
+ }
1965
+
1966
+ /// A parsed instruction from the ${fragmentFromString(programName)} program.
1967
+ sealed class ${fragmentFromString(parsedBase)} {
1968
+ const ${fragmentFromString(parsedBase)}(this.instructionType);
1969
+
1970
+ final ${fragmentFromString(instructionEnum)} instructionType;
1971
+ }
1972
+
1973
+ ${mergeFragments(parsedClasses, (cs) => cs.join("\n\n"))}
1974
+
1975
+ /// Parses a ${fragmentFromString(programName)} instruction.
1976
+ ${fragmentFromString(parsedBase)} parse${fragmentFromString(programName)}Instruction(
1977
+ ${use("Instruction", "solanaInstructions")} instruction,
1978
+ ) {
1979
+ return switch (identify${fragmentFromString(programName)}Instruction(
1980
+ instruction.data ?? Uint8List(0),
1981
+ )) {
1982
+ ${mergeFragments(parseBranches, (cs) => cs.join("\n"))}
1983
+ };
1984
+ }`);
1985
+ }
1582
1986
  }
1583
1987
  return mergeFragments(parts, (cs) => cs.join("\n"));
1584
1988
  }
1989
+ function getInstructionDiscriminatorCondition(instruction, scope) {
1990
+ const discriminators = instruction.discriminators ?? [];
1991
+ if (discriminators.length === 0) return null;
1992
+ const conditions = discriminators.map((discriminator) => {
1993
+ switch (discriminator.kind) {
1994
+ case "sizeDiscriminatorNode":
1995
+ return fragment`data.length == ${discriminator.size}`;
1996
+ case "constantDiscriminatorNode": {
1997
+ const manifest = visitorsCore.visit(
1998
+ discriminator.constant.type,
1999
+ scope.typeManifestVisitor
2000
+ );
2001
+ const value = getValueFragment(
2002
+ discriminator.constant.value,
2003
+ manifest.type.content
2004
+ );
2005
+ if (value === null) return null;
2006
+ return fragment`${use("containsBytes", "solanaCodecsCore")}(data, ${manifest.encoder}.encode(${value}), ${discriminator.offset})`;
2007
+ }
2008
+ case "fieldDiscriminatorNode": {
2009
+ const argument = (instruction.arguments ?? []).find(
2010
+ (candidate) => candidate.name === discriminator.name
2011
+ );
2012
+ if (argument?.defaultValue == null) return null;
2013
+ const manifest = visitorsCore.visit(argument.type, scope.typeManifestVisitor);
2014
+ const value = getValueFragment(
2015
+ argument.defaultValue,
2016
+ manifest.type.content
2017
+ );
2018
+ if (value === null) return null;
2019
+ return fragment`${use("containsBytes", "solanaCodecsCore")}(data, ${manifest.encoder}.encode(${value}), ${discriminator.offset})`;
2020
+ }
2021
+ }
2022
+ });
2023
+ if (conditions.some((condition) => condition === null)) return null;
2024
+ return mergeFragments(conditions, (cs) => cs.join(" && "));
2025
+ }
2026
+ function getValueFragment(value, dartType) {
2027
+ switch (value.kind) {
2028
+ case "numberValueNode":
2029
+ return fragmentFromString(
2030
+ dartType === "BigInt" ? `BigInt.from(${value.number})` : String(value.number)
2031
+ );
2032
+ case "booleanValueNode":
2033
+ return fragmentFromString(String(value.boolean));
2034
+ case "stringValueNode":
2035
+ return fragmentFromString(`'${value.string.replaceAll("'", "\\'")}'`);
2036
+ case "bytesValueNode": {
2037
+ const clean = value.data.replace(/^0x/, "");
2038
+ const bytes = clean.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)) ?? [];
2039
+ return fragment`${use("Uint8List", "dartTypedData")}.fromList([${fragmentFromString(bytes.join(", "))}])`;
2040
+ }
2041
+ default:
2042
+ return null;
2043
+ }
2044
+ }
1585
2045
  function getTypePageFragment(node, scope) {
1586
2046
  const name = node.name;
1587
2047
  scope.nameApi.dataType(name);
1588
2048
  const typeNode = node.type;
1589
2049
  if (typeNode.kind === "enumTypeNode") {
1590
- const isScalar = typeNode.variants.every(
2050
+ const variants = typeNode.variants ?? [];
2051
+ const isScalar = variants.every(
1591
2052
  (v) => v.kind === "enumEmptyVariantTypeNode"
1592
2053
  );
1593
2054
  if (isScalar) {
1594
- return getScalarEnumPageFragment(node, scope);
2055
+ return getScalarEnumPageFragment(node, variants, scope);
1595
2056
  }
1596
- return getDataEnumPageFragment(node, scope);
2057
+ return getDataEnumPageFragment(node, variants, scope);
1597
2058
  }
1598
2059
  if (typeNode.kind === "structTypeNode") {
1599
2060
  return getStructPageFragment(node, scope);
1600
2061
  }
1601
2062
  return getTypeAliasPageFragment(node, scope);
1602
2063
  }
1603
- function getScalarEnumPageFragment(node, scope) {
2064
+ function getScalarEnumPageFragment(node, enumVariants, scope) {
1604
2065
  const name = node.name;
1605
2066
  const typeName = scope.nameApi.dataType(name);
1606
2067
  const enumNode = node.type;
1607
2068
  if (enumNode.kind !== "enumTypeNode") return emptyFragment();
1608
- const variants = enumNode.variants.map((v) => {
2069
+ const variants = enumVariants.map((v) => {
1609
2070
  const variantName = scope.nameApi.enumVariant(v.name);
1610
2071
  return variantName;
1611
2072
  });
@@ -1616,6 +2077,14 @@ function getScalarEnumPageFragment(node, scope) {
1616
2077
  const resolvedSize = nodes.resolveNestedTypeNode(enumNode.size);
1617
2078
  const sizeType = resolvedSize.format ?? "u8";
1618
2079
  const sizeCodecName = getNumberCodecName2(sizeType);
2080
+ const isU64 = sizeType === "u64";
2081
+ const encodedVariantIndex = isU64 ? "BigInt.from(value.index)" : "value.index";
2082
+ const decodedVariant = isU64 ? `(BigInt value, Uint8List bytes, int offset) {
2083
+ if (value.isNegative || value >= BigInt.from(${typeName}.values.length)) {
2084
+ throw RangeError('Invalid ${typeName} discriminator: ' + value.toString());
2085
+ }
2086
+ return ${typeName}.values[value.toInt()];
2087
+ }` : `(int value, Uint8List bytes, int offset) => ${typeName}.values[value]`;
1619
2088
  return fragment`// Auto-generated. Do not edit.
1620
2089
  // ignore_for_file: type=lint
1621
2090
 
@@ -1636,14 +2105,14 @@ ${fragmentFromString(variantLines)}
1636
2105
  Encoder<${fragmentFromString(typeName)}> ${fragmentFromString(encoderName)}() {
1637
2106
  return transformEncoder(
1638
2107
  get${fragmentFromString(sizeCodecName)}Encoder(),
1639
- (${fragmentFromString(typeName)} value) => value.index,
2108
+ (${fragmentFromString(typeName)} value) => ${fragmentFromString(encodedVariantIndex)},
1640
2109
  );
1641
2110
  }
1642
2111
 
1643
2112
  Decoder<${fragmentFromString(typeName)}> ${fragmentFromString(decoderName)}() {
1644
2113
  return transformDecoder(
1645
2114
  get${fragmentFromString(sizeCodecName)}Decoder(),
1646
- (int value, Uint8List bytes, int offset) => ${fragmentFromString(typeName)}.values[value],
2115
+ ${fragmentFromString(decodedVariant)},
1647
2116
  );
1648
2117
  }
1649
2118
 
@@ -1651,7 +2120,7 @@ Codec<${fragmentFromString(typeName)}, ${fragmentFromString(typeName)}> ${fragme
1651
2120
  return combineCodec(${fragmentFromString(encoderName)}(), ${fragmentFromString(decoderName)}());
1652
2121
  }`;
1653
2122
  }
1654
- function getDataEnumPageFragment(node, scope) {
2123
+ function getDataEnumPageFragment(node, variants, scope) {
1655
2124
  const name = node.name;
1656
2125
  const typeName = scope.nameApi.dataType(name);
1657
2126
  const enumNode = node.type;
@@ -1662,8 +2131,9 @@ function getDataEnumPageFragment(node, scope) {
1662
2131
  const encodeCases = [];
1663
2132
  const decodeCases = [];
1664
2133
  const allVariantManifests = [];
1665
- for (let i = 0; i < enumNode.variants.length; i++) {
1666
- const variant = enumNode.variants[i];
2134
+ for (let i = 0; i < variants.length; i++) {
2135
+ const variant = variants[i];
2136
+ const variantName = pascalCase(variant.name);
1667
2137
  const variantClassName = scope.nameApi.sealedClassVariant(
1668
2138
  name,
1669
2139
  variant.name
@@ -1680,7 +2150,7 @@ function getDataEnumPageFragment(node, scope) {
1680
2150
  int get hashCode => runtimeType.hashCode;
1681
2151
 
1682
2152
  @override
1683
- String toString() => '${typeName}.${variantClassName}()';
2153
+ String toString() => '${typeName}.${variantName}()';
1684
2154
  }`);
1685
2155
  encoderVariants.push(
1686
2156
  `(${i}, getStructEncoder(<(String, Encoder<Object?>)>[]))`
@@ -1692,7 +2162,7 @@ function getDataEnumPageFragment(node, scope) {
1692
2162
  decodeCases.push(`case ${i}: return const ${variantClassName}();`);
1693
2163
  } else if (variant.kind === "enumStructVariantTypeNode") {
1694
2164
  const resolvedStruct = nodes.resolveNestedTypeNode(variant.struct);
1695
- const fields = resolvedStruct.fields;
2165
+ const fields = resolvedStruct.fields ?? [];
1696
2166
  const fieldManifests = fields.map((f) => ({
1697
2167
  field: f,
1698
2168
  manifest: visitorsCore.visit(f.type, scope.typeManifestVisitor)
@@ -1727,7 +2197,7 @@ ${fieldDecls}
1727
2197
  int get hashCode => ${hashExpression};
1728
2198
 
1729
2199
  @override
1730
- String toString() => '${typeName}.${variantClassName}(${toStringFields})';
2200
+ String toString() => '${typeName}.${variantName}(${toStringFields})';
1731
2201
  }`);
1732
2202
  const encFields = fieldManifests.map(({ field: f, manifest }) => {
1733
2203
  return `('${f.name}', ${manifest.encoder.content})`;
@@ -1758,7 +2228,7 @@ ${fieldDecls}
1758
2228
  decodeCases.push(`case ${i}: return ${variantClassName}(${fromMapFields.replace(/\n/g, " ").replace(/,$/, "")});`);
1759
2229
  } else if (variant.kind === "enumTupleVariantTypeNode") {
1760
2230
  const resolvedTuple = nodes.resolveNestedTypeNode(variant.tuple);
1761
- const items = resolvedTuple.items;
2231
+ const items = resolvedTuple.items ?? [];
1762
2232
  if (items.length === 1) {
1763
2233
  const manifest = visitorsCore.visit(items[0], scope.typeManifestVisitor);
1764
2234
  allVariantManifests.push(manifest);
@@ -1776,7 +2246,7 @@ ${fieldDecls}
1776
2246
  int get hashCode => value.hashCode;
1777
2247
 
1778
2248
  @override
1779
- String toString() => '${typeName}.${variantClassName}($value)';
2249
+ String toString() => '${typeName}.${variantName}($value)';
1780
2250
  }`);
1781
2251
  encoderVariants.push(
1782
2252
  `(${i}, transformEncoder<${manifest.type.content}, Map<String, Object?>>(${manifest.encoder.content}, (Map<String, Object?> map) => map['value']! as ${manifest.type.content}))`
@@ -1858,7 +2328,7 @@ function getStructPageFragment(node, scope) {
1858
2328
  const typeName = scope.nameApi.dataType(name);
1859
2329
  const structNode = node.type;
1860
2330
  if (structNode.kind !== "structTypeNode") return emptyFragment();
1861
- const fields = structNode.fields;
2331
+ const fields = structNode.fields ?? [];
1862
2332
  const fieldManifests = fields.map((f) => ({
1863
2333
  field: f,
1864
2334
  manifest: visitorsCore.visit(f.type, scope.typeManifestVisitor)
@@ -2065,7 +2535,7 @@ function getRenderMapVisitor(options = {}) {
2065
2535
  visitRoot(node, { self }) {
2066
2536
  const programMaps = [
2067
2537
  visitorsCore.visit(node.program, self),
2068
- ...node.additionalPrograms.map((p) => visitorsCore.visit(p, self))
2538
+ ...(node.additionalPrograms ?? []).map((p) => visitorsCore.visit(p, self))
2069
2539
  ];
2070
2540
  const mergedProgramMap = renderersCore.mergeRenderMaps(programMaps);
2071
2541
  const categoryMaps = getSharedCategoryIndexMaps(mergedProgramMap);
@@ -2211,16 +2681,17 @@ function renderVisitor(outputDir, options = {}) {
2211
2681
  dependencyMap
2212
2682
  } = options;
2213
2683
  return visitorsCore.rootNodeVisitor((root) => {
2684
+ const normalizedRoot = normalizeRootNode(root);
2214
2685
  if (deleteFolderBeforeRendering && fs.existsSync(outputDir)) {
2215
2686
  renderersCore.deleteDirectory(outputDir);
2216
2687
  }
2217
2688
  const renderMap = visitorsCore.visit(
2218
- root,
2689
+ normalizedRoot,
2219
2690
  getRenderMapVisitor({ nameApi, dependencyMap })
2220
2691
  );
2221
2692
  const typePathMap = {};
2222
2693
  for (const renderPath of renderMap.keys()) {
2223
- const match = renderPath.match(/^(?:.*\/)?types\/([a-z_]+)\.dart$/);
2694
+ const match = renderPath.match(/^(?:.*\/)?types\/([a-z0-9_]+)\.dart$/);
2224
2695
  if (match) {
2225
2696
  typePathMap[`definedType:${match[1]}`] = renderPath;
2226
2697
  }
@@ -2234,7 +2705,10 @@ function renderVisitor(outputDir, options = {}) {
2234
2705
  const fileDir = path.dirname(filePath);
2235
2706
  const internalMap = {};
2236
2707
  for (const [key, typePath] of Object.entries(typePathMap)) {
2237
- if (typePath === filePath) continue;
2708
+ if (typePath === filePath) {
2709
+ internalMap[key] = "";
2710
+ continue;
2711
+ }
2238
2712
  let rel = path.posix.relative(fileDir, typePath);
2239
2713
  if (!rel.startsWith(".")) {
2240
2714
  rel = `./${rel}`;
@@ -2300,6 +2774,7 @@ exports.createDartNameApi = createDartNameApi;
2300
2774
  exports.default = src_default;
2301
2775
  exports.getRenderMapVisitor = getRenderMapVisitor;
2302
2776
  exports.getTypeManifestVisitor = getTypeManifestVisitor;
2777
+ exports.normalizeRootNode = normalizeRootNode;
2303
2778
  exports.renderVisitor = renderVisitor;
2304
2779
  //# sourceMappingURL=index.browser.cjs.map
2305
2780
  //# sourceMappingURL=index.browser.cjs.map