codama-renderers-dart 0.5.3 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/index.browser.cjs +176 -107
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.js +177 -108
- package/dist/index.browser.js.map +1 -1
- package/dist/index.node.cjs +176 -107
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.js +177 -108
- package/dist/index.node.js.map +1 -1
- package/dist/index.react-native.js +177 -108
- package/dist/index.react-native.js.map +1 -1
- package/dist/types/fragments/accountPage.d.ts.map +1 -1
- package/dist/types/fragments/errorPage.d.ts.map +1 -1
- package/dist/types/fragments/instructionPage.d.ts.map +1 -1
- package/dist/types/fragments/pdaPage.d.ts.map +1 -1
- package/dist/types/fragments/programPage.d.ts.map +1 -1
- package/dist/types/utils/nameTransformers.d.ts.map +1 -1
- package/dist/types/utils/valueNodes.d.ts +4 -0
- package/dist/types/utils/valueNodes.d.ts.map +1 -1
- package/dist/types/visitors/getRenderMapVisitor.d.ts.map +1 -1
- package/dist/types/visitors/getTypeManifestVisitor.d.ts.map +1 -1
- package/dist/types/visitors/renderVisitor.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/fragments/accountPage.ts +2 -1
- package/src/fragments/errorPage.ts +8 -5
- package/src/fragments/instructionPage.ts +51 -21
- package/src/fragments/pdaPage.ts +7 -14
- package/src/fragments/programPage.ts +3 -2
- package/src/utils/formatCode.ts +2 -2
- package/src/utils/nameTransformers.ts +11 -0
- package/src/utils/valueNodes.ts +24 -1
- package/src/visitors/getRenderMapVisitor.ts +30 -5
- package/src/visitors/getTypeManifestVisitor.ts +100 -75
- package/src/visitors/renderVisitor.ts +11 -13
package/dist/index.node.js
CHANGED
|
@@ -2,13 +2,13 @@ import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
|
2
2
|
import { join, dirname, posix } from 'path';
|
|
3
3
|
import { staticVisitor, extendVisitor, visit, NodeStack, LinkableDictionary, pipe, recordNodeStackVisitor, recordLinkablesOnFirstVisitVisitor, rootNodeVisitor } from '@codama/visitors-core';
|
|
4
4
|
import { createRenderMap, addToRenderMap, mergeRenderMaps, deleteDirectory } from '@codama/renderers-core';
|
|
5
|
-
import {
|
|
5
|
+
import { execFileSync } from 'child_process';
|
|
6
6
|
import { resolveNestedTypeNode, getAllInstructionsWithSubs } from '@codama/nodes';
|
|
7
7
|
import { CodamaError, CODAMA_ERROR__UNEXPECTED_NODE_KIND } from '@codama/errors';
|
|
8
8
|
|
|
9
9
|
// src/visitors/renderVisitor.ts
|
|
10
10
|
function formatDartDirectory(dir) {
|
|
11
|
-
|
|
11
|
+
execFileSync("dart", ["format", "--", dir], {
|
|
12
12
|
encoding: "utf-8",
|
|
13
13
|
timeout: 6e4,
|
|
14
14
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -78,6 +78,7 @@ function setEmptyCollection(record, key) {
|
|
|
78
78
|
|
|
79
79
|
// src/utils/nameTransformers.ts
|
|
80
80
|
function pascalCase(str) {
|
|
81
|
+
assertSafeName(str);
|
|
81
82
|
return str.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^(.)/, (c) => c.toUpperCase());
|
|
82
83
|
}
|
|
83
84
|
var DART_RESERVED_WORDS = /* @__PURE__ */ new Set([
|
|
@@ -159,6 +160,7 @@ function camelCase(str) {
|
|
|
159
160
|
return escapeDartIdentifier(cased);
|
|
160
161
|
}
|
|
161
162
|
function snakeCase(str) {
|
|
163
|
+
assertSafeName(str);
|
|
162
164
|
return str.replace(/([A-Z])/g, "_$1").replace(/[-\s]+/g, "_").replace(/^_/, "").replace(/_+/g, "_").toLowerCase();
|
|
163
165
|
}
|
|
164
166
|
function createDartNameApi() {
|
|
@@ -184,6 +186,11 @@ function createDartNameApi() {
|
|
|
184
186
|
discriminatorConstant: (name) => `${camelCase(name)}Discriminator`
|
|
185
187
|
};
|
|
186
188
|
}
|
|
189
|
+
function assertSafeName(name) {
|
|
190
|
+
if (!/^[a-zA-Z0-9_ -]*$/.test(name)) {
|
|
191
|
+
throw new Error(`Invalid Dart node name: ${JSON.stringify(name)}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
187
194
|
|
|
188
195
|
// src/utils/codecs.ts
|
|
189
196
|
function bytesToDartHexList(bytes) {
|
|
@@ -394,10 +401,19 @@ var WELL_KNOWN_ADDRESSES = /* @__PURE__ */ new Map([
|
|
|
394
401
|
function getDartValueFragment(value, dartType) {
|
|
395
402
|
switch (value.kind) {
|
|
396
403
|
case "numberValueNode":
|
|
404
|
+
if (typeof value.number !== "number" || !Number.isFinite(value.number)) {
|
|
405
|
+
throw new Error("Invalid Dart number: expected a finite number");
|
|
406
|
+
}
|
|
407
|
+
if (dartType === "BigInt" && !Number.isSafeInteger(value.number)) {
|
|
408
|
+
throw new Error("Invalid Dart BigInt: expected a safe integer");
|
|
409
|
+
}
|
|
397
410
|
return fragmentFromString(
|
|
398
411
|
dartType === "BigInt" ? `BigInt.from(${value.number})` : String(value.number)
|
|
399
412
|
);
|
|
400
413
|
case "booleanValueNode":
|
|
414
|
+
if (typeof value.boolean !== "boolean") {
|
|
415
|
+
throw new Error("Invalid Dart boolean: expected a boolean");
|
|
416
|
+
}
|
|
401
417
|
return fragmentFromString(String(value.boolean));
|
|
402
418
|
case "stringValueNode":
|
|
403
419
|
return fragmentFromString(toDartStringLiteral(value.string));
|
|
@@ -441,6 +457,12 @@ function isConstDartValueNode(value) {
|
|
|
441
457
|
function toDartStringLiteral(value) {
|
|
442
458
|
return `'${value.replaceAll("\\", "\\\\").replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t").replaceAll("\b", "\\b").replaceAll("\f", "\\f").replaceAll("$", "\\$").replaceAll("'", "\\'")}'`;
|
|
443
459
|
}
|
|
460
|
+
function getNonNegativeInteger(value) {
|
|
461
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
462
|
+
throw new Error("Expected a non-negative safe integer value");
|
|
463
|
+
}
|
|
464
|
+
return String(value);
|
|
465
|
+
}
|
|
444
466
|
|
|
445
467
|
// src/utils/discriminators.ts
|
|
446
468
|
function getDiscriminatorValidationFragment(node, scope) {
|
|
@@ -641,23 +663,12 @@ function getTypeManifestVisitor(input) {
|
|
|
641
663
|
}
|
|
642
664
|
);
|
|
643
665
|
return extendVisitor(baseVisitor, {
|
|
644
|
-
visitNumberType(node
|
|
645
|
-
const { format, endian } = node;
|
|
646
|
-
const endianSuffix = endian === "be" ? ", NumberCodecConfig(endian: Endian.big)" : "";
|
|
647
|
-
const endianDecoderSuffix = endian === "be" ? ", NumberCodecConfig(endian: Endian.big)" : "";
|
|
648
|
-
const dartType = getNumberDartType(format);
|
|
649
|
-
const codecName = getNumberCodecName(format);
|
|
666
|
+
visitNumberType(node) {
|
|
650
667
|
return {
|
|
651
668
|
// `BigInt` and other number types live in `dart:core` and need no import.
|
|
652
|
-
type: fragmentFromString(
|
|
653
|
-
encoder:
|
|
654
|
-
|
|
655
|
-
"solanaCodecsNumbers"
|
|
656
|
-
)}(${endianSuffix ? fragmentFromString(endianSuffix.slice(2)) : fragmentFromString("")})`,
|
|
657
|
-
decoder: fragment`${use(
|
|
658
|
-
`get${codecName}Decoder`,
|
|
659
|
-
"solanaCodecsNumbers"
|
|
660
|
-
)}(${endianDecoderSuffix ? fragmentFromString(endianDecoderSuffix.slice(2)) : fragmentFromString("")})`,
|
|
669
|
+
type: fragmentFromString(getNumberDartType(node.format)),
|
|
670
|
+
encoder: getNumberCodecExpression(node, "Encoder"),
|
|
671
|
+
decoder: getNumberCodecExpression(node, "Decoder"),
|
|
661
672
|
value: emptyTypeManifest().value,
|
|
662
673
|
isEnum: false
|
|
663
674
|
};
|
|
@@ -838,7 +849,7 @@ function getTypeManifestVisitor(input) {
|
|
|
838
849
|
const fields = nodeFields.map((f) => visit(f, self));
|
|
839
850
|
const fieldNames = nodeFields.map((f) => f.name);
|
|
840
851
|
const typeStr = "Map<String, Object?>";
|
|
841
|
-
const encoderFields = fields.map((f, i) => `(
|
|
852
|
+
const encoderFields = fields.map((f, i) => `(${toDartStringLiteral(fieldNames[i])}, ${f.encoder.content})`).join(", ");
|
|
842
853
|
const encoderFrag = fragment`${use(
|
|
843
854
|
"getStructEncoder",
|
|
844
855
|
"solanaCodecsDataStructures"
|
|
@@ -846,7 +857,7 @@ function getTypeManifestVisitor(input) {
|
|
|
846
857
|
for (const f of fields) {
|
|
847
858
|
encoderFrag.imports.mergeWith(f.encoder.imports);
|
|
848
859
|
}
|
|
849
|
-
const decoderFields = fields.map((f, i) => `(
|
|
860
|
+
const decoderFields = fields.map((f, i) => `(${toDartStringLiteral(fieldNames[i])}, ${f.decoder.content})`).join(", ");
|
|
850
861
|
const decoderFrag = fragment`${use(
|
|
851
862
|
"getStructDecoder",
|
|
852
863
|
"solanaCodecsDataStructures"
|
|
@@ -900,8 +911,8 @@ function getTypeManifestVisitor(input) {
|
|
|
900
911
|
const itemManifest = visit(node.item, self);
|
|
901
912
|
const itemTypeStr = itemManifest.type.content;
|
|
902
913
|
const resolvedPrefix = resolveNestedTypeNode(node.prefix);
|
|
903
|
-
const prefixExpr = resolvedPrefix.format !== "u8" ? `, prefix: ${getNumberCodecExpression(resolvedPrefix
|
|
904
|
-
const prefixDecoderExpr = resolvedPrefix.format !== "u8" ? `, prefix: ${getNumberCodecExpression(resolvedPrefix
|
|
914
|
+
const prefixExpr = resolvedPrefix.format !== "u8" ? fragment`, prefix: ${getNumberCodecExpression(resolvedPrefix, "Encoder")}` : "";
|
|
915
|
+
const prefixDecoderExpr = resolvedPrefix.format !== "u8" ? fragment`, prefix: ${getNumberCodecExpression(resolvedPrefix, "Decoder")}` : "";
|
|
905
916
|
const noneValueExpr = node.fixed ? ", noneValue: const ZeroesNoneValue()" : "";
|
|
906
917
|
return {
|
|
907
918
|
type: fragment`${itemManifest.type}?`,
|
|
@@ -911,11 +922,11 @@ function getTypeManifestVisitor(input) {
|
|
|
911
922
|
)}<${fragmentFromString(itemTypeStr)}>(${use(
|
|
912
923
|
"transformEncoder",
|
|
913
924
|
"solanaCodecsCore"
|
|
914
|
-
)}(${itemManifest.encoder}, (${fragmentFromString(itemTypeStr)} value) => value)${
|
|
925
|
+
)}(${itemManifest.encoder}, (${fragmentFromString(itemTypeStr)} value) => value)${prefixExpr}${fragmentFromString(noneValueExpr)})`,
|
|
915
926
|
decoder: fragment`${use(
|
|
916
927
|
"getNullableDecoder",
|
|
917
928
|
"solanaCodecsDataStructures"
|
|
918
|
-
)}<${fragmentFromString(itemTypeStr)}>(${itemManifest.decoder}${
|
|
929
|
+
)}<${fragmentFromString(itemTypeStr)}>(${itemManifest.decoder}${prefixDecoderExpr}${fragmentFromString(noneValueExpr)})`,
|
|
919
930
|
value: emptyTypeManifest().value,
|
|
920
931
|
isEnum: false
|
|
921
932
|
};
|
|
@@ -961,11 +972,11 @@ function getTypeManifestVisitor(input) {
|
|
|
961
972
|
encoder: fragment`${use(
|
|
962
973
|
"fixEncoderSize",
|
|
963
974
|
"solanaCodecsCore"
|
|
964
|
-
)}(${innerManifest.encoder}, ${fragmentFromString(
|
|
975
|
+
)}(${innerManifest.encoder}, ${fragmentFromString(getNonNegativeInteger(node.size))}, allowTruncation: false)`,
|
|
965
976
|
decoder: fragment`${use(
|
|
966
977
|
"fixDecoderSize",
|
|
967
978
|
"solanaCodecsCore"
|
|
968
|
-
)}(${innerManifest.decoder}, ${fragmentFromString(
|
|
979
|
+
)}(${innerManifest.decoder}, ${fragmentFromString(getNonNegativeInteger(node.size))})`,
|
|
969
980
|
value: innerManifest.value,
|
|
970
981
|
isEnum: innerManifest.isEnum
|
|
971
982
|
};
|
|
@@ -975,10 +986,10 @@ function getTypeManifestVisitor(input) {
|
|
|
975
986
|
const bigIntFormats = /* @__PURE__ */ new Set(["u64", "u128", "i64", "i128"]);
|
|
976
987
|
let prefixManifest;
|
|
977
988
|
if (node.prefix.kind === "numberTypeNode" && bigIntFormats.has(node.prefix.format)) {
|
|
978
|
-
const
|
|
989
|
+
const numberManifest = visit(node.prefix, self);
|
|
979
990
|
prefixManifest = {
|
|
980
|
-
encoder: fragment
|
|
981
|
-
decoder: fragment
|
|
991
|
+
encoder: fragment`${use("transformEncoder", "solanaCodecsCore")}(${numberManifest.encoder}, (size) => BigInt.from(size))`,
|
|
992
|
+
decoder: fragment`${use("transformDecoder", "solanaCodecsCore")}(${numberManifest.decoder}, (size, _, __) => size.toInt())`
|
|
982
993
|
};
|
|
983
994
|
} else {
|
|
984
995
|
prefixManifest = visit(node.prefix, self);
|
|
@@ -1069,10 +1080,10 @@ function getTypeManifestVisitor(input) {
|
|
|
1069
1080
|
};
|
|
1070
1081
|
},
|
|
1071
1082
|
visitPreOffsetType(node, { self }) {
|
|
1072
|
-
return
|
|
1083
|
+
return getOffsetManifest(node, self);
|
|
1073
1084
|
},
|
|
1074
1085
|
visitPostOffsetType(node, { self }) {
|
|
1075
|
-
return
|
|
1086
|
+
return getOffsetManifest(node, self);
|
|
1076
1087
|
},
|
|
1077
1088
|
visitSolAmountType(node, { self }) {
|
|
1078
1089
|
return visit(node.number, self);
|
|
@@ -1161,16 +1172,17 @@ function getNumberCodecName(format) {
|
|
|
1161
1172
|
return "U8";
|
|
1162
1173
|
}
|
|
1163
1174
|
}
|
|
1164
|
-
function getNumberCodecExpression(
|
|
1165
|
-
const name = getNumberCodecName(format);
|
|
1166
|
-
|
|
1175
|
+
function getNumberCodecExpression(node, codecType) {
|
|
1176
|
+
const name = getNumberCodecName(node.format);
|
|
1177
|
+
const config = node.endian === "be" ? fragment`${use("NumberCodecConfig", "solanaCodecsNumbers")}(endian: ${use("Endian", "dartTypedData")}.big)` : "";
|
|
1178
|
+
return fragment`${use(`get${name}${codecType}`, "solanaCodecsNumbers")}(${config})`;
|
|
1167
1179
|
}
|
|
1168
1180
|
function getArraySizeExpression(node, codecType) {
|
|
1169
1181
|
if (!("count" in node) || !node.count) return "";
|
|
1170
1182
|
const count = node.count;
|
|
1171
1183
|
switch (count.kind) {
|
|
1172
1184
|
case "fixedCountNode":
|
|
1173
|
-
return `, size: FixedArraySize(${count.value})`;
|
|
1185
|
+
return `, size: FixedArraySize(${getNonNegativeInteger(count.value)})`;
|
|
1174
1186
|
case "remainderCountNode":
|
|
1175
1187
|
return ", size: RemainderArraySize()";
|
|
1176
1188
|
case "prefixedCountNode": {
|
|
@@ -1178,7 +1190,7 @@ function getArraySizeExpression(node, codecType) {
|
|
|
1178
1190
|
if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
|
|
1179
1191
|
return "";
|
|
1180
1192
|
}
|
|
1181
|
-
return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix
|
|
1193
|
+
return fragment`, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix, codecType)})`;
|
|
1182
1194
|
}
|
|
1183
1195
|
default:
|
|
1184
1196
|
return "";
|
|
@@ -1189,7 +1201,7 @@ function getMapSizeExpression(node, codecType) {
|
|
|
1189
1201
|
const count = node.count;
|
|
1190
1202
|
switch (count.kind) {
|
|
1191
1203
|
case "fixedCountNode":
|
|
1192
|
-
return `, size: FixedArraySize(${count.value})`;
|
|
1204
|
+
return `, size: FixedArraySize(${getNonNegativeInteger(count.value)})`;
|
|
1193
1205
|
case "remainderCountNode":
|
|
1194
1206
|
return ", size: RemainderArraySize()";
|
|
1195
1207
|
case "prefixedCountNode": {
|
|
@@ -1197,7 +1209,7 @@ function getMapSizeExpression(node, codecType) {
|
|
|
1197
1209
|
if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
|
|
1198
1210
|
return "";
|
|
1199
1211
|
}
|
|
1200
|
-
return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix
|
|
1212
|
+
return fragment`, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix, codecType)})`;
|
|
1201
1213
|
}
|
|
1202
1214
|
default:
|
|
1203
1215
|
return "";
|
|
@@ -1208,7 +1220,7 @@ function getSetSizeExpression(node, codecType) {
|
|
|
1208
1220
|
const count = node.count;
|
|
1209
1221
|
switch (count.kind) {
|
|
1210
1222
|
case "fixedCountNode":
|
|
1211
|
-
return `, size: FixedArraySize(${count.value})`;
|
|
1223
|
+
return `, size: FixedArraySize(${getNonNegativeInteger(count.value)})`;
|
|
1212
1224
|
case "remainderCountNode":
|
|
1213
1225
|
return ", size: RemainderArraySize()";
|
|
1214
1226
|
case "prefixedCountNode": {
|
|
@@ -1216,7 +1228,7 @@ function getSetSizeExpression(node, codecType) {
|
|
|
1216
1228
|
if (resolvedPrefix.format === "u32" && (!resolvedPrefix.endian || resolvedPrefix.endian === "le")) {
|
|
1217
1229
|
return "";
|
|
1218
1230
|
}
|
|
1219
|
-
return `, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix
|
|
1231
|
+
return fragment`, size: PrefixedArraySize(${getNumberCodecExpression(resolvedPrefix, codecType)})`;
|
|
1220
1232
|
}
|
|
1221
1233
|
default:
|
|
1222
1234
|
return "";
|
|
@@ -1227,36 +1239,61 @@ function getHiddenAffixManifest(node, self) {
|
|
|
1227
1239
|
const manifest = visit(node, self);
|
|
1228
1240
|
return { encoder: manifest.encoder, decoder: manifest.decoder };
|
|
1229
1241
|
}
|
|
1230
|
-
const constantBytes = getConstantBytesExpression(node);
|
|
1242
|
+
const constantBytes = getConstantBytesExpression(node, self);
|
|
1231
1243
|
return {
|
|
1232
1244
|
encoder: fragment`${use("getConstantEncoder", "solanaCodecsDataStructures")}(${constantBytes})`,
|
|
1233
1245
|
decoder: fragment`${use("getConstantDecoder", "solanaCodecsDataStructures")}(${constantBytes})`
|
|
1234
1246
|
};
|
|
1235
1247
|
}
|
|
1236
|
-
function getConstantBytesExpression(node) {
|
|
1237
|
-
|
|
1238
|
-
const typeNode = node.type;
|
|
1239
|
-
if (valueNode?.kind !== "numberValueNode") {
|
|
1248
|
+
function getConstantBytesExpression(node, self) {
|
|
1249
|
+
if (node.value.kind !== "numberValueNode") {
|
|
1240
1250
|
throw new Error(
|
|
1241
|
-
`Unsupported hidden affix constant value kind: ${
|
|
1251
|
+
`Unsupported hidden affix constant value kind: ${node.value.kind}`
|
|
1242
1252
|
);
|
|
1243
1253
|
}
|
|
1244
|
-
const
|
|
1245
|
-
if (
|
|
1246
|
-
|
|
1254
|
+
const manifest = visit(node.type, self);
|
|
1255
|
+
if (!["int", "BigInt", "double"].includes(manifest.type.content)) {
|
|
1256
|
+
throw new Error(`Unsupported hidden affix constant type kind: ${node.type.kind}`);
|
|
1247
1257
|
}
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
}
|
|
1255
|
-
return fragment`${use("padLeftEncoder", "solanaCodecsCore")}(${use(`get${getNumberCodecName(inner.format)}Encoder`, "solanaCodecsNumbers")}(), ${typeNode.offset}).encode(${value})`;
|
|
1258
|
+
const value = getDartValueFragment(node.value, manifest.type.content);
|
|
1259
|
+
return fragment`${manifest.encoder}.encode(${value})`;
|
|
1260
|
+
}
|
|
1261
|
+
function getOffsetManifest(node, self) {
|
|
1262
|
+
if (!Number.isSafeInteger(node.offset)) {
|
|
1263
|
+
throw new Error("Expected a safe integer for a codec offset");
|
|
1256
1264
|
}
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1265
|
+
const manifest = visit(node.type, self);
|
|
1266
|
+
const pre = node.kind === "preOffsetTypeNode";
|
|
1267
|
+
const key = pre ? "preOffset" : "postOffset";
|
|
1268
|
+
if (node.strategy === "padded") {
|
|
1269
|
+
const side = pre ? "Left" : "Right";
|
|
1270
|
+
const offset = getNonNegativeInteger(node.offset);
|
|
1271
|
+
return {
|
|
1272
|
+
...manifest,
|
|
1273
|
+
encoder: fragment`${use(`pad${side}Encoder`, "solanaCodecsCore")}(${manifest.encoder}, ${offset})`,
|
|
1274
|
+
decoder: fragment`${use(`pad${side}Decoder`, "solanaCodecsCore")}(${manifest.decoder}, ${offset})`
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
let expression;
|
|
1278
|
+
switch (node.strategy) {
|
|
1279
|
+
case "absolute":
|
|
1280
|
+
expression = node.offset < 0 ? `scope.wrapBytes(${node.offset})` : String(node.offset);
|
|
1281
|
+
break;
|
|
1282
|
+
case "relative":
|
|
1283
|
+
expression = `scope.${key} + ${node.offset}`;
|
|
1284
|
+
break;
|
|
1285
|
+
case "preOffset":
|
|
1286
|
+
expression = `scope.preOffset + ${node.offset}`;
|
|
1287
|
+
break;
|
|
1288
|
+
default:
|
|
1289
|
+
throw new Error(`Unsupported offset strategy: ${node.strategy}`);
|
|
1290
|
+
}
|
|
1291
|
+
const config = fragment`${use("OffsetConfig", "solanaCodecsCore")}(${key}: (scope) => ${expression})`;
|
|
1292
|
+
return {
|
|
1293
|
+
...manifest,
|
|
1294
|
+
encoder: fragment`${use("offsetEncoder", "solanaCodecsCore")}(${manifest.encoder}, ${config})`,
|
|
1295
|
+
decoder: fragment`${use("offsetDecoder", "solanaCodecsCore")}(${manifest.decoder}, ${config})`
|
|
1296
|
+
};
|
|
1260
1297
|
}
|
|
1261
1298
|
|
|
1262
1299
|
// src/fragments/discriminatorConstants.ts
|
|
@@ -1389,7 +1426,7 @@ ${ctorParams}
|
|
|
1389
1426
|
const decodeFnName = scope.nameApi.accountDecodeFunction(name);
|
|
1390
1427
|
const sizeFragment = node.size != null ? fragment`
|
|
1391
1428
|
/// The size of the [${fragmentFromString(typeName)}] account data in bytes.
|
|
1392
|
-
const int ${fragmentFromString(scope.nameApi.accountSizeConstant(name))} = ${fragmentFromString(
|
|
1429
|
+
const int ${fragmentFromString(scope.nameApi.accountSizeConstant(name))} = ${fragmentFromString(getNonNegativeInteger(node.size))};` : emptyFragment();
|
|
1393
1430
|
const discFragment = getDiscriminatorConstantsFragment(node, scope);
|
|
1394
1431
|
const discriminatorValidation = getDiscriminatorValidationFragment(
|
|
1395
1432
|
node,
|
|
@@ -1503,9 +1540,10 @@ function getErrorPageFragment(programNode, scope) {
|
|
|
1503
1540
|
programName,
|
|
1504
1541
|
err.name
|
|
1505
1542
|
);
|
|
1543
|
+
getNonNegativeInteger(err.code);
|
|
1506
1544
|
const hexCode = `0x${err.code.toString(16)}`;
|
|
1507
|
-
const docs = err.docs?.length ? err.docs.map(
|
|
1508
|
-
const message = err.message ?
|
|
1545
|
+
const docs = err.docs?.length ? err.docs.map(toDartDocComment).join("\n") + "\n" : "";
|
|
1546
|
+
const message = err.message ? `${toDartDocComment(`Message: "${err.message}"`)}
|
|
1509
1547
|
` : "";
|
|
1510
1548
|
return `${docs}${message}const int ${constName} = ${hexCode}; // ${err.code}`;
|
|
1511
1549
|
}).join("\n\n");
|
|
@@ -1515,7 +1553,7 @@ function getErrorPageFragment(programNode, scope) {
|
|
|
1515
1553
|
err.name
|
|
1516
1554
|
);
|
|
1517
1555
|
const message = err.message ?? err.name;
|
|
1518
|
-
return ` ${constName}:
|
|
1556
|
+
return ` ${constName}: ${toDartStringLiteral(message)},`;
|
|
1519
1557
|
}).join("\n");
|
|
1520
1558
|
const errorMessageFnName = scope.nameApi.errorMessageFunction(programName);
|
|
1521
1559
|
const isErrorFnName = `is${pascalCase(programName)}Error`;
|
|
@@ -1541,8 +1579,8 @@ bool ${fragmentFromString(isErrorFnName)}(int code) {
|
|
|
1541
1579
|
return _${fragmentFromString(camelCase(programName))}ErrorMessages.containsKey(code);
|
|
1542
1580
|
}`;
|
|
1543
1581
|
}
|
|
1544
|
-
function
|
|
1545
|
-
return
|
|
1582
|
+
function toDartDocComment(value) {
|
|
1583
|
+
return value.split(/\r\n?|\n/).map((line) => `/// ${line}`).join("\n");
|
|
1546
1584
|
}
|
|
1547
1585
|
|
|
1548
1586
|
// src/fragments/indexPage.ts
|
|
@@ -1668,14 +1706,28 @@ ${dataCtorInitializers}` : ""};`;
|
|
|
1668
1706
|
const dataEncoderName = `get${typeName}InstructionDataEncoder`;
|
|
1669
1707
|
const dataDecoderName = `get${typeName}InstructionDataDecoder`;
|
|
1670
1708
|
const dataCodecName = `get${typeName}InstructionDataCodec`;
|
|
1671
|
-
const
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1709
|
+
const usedNames = /* @__PURE__ */ new Set([
|
|
1710
|
+
...args.map((arg) => camelCase(arg.name)),
|
|
1711
|
+
...accounts.map((account) => camelCase(account.name))
|
|
1712
|
+
]);
|
|
1713
|
+
const instrProgramParam = reserveName(
|
|
1714
|
+
usedNames.has("programAddress") ? "instructionProgramAddress" : "programAddress",
|
|
1715
|
+
usedNames
|
|
1716
|
+
);
|
|
1717
|
+
const instructionDataLocal = reserveName("instructionData", usedNames);
|
|
1718
|
+
const signerParams = /* @__PURE__ */ new Map();
|
|
1719
|
+
for (const account of accounts) {
|
|
1720
|
+
if (account.isSigner === "either") {
|
|
1721
|
+
signerParams.set(
|
|
1722
|
+
account,
|
|
1723
|
+
reserveName(`${camelCase(account.name)}IsSigner`, usedNames)
|
|
1724
|
+
);
|
|
1725
|
+
}
|
|
1678
1726
|
}
|
|
1727
|
+
const signerParamDeclarations = [...signerParams.values()].map((parameter) => ` bool ${parameter} = true,`).join("\n");
|
|
1728
|
+
const signerParamDocs = [...signerParams.entries()].map(
|
|
1729
|
+
([account, parameter]) => `/// Set [${parameter}] to false when [${camelCase(account.name)}] does not sign (for example, a multisig authority).`
|
|
1730
|
+
).join("\n");
|
|
1679
1731
|
const accountParams = accounts.map((acc) => {
|
|
1680
1732
|
const fieldName = camelCase(acc.name);
|
|
1681
1733
|
const isRequired = !(acc.isOptional ?? false);
|
|
@@ -1695,7 +1747,7 @@ ${dataCtorInitializers}` : ""};`;
|
|
|
1695
1747
|
}).join("\n");
|
|
1696
1748
|
const accountMetas = accounts.map((acc) => {
|
|
1697
1749
|
const fieldName = camelCase(acc.name);
|
|
1698
|
-
const role = getAccountRole(acc);
|
|
1750
|
+
const role = getAccountRole(acc, signerParams.get(acc));
|
|
1699
1751
|
const isOptional = acc.isOptional ?? false;
|
|
1700
1752
|
if (isOptional) {
|
|
1701
1753
|
if (optionalAccountStrategy === "omitted") {
|
|
@@ -1778,11 +1830,13 @@ Codec<${fragmentFromString(dataClassName)}, ${fragmentFromString(dataClassName)}
|
|
|
1778
1830
|
return combineCodec(${fragmentFromString(dataEncoderName)}(), ${fragmentFromString(dataDecoderName)}());
|
|
1779
1831
|
}`);
|
|
1780
1832
|
parts.push(fragment`
|
|
1781
|
-
/// Creates a [${fragmentFromString(typeName)}] instruction
|
|
1833
|
+
/// Creates a [${fragmentFromString(typeName)}] instruction.${fragmentFromString(signerParamDocs ? `
|
|
1834
|
+
${signerParamDocs}` : "")}
|
|
1782
1835
|
Instruction ${fragmentFromString(instrFnName)}({
|
|
1783
1836
|
required Address ${fragmentFromString(instrProgramParam)},
|
|
1784
1837
|
${fragmentFromString(accountParams)}
|
|
1785
|
-
${fragmentFromString(argParams)}
|
|
1838
|
+
${fragmentFromString(argParams)}${fragmentFromString(signerParamDeclarations ? `
|
|
1839
|
+
${signerParamDeclarations}` : "")}
|
|
1786
1840
|
}) {
|
|
1787
1841
|
final ${instructionDataLocal} = ${fragmentFromString(dataClassName)}(
|
|
1788
1842
|
${fragmentFromString(dataConstruction)}
|
|
@@ -1812,13 +1866,20 @@ ${fragmentFromString(dataClassName)} ${fragmentFromString(parseFnName)}(Instruct
|
|
|
1812
1866
|
}
|
|
1813
1867
|
return result;
|
|
1814
1868
|
}
|
|
1815
|
-
function
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
return
|
|
1869
|
+
function reserveName(preferredName, usedNames) {
|
|
1870
|
+
let name = preferredName;
|
|
1871
|
+
while (usedNames.has(name)) {
|
|
1872
|
+
name = `${name}_`;
|
|
1873
|
+
}
|
|
1874
|
+
usedNames.add(name);
|
|
1875
|
+
return name;
|
|
1876
|
+
}
|
|
1877
|
+
function getAccountRole(acc, signerParameter) {
|
|
1878
|
+
const role = acc.isWritable ? "AccountRole.writable" : "AccountRole.readonly";
|
|
1879
|
+
if (signerParameter) {
|
|
1880
|
+
return `${signerParameter} ? ${role}Signer : ${role}`;
|
|
1881
|
+
}
|
|
1882
|
+
return acc.isSigner === true ? `${role}Signer` : role;
|
|
1822
1883
|
}
|
|
1823
1884
|
function isDiscriminatorArg(arg, node) {
|
|
1824
1885
|
const discriminators = node.discriminators ?? [];
|
|
@@ -1871,9 +1932,9 @@ function getPdaPageFragment(node, scope) {
|
|
|
1871
1932
|
for (const seed of seeds) {
|
|
1872
1933
|
if (seed.kind === "constantPdaSeedNode") {
|
|
1873
1934
|
if (seed.value.kind === "bytesValueNode") {
|
|
1874
|
-
seedValues.push(`
|
|
1935
|
+
seedValues.push(` ${getDartValueFragment(seed.value).content},`);
|
|
1875
1936
|
} else if (seed.value.kind === "stringValueNode") {
|
|
1876
|
-
seedValues.push(`
|
|
1937
|
+
seedValues.push(` ${toDartStringLiteral(seed.value.string)},`);
|
|
1877
1938
|
} else if (seed.value.kind === "publicKeyValueNode") {
|
|
1878
1939
|
const wellKnownSeedName = WELL_KNOWN_ADDRESSES.get(seed.value.publicKey);
|
|
1879
1940
|
if (wellKnownSeedName) {
|
|
@@ -1883,13 +1944,14 @@ function getPdaPageFragment(node, scope) {
|
|
|
1883
1944
|
);
|
|
1884
1945
|
} else {
|
|
1885
1946
|
seedValues.push(
|
|
1886
|
-
` getAddressEncoder().encode(Address(
|
|
1947
|
+
` getAddressEncoder().encode(Address(${toDartStringLiteral(seed.value.publicKey)})),`
|
|
1887
1948
|
);
|
|
1888
1949
|
}
|
|
1889
1950
|
} else if (seed.value.kind === "numberValueNode") {
|
|
1890
1951
|
const manifest = visit(seed.type, scope.typeManifestVisitor);
|
|
1891
1952
|
seedManifests.push(manifest);
|
|
1892
|
-
|
|
1953
|
+
const value = getDartValueFragment(seed.value, manifest.type.content);
|
|
1954
|
+
seedValues.push(` ${manifest.encoder.content}.encode(${value.content}),`);
|
|
1893
1955
|
}
|
|
1894
1956
|
} else if (seed.kind === "variablePdaSeedNode") {
|
|
1895
1957
|
const manifest = visit(seed.type, scope.typeManifestVisitor);
|
|
@@ -1903,7 +1965,7 @@ function getPdaPageFragment(node, scope) {
|
|
|
1903
1965
|
if (wellKnownProgramName) {
|
|
1904
1966
|
use(wellKnownProgramName, "solanaAddresses");
|
|
1905
1967
|
}
|
|
1906
|
-
const programIdParam = node.programId ? wellKnownProgramName ? `Address programAddress = ${wellKnownProgramName}` : `Address programAddress = const Address(
|
|
1968
|
+
const programIdParam = node.programId ? wellKnownProgramName ? `Address programAddress = ${wellKnownProgramName}` : `Address programAddress = const Address(${toDartStringLiteral(node.programId)})` : "required Address programAddress";
|
|
1907
1969
|
const typedDataImport = hasByteSeeds ? use("Uint8List", "dartTypedData") : fragmentFromString("");
|
|
1908
1970
|
const metaImport = hasSeedsClass ? use("immutable", "meta") : fragmentFromString("");
|
|
1909
1971
|
const parts = [
|
|
@@ -1950,14 +2012,6 @@ ${fragmentFromString(seedValues.join("\n"))}
|
|
|
1950
2012
|
}
|
|
1951
2013
|
return result;
|
|
1952
2014
|
}
|
|
1953
|
-
function bytesValueToDart(hex) {
|
|
1954
|
-
const clean = hex.replace(/^0x/, "");
|
|
1955
|
-
const pairs = [];
|
|
1956
|
-
for (let i = 0; i < clean.length; i += 2) {
|
|
1957
|
-
pairs.push(`0x${clean.slice(i, i + 2)}`);
|
|
1958
|
-
}
|
|
1959
|
-
return `[${pairs.join(", ")}]`;
|
|
1960
|
-
}
|
|
1961
2015
|
function getProgramPageFragment(node, scope) {
|
|
1962
2016
|
const name = node.name;
|
|
1963
2017
|
const addressConstName = scope.nameApi.programAddressConstant(name);
|
|
@@ -1978,7 +2032,7 @@ const ${fragmentFromString(addressConstName)} = ${fragmentFromString(wellKnownNa
|
|
|
1978
2032
|
${use("Address", "solanaAddresses")}
|
|
1979
2033
|
|
|
1980
2034
|
/// The address of the ${fragmentFromString(pascalCase(name))} program.
|
|
1981
|
-
const ${fragmentFromString(addressConstName)} = Address(
|
|
2035
|
+
const ${fragmentFromString(addressConstName)} = Address(${fragmentFromString(toDartStringLiteral(node.publicKey))});`;
|
|
1982
2036
|
const parts = [addressDeclaration];
|
|
1983
2037
|
const accounts = node.accounts ?? [];
|
|
1984
2038
|
if (accounts.length > 0) {
|
|
@@ -2119,7 +2173,7 @@ function getValueFragment(value, dartType) {
|
|
|
2119
2173
|
case "booleanValueNode":
|
|
2120
2174
|
return fragmentFromString(String(value.boolean));
|
|
2121
2175
|
case "stringValueNode":
|
|
2122
|
-
return fragmentFromString(
|
|
2176
|
+
return fragmentFromString(toDartStringLiteral(value.string));
|
|
2123
2177
|
case "bytesValueNode": {
|
|
2124
2178
|
const clean = value.data.replace(/^0x/, "");
|
|
2125
2179
|
const bytes = clean.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)) ?? [];
|
|
@@ -2685,9 +2739,12 @@ function getRenderMapVisitor(options = {}) {
|
|
|
2685
2739
|
visit(node.program, self),
|
|
2686
2740
|
...(node.additionalPrograms ?? []).map((p) => visit(p, self))
|
|
2687
2741
|
];
|
|
2688
|
-
const mergedProgramMap =
|
|
2742
|
+
const mergedProgramMap = mergeRenderMapsWithoutCollisions(programMaps);
|
|
2689
2743
|
const categoryMaps = getSharedCategoryIndexMaps(mergedProgramMap);
|
|
2690
|
-
return
|
|
2744
|
+
return mergeRenderMapsWithoutCollisions([
|
|
2745
|
+
mergedProgramMap,
|
|
2746
|
+
...categoryMaps
|
|
2747
|
+
]);
|
|
2691
2748
|
},
|
|
2692
2749
|
visitProgram(node, { self }) {
|
|
2693
2750
|
const programName = node.name;
|
|
@@ -2749,7 +2806,7 @@ function getRenderMapVisitor(options = {}) {
|
|
|
2749
2806
|
);
|
|
2750
2807
|
maps.push(rootMap);
|
|
2751
2808
|
}
|
|
2752
|
-
return
|
|
2809
|
+
return mergeRenderMapsWithoutCollisions(maps);
|
|
2753
2810
|
},
|
|
2754
2811
|
visitAccount(node, { self }) {
|
|
2755
2812
|
const fileName = `${snakeCase(node.name)}.dart`;
|
|
@@ -2784,6 +2841,18 @@ function getRenderMapVisitor(options = {}) {
|
|
|
2784
2841
|
(v) => recordLinkablesOnFirstVisitVisitor(v, linkables)
|
|
2785
2842
|
);
|
|
2786
2843
|
}
|
|
2844
|
+
function mergeRenderMapsWithoutCollisions(maps) {
|
|
2845
|
+
const paths = /* @__PURE__ */ new Set();
|
|
2846
|
+
for (const map of maps) {
|
|
2847
|
+
for (const path of map.keys()) {
|
|
2848
|
+
if (paths.has(path)) {
|
|
2849
|
+
throw new Error(`Duplicate generated path "${path}"`);
|
|
2850
|
+
}
|
|
2851
|
+
paths.add(path);
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
return mergeRenderMaps(maps);
|
|
2855
|
+
}
|
|
2787
2856
|
function getSharedCategoryIndexMaps(renderMap) {
|
|
2788
2857
|
const categories = /* @__PURE__ */ new Map();
|
|
2789
2858
|
const categoryIndexNames = {
|
|
@@ -2800,7 +2869,6 @@ function getSharedCategoryIndexMaps(renderMap) {
|
|
|
2800
2869
|
);
|
|
2801
2870
|
if (!match) continue;
|
|
2802
2871
|
const [, category, fileName] = match;
|
|
2803
|
-
if (fileName === categoryIndexNames[category]) continue;
|
|
2804
2872
|
if (!categories.has(category)) {
|
|
2805
2873
|
categories.set(category, /* @__PURE__ */ new Set());
|
|
2806
2874
|
}
|
|
@@ -2830,9 +2898,6 @@ function renderVisitor(outputDir, options = {}) {
|
|
|
2830
2898
|
} = options;
|
|
2831
2899
|
return rootNodeVisitor((root) => {
|
|
2832
2900
|
const normalizedRoot = normalizeRootNode(root);
|
|
2833
|
-
if (deleteFolderBeforeRendering && existsSync(outputDir)) {
|
|
2834
|
-
deleteDirectory(outputDir);
|
|
2835
|
-
}
|
|
2836
2901
|
const renderMap = visit(
|
|
2837
2902
|
normalizedRoot,
|
|
2838
2903
|
getRenderMapVisitor({ nameApi, dependencyMap })
|
|
@@ -2844,12 +2909,9 @@ function renderVisitor(outputDir, options = {}) {
|
|
|
2844
2909
|
typePathMap[`definedType:${match[1]}`] = renderPath;
|
|
2845
2910
|
}
|
|
2846
2911
|
}
|
|
2912
|
+
const files = [];
|
|
2847
2913
|
for (const [filePath, frag] of renderMap.entries()) {
|
|
2848
2914
|
const fullPath = join(outputDir, filePath);
|
|
2849
|
-
const dir = dirname(fullPath);
|
|
2850
|
-
if (!existsSync(dir)) {
|
|
2851
|
-
mkdirSync(dir, { recursive: true });
|
|
2852
|
-
}
|
|
2853
2915
|
const fileDir = dirname(filePath);
|
|
2854
2916
|
const internalMap = {};
|
|
2855
2917
|
for (const [key, typePath] of Object.entries(typePathMap)) {
|
|
@@ -2864,6 +2926,13 @@ function renderVisitor(outputDir, options = {}) {
|
|
|
2864
2926
|
internalMap[key] = rel;
|
|
2865
2927
|
}
|
|
2866
2928
|
const content = resolveFragmentContent(frag, dependencyMap ?? {}, internalMap);
|
|
2929
|
+
files.push([fullPath, content]);
|
|
2930
|
+
}
|
|
2931
|
+
if (deleteFolderBeforeRendering && existsSync(outputDir)) {
|
|
2932
|
+
deleteDirectory(outputDir);
|
|
2933
|
+
}
|
|
2934
|
+
for (const [fullPath, content] of files) {
|
|
2935
|
+
mkdirSync(dirname(fullPath), { recursive: true });
|
|
2867
2936
|
writeFileSync(fullPath, content, "utf-8");
|
|
2868
2937
|
}
|
|
2869
2938
|
if (formatCode) {
|