codama-renderers-dart 0.4.2 → 0.5.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"instructionPage.d.ts","sourceRoot":"","sources":["../../../src/fragments/instructionPage.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,eAAe,EAChB,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAQrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAKvD;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,eAAe,EACrB,KAAK,EAAE,WAAW,GACjB,QAAQ,CA4PV"}
1
+ {"version":3,"file":"instructionPage.d.ts","sourceRoot":"","sources":["../../../src/fragments/instructionPage.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,eAAe,EAChB,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAQrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAKvD;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,eAAe,EACrB,KAAK,EAAE,WAAW,GACjB,QAAQ,CAkQV"}
@@ -1,4 +1,4 @@
1
- import type { ProgramNode } from "@codama/nodes";
1
+ import { type ProgramNode } from "@codama/nodes";
2
2
  import type { Fragment } from "../utils/fragment.js";
3
3
  import type { RenderScope } from "../utils/options.js";
4
4
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"programPage.d.ts","sourceRoot":"","sources":["../../../src/fragments/programPage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAQrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAIvD;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,WAAW,GACjB,QAAQ,CAgEV"}
1
+ {"version":3,"file":"programPage.d.ts","sourceRoot":"","sources":["../../../src/fragments/programPage.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EAEjB,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAQrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAIvD;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,WAAW,GACjB,QAAQ,CAiJV"}
@@ -1 +1 @@
1
- {"version":3,"file":"importMap.d.ts","sourceRoot":"","sources":["../../../src/utils/importMap.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAsB5D,CAAC;AAEF;;;;;GAKG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAqB;IAErC;;;OAGG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAKzB;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAOrC;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;OAEG;IACH,IAAI,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,CAEzB;IAED;;;OAGG;IACH,OAAO,CAAC,WAAW,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAAG,MAAM,EAAE;IA2B3D;;OAEG;IACH,QAAQ,CAAC,WAAW,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAAG,MAAM;CAyB3D"}
1
+ {"version":3,"file":"importMap.d.ts","sourceRoot":"","sources":["../../../src/utils/importMap.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAsB5D,CAAC;AAEF;;;;;GAKG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAqB;IAErC;;;OAGG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAKzB;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAOrC;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;OAEG;IACH,IAAI,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,CAEzB;IAED;;;OAGG;IACH,OAAO,CAAC,WAAW,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAAG,MAAM,EAAE;IAgC3D;;OAEG;IACH,QAAQ,CAAC,WAAW,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAAG,MAAM;CAyB3D"}
@@ -44,7 +44,7 @@ export interface DartNameApi {
44
44
  errorMessageFunction(name: string): string;
45
45
  /** Enum variant: `camelCase` */
46
46
  enumVariant(name: string): string;
47
- /** Sealed class variant (data enum): `PascalCase` */
47
+ /** Sealed class variant (data enum): `{ParentName}{PascalCase}` */
48
48
  sealedClassVariant(parentName: string, variantName: string): string;
49
49
  /** File name: `snake_case.dart` */
50
50
  fileName(name: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"nameTransformers.d.ts","sourceRoot":"","sources":["../../../src/utils/nameTransformers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD;;GAEG;AAEH,qCAAqC;AACrC,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI9C;AAED,oCAAoC;AACpC,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED,qCAAqC;AACrC,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO7C;AAED,+CAA+C;AAC/C,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,sDAAsD;IACtD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtC,sDAAsD;IACtD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtC,kDAAkD;IAClD,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,kDAAkD;IAClD,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3C,6DAA6D;IAC7D,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAChD,oDAAoD;IACpD,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5C,+CAA+C;IAC/C,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1C,iDAAiD;IACjD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtC,iEAAiE;IACjE,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1C,iEAAiE;IACjE,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/C,4DAA4D;IAC5D,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7C,4DAA4D;IAC5D,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9D,4DAA4D;IAC5D,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3C,gCAAgC;IAChC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAClC,qDAAqD;IACrD,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IACpE,mCAAmC;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,yDAAyD;IACzD,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5C,oDAAoD;IACpD,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACtC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,WAAW,CAuB/C;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAEhE"}
1
+ {"version":3,"file":"nameTransformers.d.ts","sourceRoot":"","sources":["../../../src/utils/nameTransformers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD;;GAEG;AAEH,qCAAqC;AACrC,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI9C;AAED,oCAAoC;AACpC,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED,qCAAqC;AACrC,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO7C;AAED,+CAA+C;AAC/C,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,sDAAsD;IACtD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtC,sDAAsD;IACtD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtC,kDAAkD;IAClD,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,kDAAkD;IAClD,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3C,6DAA6D;IAC7D,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAChD,oDAAoD;IACpD,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5C,+CAA+C;IAC/C,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1C,iDAAiD;IACjD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtC,iEAAiE;IACjE,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1C,iEAAiE;IACjE,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/C,4DAA4D;IAC5D,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7C,4DAA4D;IAC5D,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9D,4DAA4D;IAC5D,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3C,gCAAgC;IAChC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAClC,mEAAmE;IACnE,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IACpE,mCAAmC;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,yDAAyD;IACzD,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5C,oDAAoD;IACpD,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACtC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,WAAW,CAwB/C;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAEhE"}
@@ -1 +1 @@
1
- {"version":3,"file":"getTypeManifestVisitor.d.ts","sourceRoot":"","sources":["../../../src/visitors/getTypeManifestVisitor.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,eAAe,EA8BrB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACd,KAAK,OAAO,EAIb,MAAM,uBAAuB,CAAC;AAI/B,OAAO,EAEL,KAAK,YAAY,EAKlB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,KAAK,WAAW,EAAa,MAAM,8BAA8B,CAAC;AAE3E;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE5E;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,kBAAkB,CAAC;IAC9B,KAAK,EAAE,SAAS,CAAC;IACjB,cAAc,CAAC,EAAE,eAAe,EAAE,CAAC;CACpC,6xDAymBA"}
1
+ {"version":3,"file":"getTypeManifestVisitor.d.ts","sourceRoot":"","sources":["../../../src/visitors/getTypeManifestVisitor.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,eAAe,EA8BrB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACd,KAAK,OAAO,EAIb,MAAM,uBAAuB,CAAC;AAI/B,OAAO,EAEL,KAAK,YAAY,EAKlB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,KAAK,WAAW,EAAa,MAAM,8BAA8B,CAAC;AAE3E;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE5E;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,kBAAkB,CAAC;IAC9B,KAAK,EAAE,SAAS,CAAC;IACjB,cAAc,CAAC,EAAE,eAAe,EAAE,CAAC;CACpC,6xDA6mBA"}
@@ -1 +1 @@
1
- {"version":3,"file":"renderVisitor.d.ts","sourceRoot":"","sources":["../../../src/visitors/renderVisitor.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAKzD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,aAAkB,6DA+D5B"}
1
+ {"version":3,"file":"renderVisitor.d.ts","sourceRoot":"","sources":["../../../src/visitors/renderVisitor.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAKzD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,aAAkB,6DAmE5B"}
package/package.json CHANGED
@@ -9,7 +9,12 @@
9
9
  "devDependencies": {
10
10
  "@codama/nodes-from-anchor": "^1.3.9",
11
11
  "@codama/renderers-js": "^2.0.2",
12
- "@vitest/coverage-v8": "4.1.0"
12
+ "@types/node": "25.3.0",
13
+ "@vitest/coverage-v8": "4.1.0",
14
+ "rimraf": "6.1.3",
15
+ "tsup": "8.5.1",
16
+ "typescript": "5.9.3",
17
+ "vitest": "4.1.0"
13
18
  },
14
19
  "engines": {
15
20
  "node": ">=18"
@@ -45,6 +50,9 @@
45
50
  "access": "public"
46
51
  },
47
52
  "repository": "https://github.com/openbudgetfun/solana_kit/tree/main/packages/codama-renderers-dart",
53
+ "type": "module",
54
+ "types": "./dist/types/index.d.ts",
55
+ "version": "0.5.0",
48
56
  "scripts": {
49
57
  "build": "pnpm exec rimraf dist && pnpm exec tsup && pnpm exec tsc -p tsconfig.declarations.json",
50
58
  "lint": "eslint --ext .ts src",
@@ -52,8 +60,5 @@
52
60
  "test:coverage": "pnpm exec vitest run --coverage.enabled --coverage.reporter=lcov",
53
61
  "test:watch": "pnpm exec vitest",
54
62
  "typecheck": "pnpm exec tsc --noEmit"
55
- },
56
- "type": "module",
57
- "types": "./dist/types/index.d.ts",
58
- "version": "0.4.2"
59
- }
63
+ }
64
+ }
package/readme.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # codama-renderers-dart
2
2
 
3
- [![pub package](https://img.shields.io/pub/v/codama-renderers-dart.svg)](https://pub.dev/packages/codama-renderers-dart)
4
- [![CI](https://github.com/openbudgetfun/solana_kit/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/openbudgetfun/solana_kit/actions/workflows/ci.yml)
5
- [![Coverage](https://codecov.io/gh/openbudgetfun/solana_kit/branch/main/graph/badge.svg?flag=codama-renderers-dart)](https://codecov.io/gh/openbudgetfun/solana_kit?flag=codama-renderers-dart)
3
+ [![pub package](https://img.shields.io/pub/v/codama-renderers-dart.svg)](https://pub.dev/packages/codama-renderers-dart) [![CI](https://github.com/openbudgetfun/solana_kit/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/openbudgetfun/solana_kit/actions/workflows/ci.yml) [![Coverage](https://codecov.io/gh/openbudgetfun/solana_kit/branch/main/graph/badge.svg?flag=codama-renderers-dart)](https://codecov.io/gh/openbudgetfun/solana_kit?flag=codama-renderers-dart)
6
4
 
7
5
  A [Codama](https://github.com/codama-idl/codama) renderer that generates Dart code targeting the [solana_kit](https://github.com/openbudgetfun/solana_kit) SDK.
8
6
 
@@ -134,7 +134,11 @@ export function getInstructionPageFragment(
134
134
  const manifest = argManifestMap.get(arg)!;
135
135
  const fieldName = camelCase(arg.name as string);
136
136
  const typeStr = manifest.type.content;
137
- const hasDefault = arg.defaultValue != null;
137
+ // Account bumps require asynchronous PDA derivation, so callers must
138
+ // provide them instead of relying on a synchronous generated default.
139
+ const hasDefault =
140
+ arg.defaultValue != null &&
141
+ arg.defaultValue.kind !== "accountBumpValueNode";
138
142
  if (hasDefault) {
139
143
  // Don't add `?` if the type is already nullable.
140
144
  const nullableType = typeStr.endsWith("?") ? typeStr : `${typeStr}?`;
@@ -165,7 +169,9 @@ export function getInstructionPageFragment(
165
169
  return ""; // Use default
166
170
  }
167
171
  // Use 'arg_<name>' prefix to reference builder params without shadowing.
168
- return ` ${fieldName}: ${fieldName}${arg.defaultValue != null ? ` ?? ${getDefaultValue(arg)}` : ""},`;
172
+ const typeStr2 = argManifestMap.get(arg)?.type.content;
173
+ const skipDefault = arg.defaultValue?.kind === "accountBumpValueNode";
174
+ return ` ${fieldName}: ${fieldName}${arg.defaultValue != null && !skipDefault ? ` ?? ${getDefaultValue(arg, typeStr2)}` : ""},`;
169
175
  })
170
176
  .filter(Boolean)
171
177
  .join("\n");
@@ -329,12 +335,17 @@ function isConstDefaultValue(defaultValue: InstructionArgumentNode["defaultValue
329
335
  }
330
336
  }
331
337
 
332
- function getDefaultValue(arg: InstructionArgumentNode): string {
338
+ function getDefaultValue(
339
+ arg: InstructionArgumentNode,
340
+ typeStr?: string,
341
+ ): string {
333
342
  if (!arg.defaultValue) return "null";
343
+ const isBigInt = typeStr === "BigInt";
334
344
  const dv = arg.defaultValue;
335
345
  switch (dv.kind) {
336
346
  case "numberValueNode":
337
- return String(dv.number);
347
+ // Dart's `BigInt` requires a constructor; `0` is not assignable to `BigInt`.
348
+ return isBigInt ? `BigInt.from(${dv.number})` : String(dv.number);
338
349
  case "booleanValueNode":
339
350
  return String(dv.boolean);
340
351
  case "stringValueNode":
@@ -1,4 +1,10 @@
1
- import type { ProgramNode } from "@codama/nodes";
1
+ import {
2
+ getAllInstructionsWithSubs,
3
+ type InstructionNode,
4
+ type ProgramNode,
5
+ type ValueNode,
6
+ } from "@codama/nodes";
7
+ import { visit } from "@codama/visitors-core";
2
8
 
3
9
  import type { Fragment } from "../utils/fragment.js";
4
10
  import {
@@ -67,19 +73,172 @@ ${fragmentFromString(accountVariants)}
67
73
  }`);
68
74
  }
69
75
 
70
- // Instruction identifier enum
71
- const instructions = node.instructions ?? [];
76
+ // Instruction identification and parsing helpers.
77
+ const instructions = getAllInstructionsWithSubs(node);
72
78
  if (instructions.length > 0) {
79
+ const programName = pascalCase(name);
80
+ const instructionEnum = `${programName}Instruction`;
73
81
  const instrVariants = instructions
74
82
  .map((instr) => ` ${camelCase(instr.name as string)},`)
75
83
  .join("\n");
76
84
 
77
85
  parts.push(fragment`
78
- /// Known instructions for the ${fragmentFromString(pascalCase(name))} program.
79
- enum ${fragmentFromString(pascalCase(name))}Instruction {
86
+ /// Known instructions for the ${fragmentFromString(programName)} program.
87
+ enum ${fragmentFromString(instructionEnum)} {
80
88
  ${fragmentFromString(instrVariants)}
81
89
  }`);
90
+
91
+ const identifiableInstructions = instructions
92
+ .map((instruction) => ({
93
+ instruction,
94
+ condition: getInstructionDiscriminatorCondition(instruction, scope),
95
+ }))
96
+ .filter(({ condition }) => condition !== null) as {
97
+ instruction: InstructionNode;
98
+ condition: Fragment;
99
+ }[];
100
+
101
+ if (identifiableInstructions.length > 0) {
102
+ const identifyBranches = identifiableInstructions.map(
103
+ ({ instruction, condition }) => fragment` if (${condition}) {
104
+ return ${fragmentFromString(instructionEnum)}.${fragmentFromString(camelCase(instruction.name as string))};
105
+ }`,
106
+ );
107
+ const parsedBase = `Parsed${programName}Instruction`;
108
+ const parsedClasses = instructions.map((instruction) => {
109
+ const instructionName = pascalCase(instruction.name as string);
110
+ const variant = camelCase(instruction.name as string);
111
+ const parsedClass = `Parsed${instructionName}`;
112
+ const dataClass = `${instructionName}InstructionData`;
113
+ return fragment`/// A parsed ${fragmentFromString(instructionName)} instruction.
114
+ final class ${fragmentFromString(parsedClass)} extends ${fragmentFromString(parsedBase)} {
115
+ const ${fragmentFromString(parsedClass)}({required this.data})
116
+ : super(${fragmentFromString(instructionEnum)}.${fragmentFromString(variant)});
117
+
118
+ final ${use(dataClass, "../instructions/instructions.dart")} data;
119
+ }`;
120
+ });
121
+ const parseBranches = instructions.map((instruction) => {
122
+ const instructionName = pascalCase(instruction.name as string);
123
+ const variant = camelCase(instruction.name as string);
124
+ const parsedClass = `Parsed${instructionName}`;
125
+ const parseFunction = scope.nameApi.instructionParseFunction(
126
+ instruction.name as string,
127
+ );
128
+ return fragment` ${fragmentFromString(instructionEnum)}.${fragmentFromString(variant)} => ${fragmentFromString(parsedClass)}(
129
+ data: ${use(parseFunction, "../instructions/instructions.dart")}(instruction),
130
+ ),`;
131
+ });
132
+
133
+ parts.push(fragment`
134
+ /// Identifies the type of a ${fragmentFromString(programName)} instruction.
135
+ ${fragmentFromString(instructionEnum)} identify${fragmentFromString(programName)}Instruction(
136
+ ${use("Uint8List", "dartTypedData")} data,
137
+ ) {
138
+ ${mergeFragments(identifyBranches, (cs) => cs.join("\n"))}
139
+
140
+ throw ${use("SolanaError", "solanaErrors")}(
141
+ ${use("SolanaErrorCode", "solanaErrors")}.programClientsFailedToIdentifyInstruction,
142
+ {
143
+ 'instructionData': data,
144
+ 'programName': '${fragmentFromString(name)}',
145
+ },
146
+ );
147
+ }
148
+
149
+ /// A parsed instruction from the ${fragmentFromString(programName)} program.
150
+ sealed class ${fragmentFromString(parsedBase)} {
151
+ const ${fragmentFromString(parsedBase)}(this.instructionType);
152
+
153
+ final ${fragmentFromString(instructionEnum)} instructionType;
154
+ }
155
+
156
+ ${mergeFragments(parsedClasses, (cs) => cs.join("\n\n"))}
157
+
158
+ /// Parses a ${fragmentFromString(programName)} instruction.
159
+ ${fragmentFromString(parsedBase)} parse${fragmentFromString(programName)}Instruction(
160
+ ${use("Instruction", "solanaInstructions")} instruction,
161
+ ) {
162
+ return switch (identify${fragmentFromString(programName)}Instruction(
163
+ instruction.data ?? Uint8List(0),
164
+ )) {
165
+ ${mergeFragments(parseBranches, (cs) => cs.join("\n"))}
166
+ };
167
+ }`);
168
+ }
82
169
  }
83
170
 
84
171
  return mergeFragments(parts, (cs) => cs.join("\n"));
85
- }
172
+ }
173
+
174
+ function getInstructionDiscriminatorCondition(
175
+ instruction: InstructionNode,
176
+ scope: RenderScope,
177
+ ): Fragment | null {
178
+ const discriminators = instruction.discriminators ?? [];
179
+ if (discriminators.length === 0) return null;
180
+
181
+ const conditions = discriminators.map((discriminator): Fragment | null => {
182
+ switch (discriminator.kind) {
183
+ case "sizeDiscriminatorNode":
184
+ return fragment`data.length == ${discriminator.size}`;
185
+ case "constantDiscriminatorNode": {
186
+ const manifest = visit(
187
+ discriminator.constant.type,
188
+ scope.typeManifestVisitor,
189
+ );
190
+ const value = getValueFragment(
191
+ discriminator.constant.value,
192
+ manifest.type.content,
193
+ );
194
+ if (value === null) return null;
195
+ return fragment`${use("containsBytes", "solanaCodecsCore")}(data, ${manifest.encoder}.encode(${value}), ${discriminator.offset})`;
196
+ }
197
+ case "fieldDiscriminatorNode": {
198
+ const argument = (instruction.arguments ?? []).find(
199
+ (candidate) => candidate.name === discriminator.name,
200
+ );
201
+ if (argument?.defaultValue == null) return null;
202
+ const manifest = visit(argument.type, scope.typeManifestVisitor);
203
+ const value = getValueFragment(
204
+ argument.defaultValue,
205
+ manifest.type.content,
206
+ );
207
+ if (value === null) return null;
208
+ return fragment`${use("containsBytes", "solanaCodecsCore")}(data, ${manifest.encoder}.encode(${value}), ${discriminator.offset})`;
209
+ }
210
+ }
211
+ });
212
+
213
+ if (conditions.some((condition) => condition === null)) return null;
214
+ return mergeFragments(conditions as Fragment[], (cs) => cs.join(" && "));
215
+ }
216
+
217
+ function getValueFragment(
218
+ value:
219
+ | ValueNode
220
+ | NonNullable<
221
+ NonNullable<InstructionNode["arguments"]>[number]["defaultValue"]
222
+ >,
223
+ dartType?: string,
224
+ ): Fragment | null {
225
+ switch (value.kind) {
226
+ case "numberValueNode":
227
+ return fragmentFromString(
228
+ dartType === "BigInt"
229
+ ? `BigInt.from(${value.number})`
230
+ : String(value.number),
231
+ );
232
+ case "booleanValueNode":
233
+ return fragmentFromString(String(value.boolean));
234
+ case "stringValueNode":
235
+ return fragmentFromString(`'${value.string.replaceAll("'", "\\'")}'`);
236
+ case "bytesValueNode": {
237
+ const clean = value.data.replace(/^0x/, "");
238
+ const bytes = clean.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)) ?? [];
239
+ return fragment`${use("Uint8List", "dartTypedData")}.fromList([${fragmentFromString(bytes.join(", "))}])`;
240
+ }
241
+ default:
242
+ return null;
243
+ }
244
+ }
@@ -132,6 +132,7 @@ function getDataEnumPageFragment(
132
132
 
133
133
  for (let i = 0; i < enumNode.variants.length; i++) {
134
134
  const variant = enumNode.variants[i];
135
+ const variantName = pascalCase(variant.name as string);
135
136
  const variantClassName = scope.nameApi.sealedClassVariant(
136
137
  name,
137
138
  variant.name as string,
@@ -149,7 +150,7 @@ function getDataEnumPageFragment(
149
150
  int get hashCode => runtimeType.hashCode;
150
151
 
151
152
  @override
152
- String toString() => '${typeName}.${variantClassName}()';
153
+ String toString() => '${typeName}.${variantName}()';
153
154
  }`);
154
155
 
155
156
  encoderVariants.push(
@@ -217,7 +218,7 @@ ${fieldDecls}
217
218
  int get hashCode => ${hashExpression};
218
219
 
219
220
  @override
220
- String toString() => '${typeName}.${variantClassName}(${toStringFields})';
221
+ String toString() => '${typeName}.${variantName}(${toStringFields})';
221
222
  }`);
222
223
 
223
224
  const encFields = fieldManifests
@@ -288,7 +289,7 @@ ${fieldDecls}
288
289
  int get hashCode => value.hashCode;
289
290
 
290
291
  @override
291
- String toString() => '${typeName}.${variantClassName}(\$value)';
292
+ String toString() => '${typeName}.${variantName}(\$value)';
292
293
  }`);
293
294
  encoderVariants.push(
294
295
  `(${i}, transformEncoder<${manifest.type.content}, Map<String, Object?>>(${manifest.encoder.content}, (Map<String, Object?> map) => map['value']! as ${manifest.type.content}))`,
@@ -77,15 +77,20 @@ export class DartImportMap {
77
77
  for (const module of this._imports) {
78
78
  // Check internal map first (generated cross-references)
79
79
  if (module in internalMap) {
80
- uris.add(internalMap[module]);
80
+ const uri = internalMap[module];
81
+ if (uri) {
82
+ uris.add(uri);
83
+ }
81
84
  }
82
85
  // Then external package map
83
86
  else if (module in DART_EXTERNAL_PACKAGE_MAP) {
84
87
  uris.add(DART_EXTERNAL_PACKAGE_MAP[module]);
85
88
  }
86
- // Assume it's already a raw URI (e.g., "package:..." or relative path)
87
- else {
89
+ // Finally, accept explicit Dart URIs and relative Dart paths.
90
+ else if (isRawDartImportUri(module)) {
88
91
  uris.add(module);
92
+ } else {
93
+ throw new Error(`Unresolved Dart import module "${module}"`);
89
94
  }
90
95
  }
91
96
 
@@ -127,3 +132,11 @@ export class DartImportMap {
127
132
  return lines.join("\n");
128
133
  }
129
134
  }
135
+
136
+ function isRawDartImportUri(module: string): boolean {
137
+ return (
138
+ module.startsWith("dart:") ||
139
+ module.startsWith("package:") ||
140
+ (module.endsWith(".dart") && !module.includes(":"))
141
+ );
142
+ }
@@ -66,7 +66,7 @@ export interface DartNameApi {
66
66
  errorMessageFunction(name: string): string;
67
67
  /** Enum variant: `camelCase` */
68
68
  enumVariant(name: string): string;
69
- /** Sealed class variant (data enum): `PascalCase` */
69
+ /** Sealed class variant (data enum): `{ParentName}{PascalCase}` */
70
70
  sealedClassVariant(parentName: string, variantName: string): string;
71
71
  /** File name: `snake_case.dart` */
72
72
  fileName(name: string): string;
@@ -97,7 +97,8 @@ export function createDartNameApi(): DartNameApi {
97
97
  `${camelCase(programName)}Error${pascalCase(errorName)}`,
98
98
  errorMessageFunction: (name) => `get${pascalCase(name)}ErrorMessage`,
99
99
  enumVariant: (name) => camelCase(name),
100
- sealedClassVariant: (_parentName, variantName) => pascalCase(variantName),
100
+ sealedClassVariant: (parentName, variantName) =>
101
+ `${pascalCase(parentName)}${pascalCase(variantName)}`,
101
102
  isTypeFunction: (name) => `is${pascalCase(name)}`,
102
103
  fileName: (name) => snakeCase(name),
103
104
  discriminatorConstant: (name) => `${camelCase(name)}Discriminator`,
@@ -44,10 +44,10 @@ export const WELL_KNOWN_ADDRESSES: ReadonlyMap<string, string> = new Map([
44
44
 
45
45
  // SPL programs
46
46
  ['TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', 'tokenProgramAddress'],
47
- ['TokenzQdBNb4qyze1S1U9AHB8MGXmNK1REkTPT5Z3Y', 'token2022ProgramAddress'],
48
- ['ATokenGPvbdGVxr1b2hvZbsiqW5xWH25ef7s3c8BnQKu', 'associatedTokenProgramAddress'],
49
- ['Memo1UhkJRfRhVq1sR7Y7Nto1s3J2mXTT3L6Wk4K2m6', 'memoProgramAddress'],
50
- ['MemoSq4gqABXKKYWVqKkV3m3kb1cTR7F2UMy9r6kFNK', 'memoLegacyProgramAddress'],
47
+ ['TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb', 'token2022ProgramAddress'],
48
+ ['ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL', 'associatedTokenProgramAddress'],
49
+ ['MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr', 'memoProgramAddress'],
50
+ ['Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo', 'memoLegacyProgramAddress'],
51
51
 
52
52
  // Metaplex programs
53
53
  ['metaqbxxU9qX8rZXkmUJ7haCrXXz2W1PSUq1Rn3E1od', 'tokenMetadataProgramAddress'],
@@ -226,18 +226,19 @@ export function getTypeManifestVisitor(input: {
226
226
 
227
227
  visitArrayType(node: ArrayTypeNode, { self }) {
228
228
  const itemManifest = visit(node.item, self);
229
- const sizeExpr = getArraySizeExpression(node);
229
+ const encoderSizeExpr = getArraySizeExpression(node, "Encoder");
230
+ const decoderSizeExpr = getArraySizeExpression(node, "Decoder");
230
231
 
231
232
  return {
232
233
  type: fragment`List<${itemManifest.type}>`,
233
234
  encoder: fragment`${use(
234
235
  "getArrayEncoder",
235
236
  "solanaCodecsDataStructures",
236
- )}(${itemManifest.encoder}${sizeExpr})`,
237
+ )}(${itemManifest.encoder}${encoderSizeExpr})`,
237
238
  decoder: fragment`${use(
238
239
  "getArrayDecoder",
239
240
  "solanaCodecsDataStructures",
240
- )}(${itemManifest.decoder}${sizeExpr})`,
241
+ )}(${itemManifest.decoder}${decoderSizeExpr})`,
241
242
  value: emptyTypeManifest().value,
242
243
  isEnum: false,
243
244
  };
@@ -246,18 +247,19 @@ export function getTypeManifestVisitor(input: {
246
247
  visitMapType(node: MapTypeNode, { self }) {
247
248
  const keyManifest = visit(node.key, self);
248
249
  const valueManifest = visit(node.value, self);
249
- const sizeExpr = getMapSizeExpression(node);
250
+ const encoderSizeExpr = getMapSizeExpression(node, "Encoder");
251
+ const decoderSizeExpr = getMapSizeExpression(node, "Decoder");
250
252
 
251
253
  return {
252
254
  type: fragment`Map<${keyManifest.type}, ${valueManifest.type}>`,
253
255
  encoder: fragment`${use(
254
256
  "getMapEncoder",
255
257
  "solanaCodecsDataStructures",
256
- )}(${keyManifest.encoder}, ${valueManifest.encoder}${sizeExpr})`,
258
+ )}(${keyManifest.encoder}, ${valueManifest.encoder}${encoderSizeExpr})`,
257
259
  decoder: fragment`${use(
258
260
  "getMapDecoder",
259
261
  "solanaCodecsDataStructures",
260
- )}(${keyManifest.decoder}, ${valueManifest.decoder}${sizeExpr})`,
262
+ )}(${keyManifest.decoder}, ${valueManifest.decoder}${decoderSizeExpr})`,
261
263
  value: emptyTypeManifest().value,
262
264
  isEnum: false,
263
265
  };
@@ -265,18 +267,19 @@ export function getTypeManifestVisitor(input: {
265
267
 
266
268
  visitSetType(node: SetTypeNode, { self }) {
267
269
  const itemManifest = visit(node.item, self);
268
- const sizeExpr = getSetSizeExpression(node);
270
+ const encoderSizeExpr = getSetSizeExpression(node, "Encoder");
271
+ const decoderSizeExpr = getSetSizeExpression(node, "Decoder");
269
272
 
270
273
  return {
271
274
  type: fragment`Set<${itemManifest.type}>`,
272
275
  encoder: fragment`${use(
273
276
  "getSetEncoder",
274
277
  "solanaCodecsDataStructures",
275
- )}(${itemManifest.encoder}${sizeExpr})`,
278
+ )}(${itemManifest.encoder}${encoderSizeExpr})`,
276
279
  decoder: fragment`${use(
277
280
  "getSetDecoder",
278
281
  "solanaCodecsDataStructures",
279
- )}(${itemManifest.decoder}${sizeExpr})`,
282
+ )}(${itemManifest.decoder}${decoderSizeExpr})`,
280
283
  value: emptyTypeManifest().value,
281
284
  isEnum: false,
282
285
  };
@@ -509,20 +512,21 @@ export function getTypeManifestVisitor(input: {
509
512
  const innerManifest = visit(node.type, self);
510
513
 
511
514
  // The Dart addEncoderSizePrefix / addDecoderSizePrefix APIs require
512
- // Encoder<num> / Decoder<num> for the prefix. BigInt-width number types
513
- // (u64, u128, i64, i128) return Encoder<BigInt> which is incompatible.
514
- // When the IDL specifies such a prefix, substitute u32 which is wide
515
- // enough for any realistic size prefix and satisfies the num constraint.
515
+ // Encoder<num> / Decoder<num> for the prefix, but BigInt-width number
516
+ // types (u64, u128, i64, i128) return Encoder<BigInt> which is
517
+ // incompatible. Transform the BigInt codec to int so it satisfies the
518
+ // num constraint while preserving the on-chain byte width (e.g. the
519
+ // system program's bincode u64 string-length prefix).
516
520
  const bigIntFormats = new Set(["u64", "u128", "i64", "i128"]);
517
521
  let prefixManifest;
518
522
  if (
519
523
  node.prefix.kind === "numberTypeNode" &&
520
524
  bigIntFormats.has((node.prefix as any).format)
521
525
  ) {
522
- // Use u32 encoder/decoder instead of the oversized type.
526
+ const codecName = getNumberCodecName((node.prefix as any).format);
523
527
  prefixManifest = {
524
- encoder: fragment`${use("getU32Encoder", "solanaCodecsNumbers")}()`,
525
- decoder: fragment`${use("getU32Decoder", "solanaCodecsNumbers")}()`,
528
+ encoder: fragment`transformEncoder(${use(`get${codecName}Encoder`, "solanaCodecsNumbers")}(), (size) => BigInt.from(size))`,
529
+ decoder: fragment`transformDecoder(${use(`get${codecName}Decoder`, "solanaCodecsNumbers")}(), (size, _, __) => size.toInt())`,
526
530
  };
527
531
  } else {
528
532
  prefixManifest = visit(node.prefix, self);
@@ -757,7 +761,10 @@ function getNumberCodecExpression(
757
761
  return `get${name}${codecType}()`;
758
762
  }
759
763
 
760
- function getArraySizeExpression(node: ArrayTypeNode): string {
764
+ function getArraySizeExpression(
765
+ node: ArrayTypeNode,
766
+ codecType: "Encoder" | "Decoder",
767
+ ): string {
761
768
  if (!("count" in node) || !node.count) return "";
762
769
  const count = node.count;
763
770
  switch (count.kind) {
@@ -770,14 +777,17 @@ function getArraySizeExpression(node: ArrayTypeNode): string {
770
777
  if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
771
778
  return ""; // Default
772
779
  }
773
- return `, size: PrefixedArraySize(get${getNumberCodecName(resolvedPrefix.format)}Encoder())`;
780
+ return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix.format, codecType)})`;
774
781
  }
775
782
  default:
776
783
  return "";
777
784
  }
778
785
  }
779
786
 
780
- function getMapSizeExpression(node: MapTypeNode): string {
787
+ function getMapSizeExpression(
788
+ node: MapTypeNode,
789
+ codecType: "Encoder" | "Decoder",
790
+ ): string {
781
791
  if (!("count" in node) || !node.count) return "";
782
792
  const count = node.count;
783
793
  switch (count.kind) {
@@ -790,14 +800,17 @@ function getMapSizeExpression(node: MapTypeNode): string {
790
800
  if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
791
801
  return "";
792
802
  }
793
- return `, size: PrefixedArraySize(get${getNumberCodecName(resolvedPrefix.format)}Encoder())`;
803
+ return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix.format, codecType)})`;
794
804
  }
795
805
  default:
796
806
  return "";
797
807
  }
798
808
  }
799
809
 
800
- function getSetSizeExpression(node: SetTypeNode): string {
810
+ function getSetSizeExpression(
811
+ node: SetTypeNode,
812
+ codecType: "Encoder" | "Decoder",
813
+ ): string {
801
814
  if (!("count" in node) || !node.count) return "";
802
815
  const count = node.count;
803
816
  switch (count.kind) {
@@ -810,7 +823,7 @@ function getSetSizeExpression(node: SetTypeNode): string {
810
823
  if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
811
824
  return "";
812
825
  }
813
- return `, size: PrefixedArraySize(get${getNumberCodecName(resolvedPrefix.format)}Encoder())`;
826
+ return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix.format, codecType)})`;
814
827
  }
815
828
  default:
816
829
  return "";
@@ -43,7 +43,7 @@ export function renderVisitor(
43
43
  // 3. Build a map of definedType module keys to their render map paths
44
44
  const typePathMap: Record<string, string> = {};
45
45
  for (const renderPath of renderMap.keys()) {
46
- const match = renderPath.match(/^(?:.*\/)?types\/([a-z_]+)\.dart$/);
46
+ const match = renderPath.match(/^(?:.*\/)?types\/([a-z0-9_]+)\.dart$/);
47
47
  if (match) {
48
48
  typePathMap[`definedType:${match[1]}`] = renderPath;
49
49
  }
@@ -63,8 +63,12 @@ export function renderVisitor(
63
63
  const fileDir = dirname(filePath);
64
64
  const internalMap: Record<string, string> = {};
65
65
  for (const [key, typePath] of Object.entries(typePathMap)) {
66
- // Don't import yourself
67
- if (typePath === filePath) continue;
66
+ // Keep the key resolved without generating a self-import.
67
+ if (typePath === filePath) {
68
+ internalMap[key] = "";
69
+ continue;
70
+ }
71
+
68
72
  let rel = posix.relative(fileDir, typePath);
69
73
  if (!rel.startsWith(".")) {
70
74
  rel = `./${rel}`;