@ton/sandbox 0.30.0 → 0.32.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 (38) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/README.md +104 -0
  3. package/dist/blockchain/Blockchain.d.ts +16 -2
  4. package/dist/blockchain/Blockchain.js +31 -13
  5. package/dist/blockchain/SmartContract.js +2 -0
  6. package/dist/executor/Executor.d.ts +14 -0
  7. package/dist/executor/Executor.js +34 -1
  8. package/dist/executor/emulator-emscripten.js +1 -1
  9. package/dist/executor/emulator-emscripten.wasm.js +1 -1
  10. package/dist/index.d.ts +2 -1
  11. package/dist/index.js +15 -0
  12. package/dist/jest/BenchmarkCommand.d.ts +10 -0
  13. package/dist/jest/BenchmarkCommand.js +14 -0
  14. package/dist/jest/BenchmarkEnvironment.d.ts +19 -0
  15. package/dist/jest/BenchmarkEnvironment.js +46 -0
  16. package/dist/jest/BenchmarkReporter.d.ts +45 -0
  17. package/dist/jest/BenchmarkReporter.js +207 -0
  18. package/dist/meta/ContractsMeta.d.ts +10 -0
  19. package/dist/meta/ContractsMeta.js +2 -0
  20. package/dist/metric/ContractDatabase.d.ts +21 -0
  21. package/dist/metric/ContractDatabase.js +111 -0
  22. package/dist/metric/collectMetric.d.ts +106 -0
  23. package/dist/metric/collectMetric.js +217 -0
  24. package/dist/metric/defaultColor.d.ts +2 -0
  25. package/dist/metric/defaultColor.js +45 -0
  26. package/dist/metric/deltaResult.d.ts +39 -0
  27. package/dist/metric/deltaResult.js +210 -0
  28. package/dist/metric/gasReportTable.d.ts +2 -0
  29. package/dist/metric/gasReportTable.js +112 -0
  30. package/dist/metric/index.d.ts +6 -0
  31. package/dist/metric/index.js +22 -0
  32. package/dist/metric/readSnapshots.d.ts +2 -0
  33. package/dist/metric/readSnapshots.js +33 -0
  34. package/dist/utils/readJsonl.d.ts +1 -0
  35. package/dist/utils/readJsonl.js +25 -0
  36. package/jest-environment.js +1 -0
  37. package/jest-reporter.js +1 -0
  38. package/package.json +24 -5
@@ -0,0 +1,217 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.collectMetric = exports.OpCodeReserved = exports.calcComputePhase = exports.calcStateSize = exports.calcCellSize = exports.calcDictSize = exports.calcMessageSize = exports.resetMetricStore = exports.createMetricStore = exports.getMetricStore = exports.makeSnapshotMetric = exports.sortByCreatedAt = exports.isCodeHash = exports.isAddressFriendly = void 0;
4
+ const core_1 = require("@ton/core");
5
+ const ContractDatabase_1 = require("./ContractDatabase");
6
+ function isAddressFriendly(value) {
7
+ return typeof value === 'string' && core_1.Address.isFriendly(value);
8
+ }
9
+ exports.isAddressFriendly = isAddressFriendly;
10
+ function isCodeHash(value) {
11
+ return typeof value === 'string' && value.length === 66 && /^0x[0-9a-fA-F]+$/.test(value);
12
+ }
13
+ exports.isCodeHash = isCodeHash;
14
+ function sortByCreatedAt(reverse = false) {
15
+ return (a, b) => (a.createdAt.getTime() - b.createdAt.getTime()) * (reverse ? -1 : 1);
16
+ }
17
+ exports.sortByCreatedAt = sortByCreatedAt;
18
+ const STORE_METRIC = Symbol.for('ton-sandbox-metric-store');
19
+ function makeSnapshotMetric(store, config = {}) {
20
+ const label = config.label || 'current';
21
+ const contractExcludes = config.contractExcludes || new Array();
22
+ const contractDatabase = config.contractDatabase || ContractDatabase_1.ContractDatabase.from({});
23
+ const snapshot = {
24
+ label,
25
+ createdAt: new Date(),
26
+ items: new Array(),
27
+ };
28
+ // remove duplicates and extract ABI
29
+ const seen = new Set();
30
+ for (const metric of store) {
31
+ const key = JSON.stringify(metric);
32
+ if (seen.has(key))
33
+ continue;
34
+ snapshot.items.push(metric);
35
+ seen.add(key);
36
+ if (metric.codeHash) {
37
+ contractDatabase.extract(metric);
38
+ }
39
+ }
40
+ // ABI auto-mapping
41
+ for (const item of snapshot.items) {
42
+ const find = contractDatabase.by(item);
43
+ if (!item.contractName && find.contractName) {
44
+ item.contractName = find.contractName;
45
+ }
46
+ if (!item.methodName && find.methodName) {
47
+ item.methodName = find.methodName;
48
+ }
49
+ }
50
+ if (contractExcludes.length > 0) {
51
+ snapshot.items = snapshot.items.filter((it) => typeof it.contractName === 'undefined' || !contractExcludes.includes(it.contractName));
52
+ }
53
+ return snapshot;
54
+ }
55
+ exports.makeSnapshotMetric = makeSnapshotMetric;
56
+ function getMetricStore(context = globalThis) {
57
+ return context[STORE_METRIC];
58
+ }
59
+ exports.getMetricStore = getMetricStore;
60
+ function createMetricStore(context = globalThis) {
61
+ if (!Array.isArray(context[STORE_METRIC])) {
62
+ context[STORE_METRIC] = new Array();
63
+ }
64
+ return context[STORE_METRIC];
65
+ }
66
+ exports.createMetricStore = createMetricStore;
67
+ function resetMetricStore(context = globalThis) {
68
+ const store = getMetricStore(context);
69
+ if (store)
70
+ store.length = 0;
71
+ return createMetricStore(context);
72
+ }
73
+ exports.resetMetricStore = resetMetricStore;
74
+ function calcMessageSize(msg) {
75
+ if (msg) {
76
+ return calcCellSize((0, core_1.beginCell)().store((0, core_1.storeMessage)(msg)).endCell());
77
+ }
78
+ return { cells: 0, bits: 0 };
79
+ }
80
+ exports.calcMessageSize = calcMessageSize;
81
+ function calcDictSize(dict) {
82
+ if (dict.size > 0) {
83
+ return calcCellSize((0, core_1.beginCell)().storeDict(dict).endCell().asSlice().loadRef());
84
+ }
85
+ return { cells: 0, bits: 0 };
86
+ }
87
+ exports.calcDictSize = calcDictSize;
88
+ function calcCellSize(root, visited = new Set()) {
89
+ const hash = root.hash().toString('hex');
90
+ if (visited.has(hash)) {
91
+ return { cells: 0, bits: 0 };
92
+ }
93
+ visited.add(hash);
94
+ const out = {
95
+ cells: 1,
96
+ bits: root.bits.length,
97
+ };
98
+ for (const ref of root.refs) {
99
+ const childRes = calcCellSize(ref, visited);
100
+ out.cells += childRes.cells;
101
+ out.bits += childRes.bits;
102
+ }
103
+ return out;
104
+ }
105
+ exports.calcCellSize = calcCellSize;
106
+ function calcStateSize(state) {
107
+ const codeSize = calcCellSize(state.code);
108
+ const dataSize = calcCellSize(state.data);
109
+ return {
110
+ code: codeSize,
111
+ data: dataSize,
112
+ };
113
+ }
114
+ exports.calcStateSize = calcStateSize;
115
+ function calcComputePhase(phase) {
116
+ if (phase.type === 'vm') {
117
+ return {
118
+ type: phase.type,
119
+ success: phase.success,
120
+ gasUsed: Number(phase.gasUsed),
121
+ exitCode: phase.exitCode,
122
+ vmSteps: phase.vmSteps,
123
+ };
124
+ }
125
+ return {
126
+ type: phase.type,
127
+ };
128
+ }
129
+ exports.calcComputePhase = calcComputePhase;
130
+ var OpCodeReserved;
131
+ (function (OpCodeReserved) {
132
+ OpCodeReserved[OpCodeReserved["send"] = 0] = "send";
133
+ OpCodeReserved[OpCodeReserved["notSupported"] = 4294967295] = "notSupported";
134
+ OpCodeReserved[OpCodeReserved["notAllowed"] = 4294967294] = "notAllowed";
135
+ })(OpCodeReserved = exports.OpCodeReserved || (exports.OpCodeReserved = {}));
136
+ async function collectMetric(blockchain, ctx, result) {
137
+ const store = getMetricStore();
138
+ if (!Array.isArray(store)) {
139
+ return;
140
+ }
141
+ let state = { data: { cells: 0, bits: 0 }, code: { cells: 0, bits: 0 } };
142
+ let codeHash;
143
+ if (ctx.contract.init && ctx.contract.init.code && ctx.contract.init.data) {
144
+ codeHash = `0x${ctx.contract.init.code.hash().toString('hex')}`;
145
+ state = calcStateSize({ code: ctx.contract.init.code, data: ctx.contract.init.data });
146
+ }
147
+ else {
148
+ const account = (await blockchain.getContract(ctx.contract.address)).accountState;
149
+ if (account && account.type === 'active' && account.state.code && account.state.data) {
150
+ codeHash = `0x${account.state.code.hash().toString('hex')}`;
151
+ state = calcStateSize({ code: account.state.code, data: account.state.data });
152
+ }
153
+ }
154
+ let testName;
155
+ if (globalThis['expect']) {
156
+ testName = expect.getState().currentTestName;
157
+ }
158
+ let contractName = ctx.contract.constructor.name;
159
+ let methodName = ctx.methodName;
160
+ for (const tx of result.transactions) {
161
+ if (tx.description.type !== 'generic')
162
+ continue;
163
+ const receiver = tx.inMessage?.info.type;
164
+ const body = tx.inMessage?.body ? tx.inMessage.body.beginParse() : undefined;
165
+ let opCode = '0x0';
166
+ if (receiver === 'internal') {
167
+ opCode = `0x${(body && body.remainingBits >= 32 ? body.preloadUint(32) : 0).toString(16)}`;
168
+ }
169
+ if (!methodName && Object.values(OpCodeReserved).includes(Number(opCode))) {
170
+ methodName = OpCodeReserved[Number(opCode)];
171
+ }
172
+ const address = core_1.Address.parseRaw(`0:${tx.address.toString(16).padStart(64, '0')}`);
173
+ const { computePhase, actionPhase, bouncePhase, storagePhase } = tx.description;
174
+ const action = actionPhase
175
+ ? {
176
+ success: actionPhase.success,
177
+ totalActions: actionPhase.totalActions,
178
+ skippedActions: actionPhase.skippedActions,
179
+ resultCode: actionPhase.resultCode,
180
+ totalActionFees: actionPhase.totalActions,
181
+ totalFwdFees: actionPhase.totalFwdFees ? Number(actionPhase.totalFwdFees) : undefined,
182
+ totalMessageSize: {
183
+ cells: Number(actionPhase.totalMessageSize.cells),
184
+ bits: Number(actionPhase.totalMessageSize.bits),
185
+ },
186
+ }
187
+ : undefined;
188
+ const compute = calcComputePhase(computePhase);
189
+ const metric = {
190
+ testName,
191
+ address: address.toString(),
192
+ codeHash,
193
+ contractName,
194
+ methodName,
195
+ receiver,
196
+ opCode,
197
+ execute: {
198
+ compute,
199
+ action,
200
+ },
201
+ message: {
202
+ in: calcMessageSize(tx.inMessage),
203
+ out: calcDictSize(tx.outMessages),
204
+ },
205
+ state,
206
+ };
207
+ store.push(metric);
208
+ methodName = undefined;
209
+ if (!address.equals(ctx.contract.address)) {
210
+ contractName = ctx.contract.constructor.name;
211
+ }
212
+ else {
213
+ contractName = undefined;
214
+ }
215
+ }
216
+ }
217
+ exports.collectMetric = collectMetric;
@@ -0,0 +1,2 @@
1
+ import { DeltaMetric } from './deltaResult';
2
+ export declare function defaultColor(metric: DeltaMetric): string;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.defaultColor = void 0;
27
+ const chalk_1 = __importStar(require("chalk"));
28
+ function defaultColor(metric) {
29
+ if (!chalk_1.supportsColor) {
30
+ return metric.value;
31
+ }
32
+ const { green, redBright, gray, yellowBright } = chalk_1.default;
33
+ let color = gray;
34
+ if (metric.kind === 'decrease') {
35
+ color = green;
36
+ }
37
+ else if (metric.kind === 'increase') {
38
+ color = redBright;
39
+ }
40
+ else if (metric.kind === 'undefined') {
41
+ color = yellowBright;
42
+ }
43
+ return color(metric.value);
44
+ }
45
+ exports.defaultColor = defaultColor;
@@ -0,0 +1,39 @@
1
+ import { AddressFriendly, ContractMethodName, ContractName, Metric, OpCode, SnapshotMetric } from './collectMetric';
2
+ export type KindDelta = 'undefined' | 'init' | 'same' | 'increase' | 'decrease';
3
+ export type PathDelta = string;
4
+ export type ItemDelta = {
5
+ kind: KindDelta;
6
+ path: PathDelta;
7
+ before: number;
8
+ after: number;
9
+ };
10
+ export type ListDelta = Record<PathDelta, ItemDelta>;
11
+ export type DeltaMetric = {
12
+ kind: KindDelta;
13
+ value: string;
14
+ };
15
+ export declare const undefinedDeltaMetric: () => DeltaMetric;
16
+ export type ColorDelta = (metric: DeltaMetric) => string;
17
+ export type DeltaMetrics = Record<string, DeltaMetric>;
18
+ export type MethodDelta = Record<ContractMethodName | OpCode, DeltaMetrics>;
19
+ export type ContractDelta = Record<ContractName | AddressFriendly, MethodDelta>;
20
+ export type DeltaResult = {
21
+ label: string;
22
+ createdAt: Date;
23
+ result: ContractDelta;
24
+ };
25
+ export type DeltaRow = [contract: string, method: string, ...values: DeltaMetric[]];
26
+ export type FlatDeltaResult = {
27
+ header: string[];
28
+ rows: DeltaRow[];
29
+ };
30
+ export declare function toFlatDeltaResult(deltas: DeltaResult[]): {
31
+ header: string[];
32
+ rows: DeltaRow[];
33
+ };
34
+ export declare function aggregatedCompareMetric(before: Metric, after: Metric, basePath?: string[]): ListDelta;
35
+ export declare function prepareDelta(pair: {
36
+ after: SnapshotMetric;
37
+ before?: SnapshotMetric;
38
+ }): DeltaResult;
39
+ export declare function makeGasReport(list: SnapshotMetric[]): Array<DeltaResult>;
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeGasReport = exports.prepareDelta = exports.aggregatedCompareMetric = exports.toFlatDeltaResult = exports.undefinedDeltaMetric = void 0;
4
+ const collectMetric_1 = require("./collectMetric");
5
+ const undefinedDeltaMetric = () => ({
6
+ kind: 'undefined',
7
+ value: '~',
8
+ });
9
+ exports.undefinedDeltaMetric = undefinedDeltaMetric;
10
+ function toFlatDeltaResult(deltas) {
11
+ const out = {
12
+ header: ['Contract', 'Method'],
13
+ rows: [],
14
+ };
15
+ const contractSet = new Set();
16
+ const methodMap = new Map();
17
+ const metricSet = new Set();
18
+ for (const delta of deltas) {
19
+ for (const [contract, methods] of Object.entries(delta.result)) {
20
+ contractSet.add(contract);
21
+ if (!methodMap.has(contract)) {
22
+ methodMap.set(contract, new Set());
23
+ }
24
+ for (const [method, metrics] of Object.entries(methods)) {
25
+ methodMap.get(contract).add(method);
26
+ for (const metric of Object.keys(metrics)) {
27
+ metricSet.add(metric);
28
+ }
29
+ }
30
+ }
31
+ }
32
+ const metricNames = Array.from(metricSet.values());
33
+ out.header.push(...Array.from({ length: deltas.length }, () => metricNames).flat());
34
+ for (const contract of contractSet) {
35
+ const methods = methodMap.get(contract);
36
+ for (const method of methods) {
37
+ const metrics = [];
38
+ for (const delta of deltas) {
39
+ for (const metric of metricNames) {
40
+ metrics.push(delta.result?.[contract]?.[method]?.[metric] ?? (0, exports.undefinedDeltaMetric)());
41
+ }
42
+ }
43
+ out.rows.push([contract, method, ...metrics]);
44
+ }
45
+ }
46
+ return out;
47
+ }
48
+ exports.toFlatDeltaResult = toFlatDeltaResult;
49
+ const aggregateTarget = ['cells', 'bits', 'gasUsed'];
50
+ const statusTarget = ['success', 'exitCode', 'resultCode'];
51
+ const deltaTarget = [
52
+ ...aggregateTarget,
53
+ ...statusTarget,
54
+ 'vmSteps',
55
+ 'totalActions',
56
+ 'skippedActions',
57
+ 'totalActionFees',
58
+ 'data',
59
+ 'code',
60
+ 'state',
61
+ 'message',
62
+ 'in',
63
+ 'out',
64
+ 'execute',
65
+ 'compute',
66
+ 'action',
67
+ ];
68
+ /**
69
+ * Recursively collects the sum of all numeric fields named `needle` within an arbitrary data structure
70
+ */
71
+ function sumDeep(source, needle) {
72
+ if (source === null || typeof source !== 'object') {
73
+ return 0;
74
+ }
75
+ if (Array.isArray(source)) {
76
+ return source.map((item) => sumDeep(item, needle)).reduce((total, current) => total + current, 0);
77
+ }
78
+ let total = 0;
79
+ for (const [key, value] of Object.entries(source)) {
80
+ if (key === needle && typeof value === 'number') {
81
+ total += value;
82
+ continue;
83
+ }
84
+ total += sumDeep(value, needle);
85
+ }
86
+ return total;
87
+ }
88
+ function aggregatedCompareMetric(before, after, basePath = []) {
89
+ const out = {};
90
+ const keys = new Set([...Object.keys(before ?? {}), ...Object.keys(after ?? {})]);
91
+ keys.forEach((key) => {
92
+ if (!deltaTarget.includes(key))
93
+ return;
94
+ const prev = before[key];
95
+ const next = after[key];
96
+ const path = [...basePath, key];
97
+ if (prev === undefined || next === undefined)
98
+ return;
99
+ if (typeof prev === 'object' && typeof next === 'object') {
100
+ Object.assign(out, aggregatedCompareMetric(prev, next, path));
101
+ aggregateTarget.forEach((aggKey) => {
102
+ const beforeSum = sumDeep(prev, aggKey);
103
+ const afterSum = sumDeep(next, aggKey);
104
+ const aggPath = [...path, aggKey].join('.');
105
+ out[aggPath] = {
106
+ kind: beforeSum > afterSum ? 'decrease' : beforeSum < afterSum ? 'increase' : 'same',
107
+ path: aggPath,
108
+ before: beforeSum,
109
+ after: afterSum,
110
+ };
111
+ });
112
+ return;
113
+ }
114
+ if (typeof prev === 'number' && typeof next === 'number') {
115
+ const fullPath = path.join('.');
116
+ out[fullPath] = {
117
+ kind: prev > next ? 'decrease' : prev < next ? 'increase' : 'same',
118
+ path: fullPath,
119
+ before: prev,
120
+ after: next,
121
+ };
122
+ }
123
+ });
124
+ if (basePath.length === 0) {
125
+ aggregateTarget.forEach((aggKey) => {
126
+ const beforeSum = sumDeep(before, aggKey);
127
+ const afterSum = sumDeep(after, aggKey);
128
+ const rootPath = `root.${aggKey}`;
129
+ out[rootPath] = {
130
+ kind: beforeSum > afterSum ? 'decrease' : beforeSum < afterSum ? 'increase' : 'same',
131
+ path: rootPath,
132
+ before: beforeSum,
133
+ after: afterSum,
134
+ };
135
+ });
136
+ }
137
+ return out;
138
+ }
139
+ exports.aggregatedCompareMetric = aggregatedCompareMetric;
140
+ function prepareItemDelta(item, calcDelta = true) {
141
+ const out = (0, exports.undefinedDeltaMetric)();
142
+ if (!item) {
143
+ return out;
144
+ }
145
+ out.kind = calcDelta ? item.kind : 'init';
146
+ out.value = item.after.toString();
147
+ if (calcDelta) {
148
+ let change = item.kind === 'increase' ? ' +' : ' ';
149
+ if (item.kind === 'same') {
150
+ change += 'same';
151
+ }
152
+ else if (item.before === 0) {
153
+ change += '100.00%';
154
+ }
155
+ else {
156
+ change += (((item.after - item.before) / item.before) * 100).toFixed(2) + '%';
157
+ }
158
+ out.value += change;
159
+ }
160
+ return out;
161
+ }
162
+ function prepareDelta(pair) {
163
+ const result = {};
164
+ const out = {
165
+ label: pair.after.label,
166
+ createdAt: pair.after.createdAt,
167
+ result,
168
+ };
169
+ const beforeMap = new Map();
170
+ if (pair.before) {
171
+ for (const b of pair.before.items) {
172
+ const contractKey = b.contractName || b.address;
173
+ const methodKey = b.methodName || b.opCode;
174
+ if (!beforeMap.has(contractKey)) {
175
+ beforeMap.set(contractKey, new Map());
176
+ }
177
+ beforeMap.get(contractKey).set(methodKey, b);
178
+ }
179
+ }
180
+ for (const a of pair.after.items) {
181
+ const contractKey = a.contractName || a.address;
182
+ const methodKey = a.methodName || a.opCode;
183
+ const b = beforeMap.get(contractKey)?.get(methodKey); // может быть undefined
184
+ const calcDelta = !!b;
185
+ if (!result[contractKey]) {
186
+ result[contractKey] = {};
187
+ }
188
+ const item = {};
189
+ result[contractKey][methodKey] = item;
190
+ const delta = aggregatedCompareMetric(b || a, a);
191
+ item.gasUsed = prepareItemDelta(delta['execute.compute.gasUsed'], calcDelta);
192
+ item.cells = prepareItemDelta(delta['state.cells'], calcDelta);
193
+ item.bits = prepareItemDelta(delta['state.bits'], calcDelta);
194
+ }
195
+ return out;
196
+ }
197
+ exports.prepareDelta = prepareDelta;
198
+ function makeGasReport(list) {
199
+ list = (list || []).sort((0, collectMetric_1.sortByCreatedAt)());
200
+ const out = [];
201
+ if (list.length === 0)
202
+ return out;
203
+ for (let i = 0; i < list.length; i++) {
204
+ const after = list[i];
205
+ const before = i > 0 ? list[i - 1] : undefined;
206
+ out.push(prepareDelta({ after, before }));
207
+ }
208
+ return out;
209
+ }
210
+ exports.makeGasReport = makeGasReport;
@@ -0,0 +1,2 @@
1
+ import { ColorDelta, DeltaResult } from './deltaResult';
2
+ export declare function gasReportTable(list: DeltaResult[], color?: ColorDelta): string;
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.gasReportTable = void 0;
4
+ const table_1 = require("table");
5
+ const deltaResult_1 = require("./deltaResult");
6
+ const collectMetric_1 = require("./collectMetric");
7
+ const gapCellWidth = 1;
8
+ const maxCellWidth = 25;
9
+ const border = {
10
+ topBody: '─',
11
+ topJoin: '┬',
12
+ topLeft: '┌',
13
+ topRight: '┐',
14
+ bottomBody: '─',
15
+ bottomJoin: '┴',
16
+ bottomLeft: '└',
17
+ bottomRight: '┘',
18
+ bodyLeft: '│',
19
+ bodyRight: '│',
20
+ bodyJoin: '│',
21
+ joinBody: '─',
22
+ joinLeft: '├',
23
+ joinRight: '┤',
24
+ joinJoin: '┼',
25
+ };
26
+ function wrap(value, max) {
27
+ if (value.length > max) {
28
+ return `${value.slice(0, max - 3)}...`;
29
+ }
30
+ return value;
31
+ }
32
+ function prepareResult(list, color) {
33
+ list = list.sort((0, collectMetric_1.sortByCreatedAt)(true));
34
+ const flat = (0, deltaResult_1.toFlatDeltaResult)(list);
35
+ const rows = [];
36
+ const widthCols = [];
37
+ for (const [contract, method, ...values] of flat.rows) {
38
+ const row = [wrap(contract, maxCellWidth), wrap(method, maxCellWidth)];
39
+ widthCols[0] = Math.max(widthCols[0] ?? 0, row[0].length);
40
+ widthCols[1] = Math.max(widthCols[1] ?? 0, row[1].length);
41
+ values.forEach((metric, idx) => {
42
+ const value = color ? color(metric) : metric.value;
43
+ row.push(value);
44
+ const colIdx = idx + 2;
45
+ widthCols[colIdx] = Math.max(widthCols[colIdx] ?? 0, metric.value.length);
46
+ });
47
+ rows.push(row);
48
+ }
49
+ const headers = flat.header;
50
+ for (let i = 0; i < headers.length; i++) {
51
+ widthCols[i] = Math.max(widthCols[i] ?? 0, headers[i].length + gapCellWidth);
52
+ }
53
+ const groupIndex = {};
54
+ let current = 0;
55
+ while (current < rows.length) {
56
+ const contract = rows[current][0];
57
+ let count = 1;
58
+ while (rows[current + count]?.[0] === contract) {
59
+ count++;
60
+ }
61
+ groupIndex[contract] = { index: current, size: count };
62
+ current += count;
63
+ }
64
+ return {
65
+ labels: list.map((s) => s.label),
66
+ headers,
67
+ widthCols,
68
+ rows,
69
+ groupIndex,
70
+ };
71
+ }
72
+ function gasReportTable(list, color) {
73
+ const result = prepareResult(list, color);
74
+ if (result.rows.length < 1) {
75
+ return 'No data available';
76
+ }
77
+ const columns = [];
78
+ for (let i = 0; i < result.headers.length; i++) {
79
+ columns.push({ alignment: 'center', verticalAlignment: 'middle', width: result.widthCols[i] });
80
+ }
81
+ const spanningCells = [
82
+ { col: 0, row: 0, rowSpan: 2, verticalAlignment: 'middle' },
83
+ { col: 1, row: 0, rowSpan: 2, verticalAlignment: 'middle' }, // Method title
84
+ ];
85
+ for (const group of Object.values(result.groupIndex)) {
86
+ // rowSpan for Contract name
87
+ spanningCells.push({ col: 0, row: group.index + 2, rowSpan: group.size, verticalAlignment: 'middle' });
88
+ }
89
+ const data = [[], ['', '']];
90
+ data[0].push(...result.headers.slice(0, 2));
91
+ data[1].push(...result.headers.slice(2));
92
+ const metricCount = (result.headers.length - 2) / list.length;
93
+ let labelTitleOffset = 2;
94
+ for (const label of result.labels) {
95
+ spanningCells.push({
96
+ col: labelTitleOffset,
97
+ row: 0,
98
+ colSpan: metricCount,
99
+ verticalAlignment: 'middle',
100
+ });
101
+ labelTitleOffset += metricCount;
102
+ data[0].push(...[label, ...Array.from({ length: metricCount - 1 }, () => '')]);
103
+ }
104
+ data.push(...result.rows);
105
+ const config = {
106
+ columns,
107
+ spanningCells,
108
+ border,
109
+ };
110
+ return (0, table_1.table)(data, config);
111
+ }
112
+ exports.gasReportTable = gasReportTable;
@@ -0,0 +1,6 @@
1
+ export * from './collectMetric';
2
+ export * from './ContractDatabase';
3
+ export * from './defaultColor';
4
+ export * from './deltaResult';
5
+ export * from './gasReportTable';
6
+ export * from './readSnapshots';
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./collectMetric"), exports);
18
+ __exportStar(require("./ContractDatabase"), exports);
19
+ __exportStar(require("./defaultColor"), exports);
20
+ __exportStar(require("./deltaResult"), exports);
21
+ __exportStar(require("./gasReportTable"), exports);
22
+ __exportStar(require("./readSnapshots"), exports);
@@ -0,0 +1,2 @@
1
+ import { SnapshotMetricList } from './collectMetric';
2
+ export declare function readSnapshots(snapshotDir: string): Promise<SnapshotMetricList>;