@ton/blueprint 0.40.0 โ†’ 0.41.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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.41.0] - 2025-09-23
9
+
10
+ ### Added
11
+
12
+ - Added `blueprint test --coverage` command
13
+
14
+ ### Changed
15
+
16
+ - When creating a contract, if the input contract name is invalid, blueprint does not exit, but asks for the name again
17
+
8
18
  ## [0.40.0] - 2025-08-18
9
19
 
10
20
  ### Added
package/README.md CHANGED
@@ -41,7 +41,7 @@ npm create ton@latest
41
41
 
42
42
  ## Overview
43
43
 
44
- Blueprint is an all-in-one development environment designed to enhance the process of creating, testing, and deploying smart contracts on TON blockchain using [FunC](https://docs.ton.org/develop/func/overview), [Tolk](https://docs.ton.org/develop/tolk/overview), and [Tact](https://docs.tact-lang.org/) languages.
44
+ Blueprint is an all-in-one development environment designed to enhance the process of creating, testing, and deploying smart contracts on TON blockchain using [Tolk](https://docs.ton.org/develop/tolk/overview), [FunC](https://docs.ton.org/develop/func/overview), and [Tact](https://docs.tact-lang.org/) languages.
45
45
 
46
46
  ### Core features
47
47
 
@@ -52,8 +52,8 @@ Blueprint is an all-in-one development environment designed to enhance the proce
52
52
 
53
53
  ### Tech stack
54
54
 
55
- 1. Compiling FunC with https://github.com/ton-community/func-js
56
- 2. Compiling Tolk with https://github.com/ton-blockchain/tolk-js
55
+ 1. Compiling Tolk with https://github.com/ton-blockchain/tolk-js
56
+ 2. Compiling FunC with https://github.com/ton-community/func-js
57
57
  3. Compiling Tact with https://github.com/tact-lang/tact
58
58
  * Uses [`tact.config.json`](https://docs.tact-lang.org/book/config/) as the build configuration file
59
59
  4. Testing smart contracts with https://github.com/ton-org/sandbox
@@ -63,9 +63,9 @@ Blueprint is an all-in-one development environment designed to enhance the proce
63
63
 
64
64
  * [Node.js](https://nodejs.org) with a recent version like v18. Version can be verified with `node -v`
65
65
  * IDE with TON support:
66
- * [Visual Studio Code](https://code.visualstudio.com/) with the [FunC plugin](https://marketplace.visualstudio.com/items?itemName=tonwhales.func-vscode), [Tolk plugin](https://marketplace.visualstudio.com/items?itemName=ton-core.tolk-vscode) or [Tact plugin](https://marketplace.visualstudio.com/items?itemName=tonstudio.vscode-tact)
66
+ * [Visual Studio Code](https://code.visualstudio.com/) with the [TON plugin](https://marketplace.visualstudio.com/items?itemName=ton-core.vscode-ton) or [Tact plugin](https://marketplace.visualstudio.com/items?itemName=tonstudio.vscode-tact)
67
67
  * [IntelliJ IDEA](https://www.jetbrains.com/idea/)
68
- * [TON Development plugin](https://plugins.jetbrains.com/plugin/23382-ton) for FunC, Tolk and Fift
68
+ * [TON Development plugin](https://plugins.jetbrains.com/plugin/23382-ton) for Tolk, FunC and Fift
69
69
  * [Tact plugin by TON Studio](https://plugins.jetbrains.com/plugin/27290-tact) for Tact
70
70
 
71
71
  ## Features overview
@@ -139,9 +139,13 @@ Start by adding the following environment variables to your `.env` file:
139
139
 
140
140
  Once your environment is set up, you can use the mnemonic wallet for deployment with the appropriate configuration.
141
141
 
142
+ ### Updating Tolk version
143
+
144
+ Tolk version can be updated to the latest using `npm update/yarn upgrade @ton/tolk-js` command
145
+
142
146
  ### Updating FunC version
143
147
 
144
- FunC version can be updated using `npx/yarn blueprint set func` command
148
+ FunC version can be updated to a specific version using `npx/yarn blueprint set func` command, or to the latest using `npm update/yarn upgrade @ton-community/func-js` command
145
149
 
146
150
  ### Updating Tact version
147
151
 
@@ -158,8 +162,8 @@ Before developing, make sure that your current working directory is located in t
158
162
  ### Creating contracts
159
163
 
160
164
  1. Run interactive:    `npx blueprint create`   or   `yarn blueprint create`
161
- 2. Non-interactive: &nbsp; `npx/yarn blueprint create <CONTRACT> --type <TYPE>` (type can be `func-empty`, `tolk-empty`, `tact-empty`, `func-counter`, `tolk-counter`, `tact-counter`)
162
- * Example: `yarn blueprint create MyNewContract --type func-empty`
165
+ 2. Non-interactive: &nbsp; `npx/yarn blueprint create <CONTRACT> --type <TYPE>` (type can be `tolk-empty`, `func-empty`, `tact-empty`, `tolk-counter`, `func-counter`, `tact-counter`)
166
+ * Example: `yarn blueprint create MyNewContract --type tolk-empty`
163
167
 
164
168
  ### Renaming contracts
165
169
 
@@ -169,15 +173,15 @@ Before developing, make sure that your current working directory is located in t
169
173
 
170
174
  ### Writing contract code
171
175
 
176
+ #### Tolk
177
+ 1. Implement the contract in `contracts/<CONTRACT>.tolk`; if you wish, split into multiple files
178
+ 2. Implement wrapper TypeScript class in `wrappers/<CONTRACT>.ts` to encode messages and decode getters
179
+
172
180
  #### FunC
173
181
  1. Implement the standalone FunC root contract in `contracts/<CONTRACT>.fc`
174
182
  2. Implement shared FunC imports (if breaking code to multiple files) in `contracts/imports/*.fc`
175
183
  3. Implement wrapper TypeScript class in `wrappers/<CONTRACT>.ts` to encode messages and decode getters
176
184
 
177
- #### Tolk
178
- 1. Implement the contract in `contracts/<CONTRACT>.tolk`; if you wish, split into multiple files
179
- 2. Implement wrapper TypeScript class in `wrappers/<CONTRACT>.ts` to encode messages and decode getters
180
-
181
185
  #### Tact
182
186
  1. Implement the contract in `contracts/<CONTRACT>.tact`
183
187
  2. Wrappers will be automatically generated in `build/<CONTRACT>/tact_<CONTRACT>.ts`
@@ -187,6 +191,10 @@ Before developing, make sure that your current working directory is located in t
187
191
  1. Implement TypeScript tests in `tests/<CONTRACT>.spec.ts`
188
192
  2. Rely on the wrapper TypeScript class from `wrappers/<CONTRACT>.ts` to interact with the contract
189
193
 
194
+ #### Collecting coverage
195
+
196
+ To collect coverage run `blueprint test --coverage`. Coverage will appear in coverage directory.
197
+
190
198
  > Learn more about writing tests from the Sandbox's documentation - [here](https://github.com/ton-org/sandbox#writing-tests).
191
199
 
192
200
  ### Publishing Wrapper Code
package/dist/cli/cli.js CHANGED
@@ -55,6 +55,7 @@ const InquirerUIProvider_1 = require("../ui/InquirerUIProvider");
55
55
  const Runner_1 = require("./Runner");
56
56
  const utils_1 = require("../config/utils");
57
57
  const rename_1 = require("./rename");
58
+ const constants_1 = require("./constants");
58
59
  const runners = {
59
60
  create: create_1.create,
60
61
  run: run_1.run,
@@ -121,34 +122,26 @@ main()
121
122
  .then(() => process.exit(0));
122
123
  function showHelp() {
123
124
  console.log(chalk_1.default.blueBright(`
124
- ____ _ _ _ _____ ____ ____ ___ _ _ _____
125
+ ____ _ _ _ _____ ____ ____ ___ _ _ _____
125
126
  | __ )| | | | | | ____| _ \\| _ \\|_ _| \\ | |_ _|
126
- | _ \\| | | | | | _| | |_) | |_) || || \\| | | |
127
- | |_) | |__| |_| | |___| __/| _ < | || |\\ | | |
127
+ | _ \\| | | | | | _| | |_) | |_) || || \\| | | |
128
+ | |_) | |__| |_| | |___| __/| _ < | || |\\ | | |
128
129
  |____/|_____\\___/|_____|_| |_| \\_\\___|_| \\_| |_| `));
129
130
  console.log(chalk_1.default.blue(` TON development for professionals`));
130
131
  console.log(``);
131
132
  console.log(` Usage: blueprint [OPTIONS] COMMAND [ARGS]`);
132
133
  console.log(``);
133
- console.log(chalk_1.default.cyanBright(` blueprint create`) +
134
- `\t` +
135
- chalk_1.default.whiteBright(`create a new contract with .fc source, .ts wrapper, .spec.ts test`));
136
- console.log(`\t\t\t` + chalk_1.default.gray(`blueprint create ContractName`));
137
- console.log(chalk_1.default.cyanBright(` blueprint build`) +
138
- `\t` +
139
- chalk_1.default.whiteBright(`builds a contract that has a .compile.ts file`));
140
- console.log(`\t\t\t` + chalk_1.default.gray(`blueprint build ContractName`));
141
- console.log(chalk_1.default.cyanBright(` blueprint test`) +
142
- `\t` +
143
- chalk_1.default.whiteBright(`run the full project test suite with all .spec.ts files`));
144
- console.log(`\t\t\t` + chalk_1.default.gray(`blueprint test`));
145
- console.log(chalk_1.default.cyanBright(` blueprint run `) +
146
- `\t` +
147
- chalk_1.default.whiteBright(`runs a script from 'scripts' directory (eg. a deploy script)`));
148
- console.log(`\t\t\t` + chalk_1.default.gray(`blueprint run deployContractName`));
149
- console.log(chalk_1.default.cyanBright(` blueprint help`) +
150
- `\t` +
151
- chalk_1.default.whiteBright(`shows more detailed help, also see https://github.com/ton-org/blueprint`));
152
- console.log(`\t\t\t` + chalk_1.default.gray(`blueprint help`));
134
+ const mainPageCommands = new Set(['create', 'build', 'test', 'run', 'help']);
135
+ for (const cmd of constants_1.availableCommands) {
136
+ if (!mainPageCommands.has(cmd.name)) {
137
+ continue;
138
+ }
139
+ const commandName = ` blueprint ${cmd.name}`;
140
+ const description = cmd.description;
141
+ const cmdPadding = ' '.repeat(Math.max(0, 24 - commandName.length));
142
+ const examplePadding = ' '.repeat(cmdPadding.length + commandName.length);
143
+ console.log(chalk_1.default.cyanBright(commandName) + cmdPadding + chalk_1.default.whiteBright(description));
144
+ console.log(examplePadding + chalk_1.default.gray(cmd.example));
145
+ }
153
146
  console.log(``);
154
147
  }
@@ -6,6 +6,13 @@ export declare const helpArgs: {
6
6
  '--help': BooleanConstructor;
7
7
  '-h': string;
8
8
  };
9
+ export type KnownCommandName = 'create' | 'run' | 'build' | 'set' | 'help' | 'test' | 'verify' | 'convert' | 'rename' | 'pack' | 'snapshot';
10
+ export interface CommandInfo {
11
+ readonly name: KnownCommandName;
12
+ readonly description: string;
13
+ readonly example: string;
14
+ }
15
+ export declare const availableCommands: CommandInfo[];
9
16
  export declare const helpMessages: {
10
17
  help: string;
11
18
  create: string;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.helpMessages = exports.helpArgs = exports.templateTypes = void 0;
6
+ exports.helpMessages = exports.availableCommands = exports.helpArgs = exports.templateTypes = void 0;
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
8
  exports.templateTypes = [
9
9
  {
@@ -32,18 +32,46 @@ exports.templateTypes = [
32
32
  },
33
33
  ];
34
34
  exports.helpArgs = { '--help': Boolean, '-h': '--help' };
35
- const availableCommands = [
36
- 'create',
37
- 'run',
38
- 'build',
39
- 'set',
40
- 'help',
41
- 'test',
42
- 'verify',
43
- 'convert',
44
- 'rename',
45
- 'pack',
46
- 'snapshot',
35
+ exports.availableCommands = [
36
+ {
37
+ name: 'create',
38
+ description: 'create a new contract with .fc source, .ts wrapper, .spec.ts test',
39
+ example: 'blueprint create ContractName',
40
+ },
41
+ {
42
+ name: 'build',
43
+ description: 'builds a contract that has a .compile.ts file',
44
+ example: 'blueprint build ContractName',
45
+ },
46
+ { name: 'test', description: 'run the full project test suite with all .spec.ts files', example: 'blueprint test' },
47
+ {
48
+ name: 'run',
49
+ description: "runs a script from 'scripts' directory (eg. a deploy script)",
50
+ example: 'blueprint run deployContractName',
51
+ },
52
+ {
53
+ name: 'help',
54
+ description: 'shows more detailed help, also see https://github.com/ton-org/blueprint',
55
+ example: 'blueprint help',
56
+ },
57
+ { name: 'set', description: 'sets configuration values', example: 'blueprint set' },
58
+ { name: 'verify', description: 'verifies a deployed contract on verifier.ton.org', example: 'blueprint verify' },
59
+ {
60
+ name: 'convert',
61
+ description: 'converts legacy bash build scripts to Blueprint wrappers',
62
+ example: 'blueprint convert',
63
+ },
64
+ {
65
+ name: 'rename',
66
+ description: 'renames contract by matching in wrappers, scripts and tests',
67
+ example: 'blueprint rename',
68
+ },
69
+ { name: 'pack', description: 'builds and prepares a publish-ready package of wrappers', example: 'blueprint pack' },
70
+ {
71
+ name: 'snapshot',
72
+ description: 'creates snapshots with gas usage and cells sizes',
73
+ example: 'blueprint snapshot',
74
+ },
47
75
  ];
48
76
  exports.helpMessages = {
49
77
  help: `${chalk_1.default.bold('Usage:')} blueprint ${chalk_1.default.cyan('help')} [${chalk_1.default.yellow('command')}]
@@ -54,7 +82,9 @@ Blueprint is generally invoked as follows:
54
82
  ${chalk_1.default.cyan('blueprint')} ${chalk_1.default.yellow('[command]')} ${chalk_1.default.gray('[command-args]')} ${chalk_1.default.gray('[flags]')}
55
83
 
56
84
  ${chalk_1.default.bold('List of available commands:')}
57
- ${availableCommands.map((c) => `- ${chalk_1.default.green(c)}`).join('\n')}`,
85
+ ${exports.availableCommands.map((c) => ` ${chalk_1.default.cyanBright(c.name)}${' '.repeat(Math.max(0, 20 - c.name.length))}${c.description}`).join('\n')}
86
+
87
+ To get more information about a command, run ${chalk_1.default.cyan('blueprint help <command>')}`,
58
88
  create: `${chalk_1.default.bold('Usage:')} blueprint ${chalk_1.default.cyan('create')} ${chalk_1.default.yellow('[contract name]')} ${chalk_1.default.gray('[flags]')}
59
89
 
60
90
  Creates a new contract together with supporting files according to a template.
@@ -1,2 +1,4 @@
1
1
  import { Runner } from './Runner';
2
+ import { UIProvider } from '../ui/UIProvider';
2
3
  export declare const create: Runner;
4
+ export declare function requestContractName(message: string, ui: UIProvider): Promise<string>;
@@ -4,10 +4,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.create = void 0;
7
+ exports.requestContractName = requestContractName;
7
8
  const path_1 = __importDefault(require("path"));
8
9
  const posix_1 = __importDefault(require("path/posix"));
9
10
  const promises_1 = require("fs/promises");
10
11
  const arg_1 = __importDefault(require("arg"));
12
+ const chalk_1 = __importDefault(require("chalk"));
11
13
  const utils_1 = require("../config/utils");
12
14
  const tact_config_1 = require("../config/tact.config");
13
15
  const Runner_1 = require("./Runner");
@@ -80,8 +82,14 @@ const create = async (_args, ui) => {
80
82
  ui.write(constants_1.helpMessages['create']);
81
83
  return;
82
84
  }
83
- const name = (0, Runner_1.extractFirstArg)(localArgs) ?? (await ui.input('Contract name (PascalCase)'));
84
- (0, utils_2.assertValidContractName)(name);
85
+ const argName = (0, Runner_1.extractFirstArg)(localArgs);
86
+ if (argName !== undefined) {
87
+ const error = (0, utils_2.validateContractName)(argName);
88
+ if (error) {
89
+ throw new Error(error);
90
+ }
91
+ }
92
+ const name = argName ?? (await requestContractName('Contract name (PascalCase)', ui));
85
93
  const which = (await (0, utils_2.selectOption)(constants_1.templateTypes, {
86
94
  ui,
87
95
  msg: 'What type of contract do you want to create?',
@@ -109,3 +117,15 @@ const create = async (_args, ui) => {
109
117
  }
110
118
  };
111
119
  exports.create = create;
120
+ async function requestContractName(message, ui) {
121
+ while (true) {
122
+ const name = await ui.input(message);
123
+ const error = (0, utils_2.validateContractName)(name);
124
+ if (error !== undefined) {
125
+ ui.write(chalk_1.default.redBright(`Error: `) + error);
126
+ // ask user again
127
+ continue;
128
+ }
129
+ return name;
130
+ }
131
+ }
@@ -13,6 +13,7 @@ const Runner_1 = require("./Runner");
13
13
  const utils_1 = require("../utils");
14
14
  const constants_1 = require("./constants");
15
15
  const build_1 = require("./build");
16
+ const create_1 = require("./create");
16
17
  function renameExactIfRequired(str, replaces) {
17
18
  let renamedString = str;
18
19
  let isRenamed = false;
@@ -71,8 +72,14 @@ const rename = async (_args, ui, _context) => {
71
72
  return;
72
73
  }
73
74
  const oldName = await (0, build_1.selectContract)(ui, (0, Runner_1.extractFirstArg)(localArgs));
74
- const newName = (0, Runner_1.extractSecondArg)(localArgs) ?? (await ui.input('New contract name (PascalCase)'));
75
- (0, utils_1.assertValidContractName)(newName);
75
+ const argName = (0, Runner_1.extractSecondArg)(localArgs);
76
+ if (argName !== undefined) {
77
+ const error = (0, utils_1.validateContractName)(argName);
78
+ if (error) {
79
+ throw new Error(error);
80
+ }
81
+ }
82
+ const newName = argName ?? (await (0, create_1.requestContractName)('New contract name (PascalCase)', ui));
76
83
  const contracts = await (0, utils_1.findContracts)();
77
84
  if (contracts.includes(newName)) {
78
85
  ui.write(`Contract with name ${newName} already exists.`);
@@ -2,5 +2,7 @@ import { Runner } from './Runner';
2
2
  export declare const argSpec: {
3
3
  '--gas-report': BooleanConstructor;
4
4
  '-g': string;
5
+ '--coverage': BooleanConstructor;
5
6
  };
7
+ export declare function coverage(): Promise<void>;
6
8
  export declare const test: Runner;
package/dist/cli/test.js CHANGED
@@ -4,13 +4,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.test = exports.argSpec = void 0;
7
+ exports.coverage = coverage;
7
8
  const child_process_1 = require("child_process");
8
9
  const arg_1 = __importDefault(require("arg"));
9
10
  const constants_1 = require("./constants");
10
11
  exports.argSpec = {
11
12
  '--gas-report': Boolean,
12
13
  '-g': '--gas-report',
14
+ '--coverage': Boolean,
13
15
  };
16
+ async function coverage() {
17
+ (0, child_process_1.execSync)(`npm test -- --reporters @ton/blueprint/dist/jest/CoverageReporter --setupFilesAfterEnv @ton/blueprint/dist/jest/coverageSetup`, {
18
+ stdio: 'inherit',
19
+ });
20
+ }
14
21
  const test = async (args, ui) => {
15
22
  const localArgs = (0, arg_1.default)({
16
23
  ...constants_1.helpArgs,
@@ -20,6 +27,10 @@ const test = async (args, ui) => {
20
27
  ui.write(constants_1.helpMessages['test']);
21
28
  return;
22
29
  }
30
+ if (localArgs['--coverage']) {
31
+ await coverage();
32
+ return;
33
+ }
23
34
  let testArgs = args._.slice(1); // first argument is `test`, need to get rid of it
24
35
  if (localArgs['--gas-report']) {
25
36
  testArgs = testArgs.slice(1);
@@ -151,7 +151,7 @@ const verify = async (_args, ui, context) => {
151
151
  if (network === 'custom') {
152
152
  throw new Error('Cannot use custom network');
153
153
  }
154
- const result = await (0, compile_1.doCompile)(selectedContract);
154
+ const result = await (0, compile_1.doCompile)(selectedContract, { buildLibrary: false });
155
155
  const resHash = result.code.hash();
156
156
  ui.write(`Compiled code hash hex: ${resHash.toString('hex')}`);
157
157
  ui.write('We can look up the address with such code hash in the blockchain automatically');
@@ -0,0 +1,11 @@
1
+ import type { Reporter } from '@jest/reporters';
2
+ declare class CoverageReporter implements Reporter {
3
+ private contracts;
4
+ private get coverageDir();
5
+ private get blueprintCoverageDir();
6
+ onRunStart(): Promise<void>;
7
+ onRunComplete(): Promise<void>;
8
+ private printSummary;
9
+ private collectLogs;
10
+ }
11
+ export default CoverageReporter;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_path_1 = __importDefault(require("node:path"));
7
+ const promises_1 = __importDefault(require("node:fs/promises"));
8
+ const fs_1 = require("fs");
9
+ const core_1 = require("@ton/core");
10
+ // @ts-expect-error blueprint imported inside package
11
+ const blueprint_1 = require("@ton/blueprint");
12
+ const sandbox_1 = require("@ton/sandbox");
13
+ const chalk_1 = __importDefault(require("chalk"));
14
+ class CoverageReporter {
15
+ constructor() {
16
+ this.contracts = [];
17
+ }
18
+ get coverageDir() {
19
+ return node_path_1.default.join(process.cwd(), 'coverage');
20
+ }
21
+ get blueprintCoverageDir() {
22
+ return node_path_1.default.join(this.coverageDir, 'blueprint');
23
+ }
24
+ async onRunStart() {
25
+ console.log(`\n๐Ÿ› ๏ธ Building contracts...`);
26
+ await (0, blueprint_1.buildAll)();
27
+ const buildDir = node_path_1.default.join(process.cwd(), 'build');
28
+ const buildFiles = (await promises_1.default.readdir(buildDir)).filter((f) => f.endsWith('.json'));
29
+ const contents = await Promise.all(buildFiles.map((f) => promises_1.default.readFile(node_path_1.default.join(buildDir, f), 'utf-8')));
30
+ this.contracts = contents.map((json, i) => ({
31
+ name: node_path_1.default.basename(buildFiles[i], '.compiled.json'),
32
+ ...JSON.parse(json),
33
+ }));
34
+ console.log(`โœ… Built ${this.contracts.length} contracts: ${this.contracts.map((c) => c.name).join(', ')}`);
35
+ if ((0, fs_1.existsSync)(this.blueprintCoverageDir)) {
36
+ console.log(`๐Ÿงน Cleaning old coverage at ${this.blueprintCoverageDir}`);
37
+ await promises_1.default.rm(this.blueprintCoverageDir, { recursive: true, force: true });
38
+ }
39
+ }
40
+ async onRunComplete() {
41
+ if (!(0, fs_1.existsSync)(this.blueprintCoverageDir)) {
42
+ console.log(`โš ๏ธ No blueprint coverage data found, skipping coverage reports.`);
43
+ return;
44
+ }
45
+ console.log(`\n๐Ÿ“„ Collecting coverage logs...`);
46
+ const logs = await this.collectLogs();
47
+ console.log(`\n๐Ÿ“ Generating coverage reports...`);
48
+ for (const { name, hex } of this.contracts) {
49
+ const codeCell = core_1.Cell.fromHex(hex);
50
+ const merged = (0, sandbox_1.mergeCoverages)(...logs.map((l) => (0, sandbox_1.collectAsmCoverage)(codeCell, l)));
51
+ const coverage = new sandbox_1.Coverage(merged);
52
+ const report = coverage.report('html');
53
+ const reportPath = node_path_1.default.join(this.coverageDir, `${name}-report.html`);
54
+ await promises_1.default.writeFile(reportPath, report);
55
+ const summary = coverage.summary();
56
+ this.printSummary(name, summary, reportPath);
57
+ }
58
+ console.log(`\nโœ… Coverage reports generated in ${this.coverageDir}\n`);
59
+ }
60
+ printSummary(name, summary, reportPath) {
61
+ const pct = summary.coveragePercentage.toFixed(2) + '%';
62
+ const line = chalk_1.default.bold(name.padEnd(20)) +
63
+ chalk_1.default.white(`${summary.coveredLines}/${summary.totalLines} lines`.padEnd(20)) +
64
+ (summary.coveragePercentage >= 80 ? chalk_1.default.green(pct.padEnd(10)) : chalk_1.default.red(pct.padEnd(10))) +
65
+ chalk_1.default.gray(reportPath);
66
+ console.log(' โ€ข ' + line);
67
+ }
68
+ async collectLogs() {
69
+ const files = (await promises_1.default.readdir(this.blueprintCoverageDir, { recursive: true })).filter((f) => f.endsWith('.json'));
70
+ const contents = await Promise.all(files.map((f) => promises_1.default.readFile(node_path_1.default.join(this.blueprintCoverageDir, f), 'utf-8')));
71
+ return contents.flatMap((content) => {
72
+ const { txLogs, getLogs } = JSON.parse(content);
73
+ return [...txLogs, ...getLogs];
74
+ });
75
+ }
76
+ }
77
+ exports.default = CoverageReporter;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_fs_1 = __importDefault(require("node:fs"));
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const sandbox_1 = require("@ton/sandbox");
9
+ const txLogs = [];
10
+ const getLogs = [];
11
+ beforeAll(() => {
12
+ const originalCreate = sandbox_1.Blockchain.create.bind(sandbox_1.Blockchain);
13
+ sandbox_1.Blockchain.create = async (...args) => {
14
+ const blockchain = await originalCreate(...args);
15
+ blockchain.enableCoverage();
16
+ const originalVerbosity = { ...blockchain.verbosity };
17
+ originalVerbosity.print = false;
18
+ originalVerbosity.vmLogs = 'vm_logs_verbose';
19
+ Object.defineProperty(blockchain, 'verbosity', {
20
+ get() {
21
+ return originalVerbosity;
22
+ },
23
+ set(_) {
24
+ // prevent changes
25
+ },
26
+ });
27
+ blockchain.verbosity = originalVerbosity;
28
+ // @ts-expect-error error
29
+ const origRegisterTx = blockchain.registerTxsForCoverage.bind(blockchain);
30
+ // @ts-expect-error error
31
+ const origRegisterGet = blockchain.registerGetMethodForCoverage.bind(blockchain);
32
+ // @ts-expect-error error
33
+ blockchain.registerTxsForCoverage = (txs) => {
34
+ txLogs.push(...txs.map((tx) => tx.vmLogs));
35
+ return origRegisterTx(txs);
36
+ };
37
+ // @ts-expect-error error
38
+ blockchain.registerGetMethodForCoverage = (gets) => {
39
+ getLogs.push(gets.vmLogs);
40
+ return origRegisterGet(gets);
41
+ };
42
+ return blockchain;
43
+ };
44
+ });
45
+ afterAll(() => {
46
+ const coveragePath = node_path_1.default.join(process.cwd(), 'coverage');
47
+ const blueprintCoveragePath = node_path_1.default.join(coveragePath, 'blueprint');
48
+ const testPath = expect.getState().testPath;
49
+ const testRelative = node_path_1.default.relative(process.cwd(), testPath);
50
+ const logsFilePath = node_path_1.default.join(blueprintCoveragePath, testRelative + '.json');
51
+ node_fs_1.default.mkdirSync(node_path_1.default.dirname(logsFilePath), { recursive: true });
52
+ node_fs_1.default.writeFileSync(logsFilePath, JSON.stringify({ txLogs, getLogs }, null, 2));
53
+ });
@@ -0,0 +1 @@
1
+ export type Explorer = 'tonscan' | 'tonviewer' | 'toncx' | 'dton';
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -3,6 +3,7 @@ import { Address, Cell, Contract, ContractProvider, ContractState, OpenedContrac
3
3
  import { ContractAdapter } from '@ton-api/ton-adapter';
4
4
  import { LiteClient } from 'ton-lite-client';
5
5
  import { UIProvider } from '../ui/UIProvider';
6
+ import { Explorer } from './Explorer';
6
7
  export type BlueprintTonClient = TonClient4 | TonClient | ContractAdapter | LiteClient;
7
8
  type BlockchainConfig = ReturnType<typeof parseFullConfig>;
8
9
  export interface SenderWithSendResult extends Sender {
@@ -17,6 +18,11 @@ export interface NetworkProvider {
17
18
  * @returns {'mainnet' | 'testnet' | 'custom'} The type of network.
18
19
  */
19
20
  network(): 'mainnet' | 'testnet' | 'custom';
21
+ /**
22
+ * Returns the current explorer type.
23
+ * @returns {Explorer} The type of explorer.
24
+ */
25
+ explorer(): Explorer;
20
26
  /**
21
27
  * Returns the sender used for transactions.
22
28
  * @example