@polkadot-api/cli 0.19.5-canary.d6be873 → 0.20.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.
Files changed (61) hide show
  1. package/dist/index.d.ts +2 -1
  2. package/dist/main/package.json.js +4 -0
  3. package/dist/main/package.json.js.map +1 -0
  4. package/dist/main/src/cli.js +49 -0
  5. package/dist/main/src/cli.js.map +1 -0
  6. package/dist/main/src/commands/add.js +91 -0
  7. package/dist/main/src/commands/add.js.map +1 -0
  8. package/dist/main/src/commands/generate.js +459 -0
  9. package/dist/main/src/commands/generate.js.map +1 -0
  10. package/dist/main/src/commands/ink.js +53 -0
  11. package/dist/main/src/commands/ink.js.map +1 -0
  12. package/dist/main/src/commands/remove.js +21 -0
  13. package/dist/main/src/commands/remove.js.map +1 -0
  14. package/dist/main/src/commands/sol.js +51 -0
  15. package/dist/main/src/commands/sol.js.map +1 -0
  16. package/dist/main/src/commands/update.js +56 -0
  17. package/dist/main/src/commands/update.js.map +1 -0
  18. package/dist/main/src/main.js +12 -0
  19. package/dist/main/src/main.js.map +1 -0
  20. package/dist/main/src/metadata.js +175 -0
  21. package/dist/main/src/metadata.js.map +1 -0
  22. package/dist/main/src/packageManager.js +70 -0
  23. package/dist/main/src/packageManager.js.map +1 -0
  24. package/dist/main/src/papiConfig.js +103 -0
  25. package/dist/main/src/papiConfig.js.map +1 -0
  26. package/dist/main/src/version.js +6 -0
  27. package/dist/main/src/version.js.map +1 -0
  28. package/dist/package.json.js +4 -0
  29. package/dist/package.json.js.map +1 -0
  30. package/dist/src/cli.js +49 -0
  31. package/dist/src/cli.js.map +1 -0
  32. package/dist/src/commands/add.js +91 -0
  33. package/dist/src/commands/add.js.map +1 -0
  34. package/dist/src/commands/generate.js +459 -0
  35. package/dist/src/commands/generate.js.map +1 -0
  36. package/dist/src/commands/ink.js +53 -0
  37. package/dist/src/commands/ink.js.map +1 -0
  38. package/dist/src/commands/remove.js +21 -0
  39. package/dist/src/commands/remove.js.map +1 -0
  40. package/dist/src/commands/sol.js +51 -0
  41. package/dist/src/commands/sol.js.map +1 -0
  42. package/dist/src/commands/update.js +56 -0
  43. package/dist/src/commands/update.js.map +1 -0
  44. package/dist/src/index.js +9 -0
  45. package/dist/src/index.js.map +1 -0
  46. package/dist/src/metadata.js +175 -0
  47. package/dist/src/metadata.js.map +1 -0
  48. package/dist/src/packageManager.js +70 -0
  49. package/dist/src/packageManager.js.map +1 -0
  50. package/dist/src/papiConfig.js +103 -0
  51. package/dist/src/papiConfig.js.map +1 -0
  52. package/dist/src/version.js +6 -0
  53. package/dist/src/version.js.map +1 -0
  54. package/package.json +29 -26
  55. package/dist/chunk-YWAERR2C.js +0 -1127
  56. package/dist/chunk-YWAERR2C.js.map +0 -1
  57. package/dist/index.js +0 -21
  58. package/dist/index.js.map +0 -1
  59. package/dist/main.js +0 -15
  60. package/dist/main.js.map +0 -1
  61. /package/dist/{main.d.ts → main/main.d.ts} +0 -0
@@ -0,0 +1,103 @@
1
+ import fsExists from 'fs.promises.exists';
2
+ import { mkdir, readFile, writeFile, rename } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { existsSync } from 'node:fs';
5
+
6
+ const papiFolder = ".papi";
7
+ const papiCfgDefaultFile = "polkadot-api.json";
8
+ const defaultConfig = {
9
+ version: 0,
10
+ descriptorPath: join(papiFolder, "descriptors"),
11
+ entries: {}
12
+ };
13
+ async function readPapiConfig(configFile) {
14
+ if (configFile) return readFromFile(configFile);
15
+ const currentVersionLocation = join(papiFolder, papiCfgDefaultFile);
16
+ const currentVersionLocationExists = await fsExists(currentVersionLocation);
17
+ const readConfig = await (currentVersionLocationExists ? readFromFile(currentVersionLocation) : readFromFile(papiCfgDefaultFile));
18
+ if (readConfig && !currentVersionLocationExists) {
19
+ await writePapiConfig(void 0, readConfig);
20
+ }
21
+ return readConfig;
22
+ }
23
+ async function writePapiConfig(configFile, config) {
24
+ if (configFile) return writeToFile(configFile, config);
25
+ if (!existsSync(papiFolder)) {
26
+ await mkdir(papiFolder);
27
+ }
28
+ return writeToFile(join(papiFolder, papiCfgDefaultFile), config);
29
+ }
30
+ async function readFromFile(file) {
31
+ const fileExists = await fsExists(file);
32
+ if (!fileExists) return null;
33
+ const content = JSON.parse(await readFile(file, "utf8"));
34
+ const config = migrateOldConfig(content);
35
+ const migrations = [migrateOldChains, migrateWhitelistPath];
36
+ let migrationMsgs = [];
37
+ for (const migration of migrations) {
38
+ migrationMsgs = [...migrationMsgs, ...await migration(config)];
39
+ }
40
+ if (migrationMsgs.length) {
41
+ try {
42
+ await writeToFile(file, config);
43
+ migrationMsgs.forEach((msg) => console.warn(msg));
44
+ } catch {
45
+ console.warn("Unable to migrate polkadot-api.json");
46
+ }
47
+ }
48
+ return config;
49
+ }
50
+ const MIGRATION_CHAINS = {
51
+ ksmcc3: "kusama",
52
+ westend2: "westend"
53
+ };
54
+ function migrateOldConfig(content) {
55
+ if (typeof content.version === "number") {
56
+ return content;
57
+ }
58
+ return {
59
+ ...defaultConfig,
60
+ entries: content
61
+ };
62
+ }
63
+ function migrateOldChains(config) {
64
+ const migrationMsgs = [];
65
+ config.entries = Object.fromEntries(
66
+ Object.entries(config.entries).flatMap(([k, entry]) => {
67
+ if ("chain" in entry) {
68
+ if (entry.chain.startsWith("rococo_v2_2")) {
69
+ migrationMsgs.push(
70
+ `Rococo has been sunset. Removing ${entry.chain} descriptors.`
71
+ );
72
+ return [];
73
+ }
74
+ const oldKey = Object.keys(MIGRATION_CHAINS).find(
75
+ (k2) => entry.chain.startsWith(k2)
76
+ );
77
+ if (oldKey) {
78
+ const newChain = entry.chain.replace(oldKey, MIGRATION_CHAINS[oldKey]);
79
+ migrationMsgs.push(`Migrating ${entry.chain} to ${newChain}`);
80
+ entry.chain = newChain;
81
+ }
82
+ }
83
+ return [[k, entry]];
84
+ })
85
+ );
86
+ return migrationMsgs;
87
+ }
88
+ async function migrateWhitelistPath(config) {
89
+ const whitelist = config.options?.whitelist;
90
+ if (!whitelist) return [];
91
+ delete config.options.whitelist;
92
+ if (whitelist === ".papi/whitelist.ts") {
93
+ return ["Removed redundant `whitelist` option from config file"];
94
+ }
95
+ await rename(whitelist, ".papi/whitelist.ts");
96
+ return [`Moved whitelist file from ${whitelist} to .papi/whitelist.ts`];
97
+ }
98
+ async function writeToFile(file, config) {
99
+ return writeFile(file, JSON.stringify(config, null, 2) + "\n");
100
+ }
101
+
102
+ export { defaultConfig, papiFolder, readPapiConfig, writePapiConfig };
103
+ //# sourceMappingURL=papiConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"papiConfig.js","sources":["../../../src/papiConfig.ts"],"sourcesContent":["import fsExists from \"fs.promises.exists\"\nimport { mkdir, readFile, writeFile, rename } from \"node:fs/promises\"\nimport { join } from \"node:path\"\nimport { existsSync } from \"node:fs\"\n\nexport type EntryConfig =\n | {\n metadata: string\n genesis?: string\n codeHash?: string\n }\n | {\n chainSpec: string\n metadata?: string\n genesis?: string\n codeHash?: string\n }\n | {\n wsUrl: string\n at?: string\n metadata?: string\n genesis?: string\n codeHash?: string\n }\n | {\n chain: string\n metadata?: string\n genesis?: string\n codeHash?: string\n }\ntype Entries = Record<string, EntryConfig>\nexport interface PapiConfigOptions {\n noDescriptorsPackage?: boolean\n whitelist?: string // Deprecated\n}\nexport type PapiConfig = {\n version: 0\n descriptorPath: string\n entries: Record<string, EntryConfig>\n ink?: Record<string, string>\n sol?: Record<string, string>\n options?: Partial<PapiConfigOptions>\n}\n\nexport const papiFolder = \".papi\"\nconst papiCfgDefaultFile = \"polkadot-api.json\"\n\nexport const defaultConfig: PapiConfig = {\n version: 0,\n descriptorPath: join(papiFolder, \"descriptors\"),\n entries: {},\n}\n\nexport async function readPapiConfig(\n configFile: string | undefined,\n): Promise<PapiConfig | null> {\n if (configFile) return readFromFile(configFile)\n\n const currentVersionLocation = join(papiFolder, papiCfgDefaultFile)\n const currentVersionLocationExists = await fsExists(currentVersionLocation)\n\n const readConfig = await (currentVersionLocationExists\n ? readFromFile(currentVersionLocation)\n : readFromFile(papiCfgDefaultFile))\n\n // Store into current version location if it wasn't there\n if (readConfig && !currentVersionLocationExists) {\n await writePapiConfig(undefined, readConfig)\n }\n return readConfig\n}\n\n/**\n * Writes config to configFile. If configFile is not specified, it writes to the\n * default path (.papi/polkadot-api.json).\n */\nexport async function writePapiConfig(\n configFile: string | undefined,\n config: PapiConfig,\n) {\n if (configFile) return writeToFile(configFile, config)\n\n if (!existsSync(papiFolder)) {\n await mkdir(papiFolder)\n }\n return writeToFile(join(papiFolder, papiCfgDefaultFile), config)\n}\n\nasync function readFromFile(file: string) {\n const fileExists = await fsExists(file)\n if (!fileExists) return null\n\n const content = JSON.parse(await readFile(file, \"utf8\"))\n const config = migrateOldConfig(content)\n\n const migrations = [migrateOldChains, migrateWhitelistPath]\n let migrationMsgs: string[] = []\n for (const migration of migrations) {\n migrationMsgs = [...migrationMsgs, ...(await migration(config))]\n }\n\n if (migrationMsgs.length) {\n try {\n await writeToFile(file, config as PapiConfig)\n migrationMsgs.forEach((msg) => console.warn(msg))\n } catch {\n console.warn(\"Unable to migrate polkadot-api.json\")\n }\n }\n return config\n}\n\nconst MIGRATION_CHAINS: Record<string, string> = {\n ksmcc3: \"kusama\",\n westend2: \"westend\",\n}\n\nfunction migrateOldConfig(content: Entries | PapiConfig): PapiConfig {\n if (typeof content.version === \"number\") {\n return content as any\n }\n return {\n ...defaultConfig,\n entries: content as Entries,\n }\n}\nfunction migrateOldChains(config: PapiConfig) {\n const migrationMsgs: string[] = []\n config.entries = Object.fromEntries(\n Object.entries(config.entries).flatMap(([k, entry]) => {\n if (\"chain\" in entry) {\n if (entry.chain.startsWith(\"rococo_v2_2\")) {\n migrationMsgs.push(\n `Rococo has been sunset. Removing ${entry.chain} descriptors.`,\n )\n return []\n }\n const oldKey = Object.keys(MIGRATION_CHAINS).find((k) =>\n entry.chain.startsWith(k),\n )\n if (oldKey) {\n const newChain = entry.chain.replace(oldKey, MIGRATION_CHAINS[oldKey])\n migrationMsgs.push(`Migrating ${entry.chain} to ${newChain}`)\n entry.chain = newChain\n }\n }\n return [[k, entry]]\n }),\n )\n return migrationMsgs\n}\nasync function migrateWhitelistPath(config: PapiConfig) {\n const whitelist = config.options?.whitelist\n if (!whitelist) return []\n\n delete config.options!.whitelist\n if (whitelist === \".papi/whitelist.ts\") {\n return [\"Removed redundant `whitelist` option from config file\"]\n }\n await rename(whitelist, \".papi/whitelist.ts\")\n return [`Moved whitelist file from ${whitelist} to .papi/whitelist.ts`]\n}\n\nasync function writeToFile(file: string, config: PapiConfig) {\n return writeFile(file, JSON.stringify(config, null, 2) + \"\\n\")\n}\n"],"names":["k"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { version } from '../package.json.js';
2
+
3
+ const cliVersion = version;
4
+
5
+ export { cliVersion };
6
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sources":["../../../src/version.ts"],"sourcesContent":["import { version } from \"../package.json\"\nexport const cliVersion = version\n"],"names":[],"mappings":";;AACO,MAAM,UAAA,GAAa;;;;"}
@@ -0,0 +1,4 @@
1
+ var version = "0.20.0";
2
+
3
+ export { version };
4
+ //# sourceMappingURL=package.json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
@@ -0,0 +1,49 @@
1
+ import { program, Option } from '@commander-js/extra-typings';
2
+ import * as knownChains from '@polkadot-api/known-chains';
3
+ import { sol } from './commands/sol.js';
4
+
5
+ function getCli({
6
+ add,
7
+ generate,
8
+ remove,
9
+ update,
10
+ ink,
11
+ version
12
+ }) {
13
+ program.name("polkadot-api").description("Polkadot API CLI").version(version);
14
+ const config = new Option("--config <filename>", "Source for the config file");
15
+ const skipCodegen = new Option(
16
+ "--skip-codegen",
17
+ "Skip running codegen after adding"
18
+ );
19
+ const whitelist = new Option(
20
+ "--whitelist <filename>",
21
+ "Removed. The whitelist path is now .papi/whitelist.ts"
22
+ ).hideHelp();
23
+ program.command("generate", {
24
+ isDefault: true
25
+ }).description("Generate descriptor files").addOption(config).addOption(whitelist).action(generate);
26
+ program.command("add").description("Add a new chain spec to the list").argument("<key>", "Key identifier for the chain spec").addOption(config).option("-f, --file <filename>", "Source from metadata encoded file").option("-w, --wsUrl <URL>", "Source from websocket url").option("-c, --chainSpec <filename>", "Source from chain spec file").addOption(
27
+ new Option("-n, --name <name>", "Source from a well-known chain").choices(
28
+ Object.keys(knownChains)
29
+ )
30
+ ).option("--wasm <filename>", "Source from runtime wasm file").option(
31
+ "--at <block hash or number>",
32
+ "Only for -w/--wsUrl. Fetch the metadata for a specific block or hash"
33
+ ).option("--no-persist", "Do not persist the metadata as a file").addOption(skipCodegen).addOption(whitelist).action(add);
34
+ program.command("update").description("Update the metadata files and generate descriptor files").argument(
35
+ "[keys]",
36
+ "Keys of the metadata files to update, separated by commas. Leave empty for all"
37
+ ).addOption(config).addOption(skipCodegen).addOption(whitelist).action(update);
38
+ program.command("remove").description("Remove a chain spec from the list").argument("<key>", "Key identifier for the chain spec").addOption(config).addOption(skipCodegen).addOption(whitelist).action(remove);
39
+ const inkCommand = program.command("ink").description("Add, update or remove ink contracts");
40
+ inkCommand.command("add").description("Add or update an ink contract").argument("<file>", ".contract or .json metadata file for the contract").option("-k, --key <key>", "Key identifier for the contract").addOption(config).addOption(skipCodegen).addOption(whitelist).action(ink.add);
41
+ inkCommand.command("remove").description("Remove an ink contract").argument("<key>", "Key identifier for the contract to remove").addOption(config).addOption(skipCodegen).addOption(whitelist).action(ink.remove);
42
+ const solCommand = program.command("sol").description("Add, update or remove solidity contracts");
43
+ solCommand.command("add").description("Add or update a solidity contract").argument("<file>", ".abi file for the contract").argument("<key>", "Key identifier for the contract").addOption(config).addOption(skipCodegen).addOption(whitelist).action(sol.add);
44
+ solCommand.command("remove").description("Remove a solidity contract").argument("<key>", "Key identifier for the contract to remove").addOption(config).addOption(skipCodegen).addOption(whitelist).action(sol.remove);
45
+ return program;
46
+ }
47
+
48
+ export { getCli };
49
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sources":["../../src/cli.ts"],"sourcesContent":["import { Option, program } from \"@commander-js/extra-typings\"\nimport type { add, generate, ink, remove, update } from \"./commands\"\nimport * as knownChains from \"@polkadot-api/known-chains\"\nimport { sol } from \"./commands/sol\"\n\nexport type Commands = {\n add: typeof add\n generate: typeof generate\n remove: typeof remove\n update: typeof update\n ink: typeof ink\n version: string\n}\n\nexport function getCli({\n add,\n generate,\n remove,\n update,\n ink,\n version,\n}: Commands) {\n program.name(\"polkadot-api\").description(\"Polkadot API CLI\").version(version)\n\n const config = new Option(\"--config <filename>\", \"Source for the config file\")\n const skipCodegen = new Option(\n \"--skip-codegen\",\n \"Skip running codegen after adding\",\n )\n const whitelist = new Option(\n \"--whitelist <filename>\",\n \"Removed. The whitelist path is now .papi/whitelist.ts\",\n ).hideHelp()\n\n program\n .command(\"generate\", {\n isDefault: true,\n })\n .description(\"Generate descriptor files\")\n .addOption(config)\n .addOption(whitelist)\n .action(generate)\n\n program\n .command(\"add\")\n .description(\"Add a new chain spec to the list\")\n .argument(\"<key>\", \"Key identifier for the chain spec\")\n .addOption(config)\n .option(\"-f, --file <filename>\", \"Source from metadata encoded file\")\n .option(\"-w, --wsUrl <URL>\", \"Source from websocket url\")\n .option(\"-c, --chainSpec <filename>\", \"Source from chain spec file\")\n .addOption(\n new Option(\"-n, --name <name>\", \"Source from a well-known chain\").choices(\n Object.keys(knownChains),\n ),\n )\n .option(\"--wasm <filename>\", \"Source from runtime wasm file\")\n .option(\n \"--at <block hash or number>\",\n \"Only for -w/--wsUrl. Fetch the metadata for a specific block or hash\",\n )\n .option(\"--no-persist\", \"Do not persist the metadata as a file\")\n .addOption(skipCodegen)\n .addOption(whitelist)\n .action(add)\n\n program\n .command(\"update\")\n .description(\"Update the metadata files and generate descriptor files\")\n .argument(\n \"[keys]\",\n \"Keys of the metadata files to update, separated by commas. Leave empty for all\",\n )\n .addOption(config)\n .addOption(skipCodegen)\n .addOption(whitelist)\n .action(update)\n\n program\n .command(\"remove\")\n .description(\"Remove a chain spec from the list\")\n .argument(\"<key>\", \"Key identifier for the chain spec\")\n .addOption(config)\n .addOption(skipCodegen)\n .addOption(whitelist)\n .action(remove)\n\n const inkCommand = program\n .command(\"ink\")\n .description(\"Add, update or remove ink contracts\")\n inkCommand\n .command(\"add\")\n .description(\"Add or update an ink contract\")\n .argument(\"<file>\", \".contract or .json metadata file for the contract\")\n .option(\"-k, --key <key>\", \"Key identifier for the contract\")\n .addOption(config)\n .addOption(skipCodegen)\n .addOption(whitelist)\n .action(ink.add)\n inkCommand\n .command(\"remove\")\n .description(\"Remove an ink contract\")\n .argument(\"<key>\", \"Key identifier for the contract to remove\")\n .addOption(config)\n .addOption(skipCodegen)\n .addOption(whitelist)\n .action(ink.remove)\n\n const solCommand = program\n .command(\"sol\")\n .description(\"Add, update or remove solidity contracts\")\n solCommand\n .command(\"add\")\n .description(\"Add or update a solidity contract\")\n .argument(\"<file>\", \".abi file for the contract\")\n .argument(\"<key>\", \"Key identifier for the contract\")\n .addOption(config)\n .addOption(skipCodegen)\n .addOption(whitelist)\n .action(sol.add)\n solCommand\n .command(\"remove\")\n .description(\"Remove a solidity contract\")\n .argument(\"<key>\", \"Key identifier for the contract to remove\")\n .addOption(config)\n .addOption(skipCodegen)\n .addOption(whitelist)\n .action(sol.remove)\n\n return program\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ import { getMetadata, writeMetadataToDisk } from '../metadata.js';
2
+ import { readPapiConfig, defaultConfig, writePapiConfig, papiFolder } from '../papiConfig.js';
3
+ import { compactNumber } from '@polkadot-api/substrate-bindings';
4
+ import { fromHex } from '@polkadot-api/utils';
5
+ import { getMetadataFromRuntime } from '@polkadot-api/wasm-executor/node';
6
+ import * as fs from 'node:fs/promises';
7
+ import ora from 'ora';
8
+ import { generate } from './generate.js';
9
+ import { join } from 'node:path';
10
+ import { existsSync } from 'node:fs';
11
+
12
+ async function add(key, options) {
13
+ const config = await readPapiConfig(options.config) ?? defaultConfig;
14
+ const entries = config.entries;
15
+ if (key in entries) {
16
+ console.warn(`Replacing existing ${key} config`);
17
+ }
18
+ if (options.file) {
19
+ entries[key] = {
20
+ metadata: options.file
21
+ };
22
+ } else if (options.wasm) {
23
+ const spinner = ora(`Loading metadata from runtime`).start();
24
+ const metadataHex = (await fs.readFile(options.wasm)).toString("hex");
25
+ const opaqueMeta = fromHex(getMetadataFromRuntime(`0x${metadataHex}`));
26
+ const metadataLen = compactNumber.dec(opaqueMeta);
27
+ const compactLen = compactNumber.enc(metadataLen).length;
28
+ if (opaqueMeta.length - compactLen !== metadataLen)
29
+ throw new Error("Not able to retrieve runtime metadata");
30
+ spinner.text = "Writing metadata";
31
+ const metadataRaw = opaqueMeta.slice(compactLen);
32
+ const filename = await storeMetadata(metadataRaw, key);
33
+ spinner.succeed(`Metadata saved as ${filename}`);
34
+ entries[key] = {
35
+ metadata: filename
36
+ };
37
+ } else {
38
+ const entry = entryFromOptions(options);
39
+ entries[key] = entry;
40
+ if (!options.noPersist) {
41
+ const spinner = ora(`Loading metadata`).start();
42
+ const { metadataRaw, genesis, codeHash } = await getMetadata(entry);
43
+ spinner.text = "Writing metadata";
44
+ const filename = await storeMetadata(metadataRaw, key);
45
+ spinner.succeed(`Metadata saved as ${filename}`);
46
+ entry.metadata = filename;
47
+ entry.genesis = genesis;
48
+ entry.codeHash = codeHash;
49
+ }
50
+ }
51
+ await writePapiConfig(options.config, config);
52
+ console.log(`Saved new spec "${key}"`);
53
+ if (!options.skipCodegen) {
54
+ generate({
55
+ config: options.config
56
+ });
57
+ }
58
+ }
59
+ async function storeMetadata(metadata, key) {
60
+ const defaultFolder = join(papiFolder, "metadata");
61
+ if (!existsSync(defaultFolder)) {
62
+ await fs.mkdir(defaultFolder, { recursive: true });
63
+ }
64
+ const filename = join(defaultFolder, `${key}.scale`);
65
+ await writeMetadataToDisk(metadata, filename);
66
+ return filename;
67
+ }
68
+ const entryFromOptions = (options) => {
69
+ if (options.wsUrl) {
70
+ return {
71
+ wsUrl: options.wsUrl,
72
+ at: options.at
73
+ };
74
+ }
75
+ if (options.chainSpec) {
76
+ return {
77
+ chainSpec: options.chainSpec
78
+ };
79
+ }
80
+ if (options.name) {
81
+ return {
82
+ chain: options.name
83
+ };
84
+ }
85
+ throw new Error(
86
+ "add command needs one source, specified by options -f -w -c or -n"
87
+ );
88
+ };
89
+
90
+ export { add };
91
+ //# sourceMappingURL=add.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.js","sources":["../../../src/commands/add.ts"],"sourcesContent":["import { getMetadata, writeMetadataToDisk } from \"@/metadata\"\nimport {\n defaultConfig,\n EntryConfig,\n papiFolder,\n readPapiConfig,\n writePapiConfig,\n} from \"@/papiConfig\"\nimport { compactNumber } from \"@polkadot-api/substrate-bindings\"\nimport { fromHex } from \"@polkadot-api/utils\"\nimport { getMetadataFromRuntime } from \"@polkadot-api/wasm-executor/node\"\nimport * as fs from \"node:fs/promises\"\nimport ora from \"ora\"\nimport { CommonOptions } from \"./commonOptions\"\nimport { generate } from \"./generate\"\nimport { join } from \"node:path\"\nimport { existsSync } from \"node:fs\"\n\nexport interface AddOptions extends CommonOptions {\n file?: string\n wsUrl?: string\n chainSpec?: string\n // well-known chains\n name?: string\n wasm?: string\n noPersist?: boolean\n // only for wsUrl\n at?: string\n}\n\nexport async function add(key: string, options: AddOptions) {\n const config = (await readPapiConfig(options.config)) ?? defaultConfig\n const entries = config.entries\n\n if (key in entries) {\n console.warn(`Replacing existing ${key} config`)\n }\n\n if (options.file) {\n entries[key] = {\n metadata: options.file,\n }\n } else if (options.wasm) {\n const spinner = ora(`Loading metadata from runtime`).start()\n const metadataHex = (await fs.readFile(options.wasm)).toString(\"hex\")\n const opaqueMeta = fromHex(getMetadataFromRuntime(`0x${metadataHex}`))\n\n // metadata comes with compact length prepended\n const metadataLen = compactNumber.dec(opaqueMeta)\n const compactLen = compactNumber.enc(metadataLen).length\n // verify we got all data\n if (opaqueMeta.length - compactLen !== metadataLen)\n throw new Error(\"Not able to retrieve runtime metadata\")\n\n spinner.text = \"Writing metadata\"\n const metadataRaw = opaqueMeta.slice(compactLen)\n const filename = await storeMetadata(metadataRaw, key)\n spinner.succeed(`Metadata saved as ${filename}`)\n\n entries[key] = {\n metadata: filename,\n }\n } else {\n const entry = entryFromOptions(options)\n entries[key] = entry\n\n if (!options.noPersist) {\n const spinner = ora(`Loading metadata`).start()\n const { metadataRaw, genesis, codeHash } = (await getMetadata(entry))!\n\n spinner.text = \"Writing metadata\"\n const filename = await storeMetadata(metadataRaw, key)\n\n spinner.succeed(`Metadata saved as ${filename}`)\n entry.metadata = filename\n entry.genesis = genesis\n entry.codeHash = codeHash\n }\n }\n\n await writePapiConfig(options.config, config)\n console.log(`Saved new spec \"${key}\"`)\n\n if (!options.skipCodegen) {\n generate({\n config: options.config,\n })\n }\n}\n\nasync function storeMetadata(metadata: Uint8Array, key: string) {\n const defaultFolder = join(papiFolder, \"metadata\")\n if (!existsSync(defaultFolder)) {\n await fs.mkdir(defaultFolder, { recursive: true })\n }\n const filename = join(defaultFolder, `${key}.scale`)\n await writeMetadataToDisk(metadata, filename)\n return filename\n}\n\nconst entryFromOptions = (options: AddOptions): EntryConfig => {\n if (options.wsUrl) {\n return {\n wsUrl: options.wsUrl,\n at: options.at,\n }\n }\n if (options.chainSpec) {\n return {\n chainSpec: options.chainSpec,\n }\n }\n if (options.name) {\n return {\n chain: options.name,\n }\n }\n\n throw new Error(\n \"add command needs one source, specified by options -f -w -c or -n\",\n )\n}\n"],"names":[],"mappings":"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"}