@acala-network/chopsticks 0.3.10 → 0.3.12
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/dist/blockchain/block-builder.d.ts +3 -1
- package/dist/blockchain/block-builder.js +29 -10
- package/dist/blockchain/index.d.ts +3 -2
- package/dist/blockchain/index.js +8 -3
- package/dist/blockchain/txpool.d.ts +4 -0
- package/dist/blockchain/txpool.js +7 -2
- package/dist/cli.js +12 -2
- package/dist/dry-run-preimage.d.ts +2 -0
- package/dist/dry-run-preimage.js +99 -0
- package/dist/logger.js +7 -3
- package/dist/rpc/substrate/author.js +21 -5
- package/dist/rpc/substrate/chain.d.ts +1 -0
- package/dist/rpc/substrate/chain.js +10 -4
- package/dist/server.js +6 -1
- package/dist/utils/decoder.js +5 -1
- package/dist/utils/set-storage.js +4 -2
- package/package.json +4 -4
- package/template/diff.html +139 -78
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { Header, TransactionValidityError } from '@polkadot/types/interfaces';
|
|
1
2
|
import { Block, TaskCallResponse } from './block';
|
|
2
3
|
import { HexString } from '@polkadot/util/types';
|
|
3
|
-
export declare const
|
|
4
|
+
export declare const newHeader: (head: Block) => Promise<Header>;
|
|
5
|
+
export declare const buildBlock: (head: Block, inherents: HexString[], extrinsics: HexString[], onApplyExtrinsicError: (extrinsic: HexString, error: TransactionValidityError) => void) => Promise<[Block, HexString[]]>;
|
|
4
6
|
export declare const dryRunExtrinsic: (head: Block, inherents: HexString[], extrinsic: HexString | {
|
|
5
7
|
call: HexString;
|
|
6
8
|
address: string;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.dryRunInherents = exports.dryRunExtrinsic = exports.buildBlock = void 0;
|
|
3
|
+
exports.dryRunInherents = exports.dryRunExtrinsic = exports.buildBlock = exports.newHeader = void 0;
|
|
4
4
|
const block_1 = require("./block");
|
|
5
|
+
const util_crypto_1 = require("@polkadot/util-crypto");
|
|
5
6
|
const util_1 = require("@polkadot/util");
|
|
6
7
|
const utils_1 = require("../utils");
|
|
7
8
|
const logger_1 = require("../logger");
|
|
@@ -84,6 +85,7 @@ const newHeader = async (head) => {
|
|
|
84
85
|
});
|
|
85
86
|
return header;
|
|
86
87
|
};
|
|
88
|
+
exports.newHeader = newHeader;
|
|
87
89
|
const initNewBlock = async (head, header, inherents) => {
|
|
88
90
|
const blockNumber = header.number.toNumber();
|
|
89
91
|
const hash = `0x${Math.round(Math.random() * 100000000)
|
|
@@ -110,22 +112,34 @@ const initNewBlock = async (head, header, inherents) => {
|
|
|
110
112
|
}
|
|
111
113
|
return newBlock;
|
|
112
114
|
};
|
|
113
|
-
const buildBlock = async (head, inherents, extrinsics) => {
|
|
115
|
+
const buildBlock = async (head, inherents, extrinsics, onApplyExtrinsicError) => {
|
|
114
116
|
const registry = await head.registry;
|
|
115
|
-
const header = await newHeader(head);
|
|
117
|
+
const header = await (0, exports.newHeader)(head);
|
|
116
118
|
const newBlock = await initNewBlock(head, header, inherents);
|
|
117
119
|
logger.info({
|
|
118
120
|
number: newBlock.number,
|
|
119
121
|
extrinsicsCount: extrinsics.length,
|
|
120
122
|
tempHash: newBlock.hash,
|
|
121
|
-
}, `
|
|
123
|
+
}, `Try building block #${newBlock.number.toLocaleString()}`);
|
|
122
124
|
const pendingExtrinsics = [];
|
|
125
|
+
const includedExtrinsic = [];
|
|
123
126
|
// apply extrinsics
|
|
124
127
|
for (const extrinsic of extrinsics) {
|
|
125
128
|
try {
|
|
126
|
-
const { storageDiff } = await newBlock.call('BlockBuilder_apply_extrinsic', [extrinsic]);
|
|
129
|
+
const { result, storageDiff } = await newBlock.call('BlockBuilder_apply_extrinsic', [extrinsic]);
|
|
130
|
+
const outcome = registry.createType('ApplyExtrinsicResult', result);
|
|
131
|
+
if (outcome.isErr) {
|
|
132
|
+
if (outcome.asErr.isInvalid && outcome.asErr.asInvalid.isFuture) {
|
|
133
|
+
pendingExtrinsics.push(extrinsic);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
onApplyExtrinsicError(extrinsic, outcome.asErr);
|
|
137
|
+
}
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
127
140
|
newBlock.pushStorageLayer().setAll(storageDiff);
|
|
128
141
|
logger.trace((0, logger_1.truncate)(storageDiff), 'Applied extrinsic');
|
|
142
|
+
includedExtrinsic.push(extrinsic);
|
|
129
143
|
}
|
|
130
144
|
catch (e) {
|
|
131
145
|
logger.info('Failed to apply extrinsic %o %s', e, e);
|
|
@@ -140,23 +154,28 @@ const buildBlock = async (head, inherents, extrinsics) => {
|
|
|
140
154
|
}
|
|
141
155
|
const blockData = registry.createType('Block', {
|
|
142
156
|
header,
|
|
143
|
-
extrinsics,
|
|
157
|
+
extrinsics: includedExtrinsic,
|
|
144
158
|
});
|
|
145
159
|
const storageDiff = await newBlock.storageDiff();
|
|
146
160
|
logger.trace(Object.entries(storageDiff).map(([key, value]) => [key, (0, logger_1.truncate)(value)]), 'Final block');
|
|
147
161
|
const finalBlock = new block_1.Block(head.chain, newBlock.number, blockData.hash.toHex(), head, {
|
|
148
162
|
header,
|
|
149
|
-
extrinsics: [...inherents, ...
|
|
163
|
+
extrinsics: [...inherents, ...includedExtrinsic],
|
|
150
164
|
storage: head.storage,
|
|
151
165
|
storageDiff,
|
|
152
166
|
});
|
|
153
|
-
logger.info({
|
|
167
|
+
logger.info({
|
|
168
|
+
hash: finalBlock.hash,
|
|
169
|
+
extrinsics: includedExtrinsic.map((x) => (0, util_crypto_1.blake2AsHex)(x, 256)),
|
|
170
|
+
pendingExtrinsics: pendingExtrinsics.length,
|
|
171
|
+
number: newBlock.number,
|
|
172
|
+
}, `Block built #${newBlock.number.toLocaleString()} hash ${finalBlock.hash}`);
|
|
154
173
|
return [finalBlock, pendingExtrinsics];
|
|
155
174
|
};
|
|
156
175
|
exports.buildBlock = buildBlock;
|
|
157
176
|
const dryRunExtrinsic = async (head, inherents, extrinsic) => {
|
|
158
177
|
const registry = await head.registry;
|
|
159
|
-
const header = await newHeader(head);
|
|
178
|
+
const header = await (0, exports.newHeader)(head);
|
|
160
179
|
const newBlock = await initNewBlock(head, header, inherents);
|
|
161
180
|
if (typeof extrinsic !== 'string') {
|
|
162
181
|
if (!head.chain.mockSignatureHost) {
|
|
@@ -185,7 +204,7 @@ const dryRunExtrinsic = async (head, inherents, extrinsic) => {
|
|
|
185
204
|
};
|
|
186
205
|
exports.dryRunExtrinsic = dryRunExtrinsic;
|
|
187
206
|
const dryRunInherents = async (head, inherents) => {
|
|
188
|
-
const header = await newHeader(head);
|
|
207
|
+
const header = await (0, exports.newHeader)(head);
|
|
189
208
|
const newBlock = await initNewBlock(head, header, inherents);
|
|
190
209
|
return Object.entries(await newBlock.storageDiff());
|
|
191
210
|
};
|
|
@@ -4,7 +4,7 @@ import { HexString } from '@polkadot/util/types';
|
|
|
4
4
|
import { RegisteredTypes } from '@polkadot/types/types';
|
|
5
5
|
import { Api } from '../api';
|
|
6
6
|
import { Block } from './block';
|
|
7
|
-
import { BuildBlockMode, BuildBlockParams, HorizontalMessage } from './txpool';
|
|
7
|
+
import { BuildBlockMode, BuildBlockParams, HorizontalMessage, TxPool } from './txpool';
|
|
8
8
|
import { HeadState } from './head-state';
|
|
9
9
|
import { InherentProvider } from './inherent';
|
|
10
10
|
export interface Options {
|
|
@@ -31,7 +31,7 @@ export declare class Blockchain {
|
|
|
31
31
|
readonly headState: HeadState;
|
|
32
32
|
constructor({ api, buildBlockMode, inherentProvider, db, header, mockSignatureHost, allowUnresolvedImports, registeredTypes, }: Options);
|
|
33
33
|
get head(): Block;
|
|
34
|
-
get
|
|
34
|
+
get txPool(): TxPool;
|
|
35
35
|
getBlockAt(number?: number): Promise<Block | undefined>;
|
|
36
36
|
getBlock(hash?: HexString): Promise<Block | undefined>;
|
|
37
37
|
unregisterBlock(block: Block): void;
|
|
@@ -47,4 +47,5 @@ export declare class Blockchain {
|
|
|
47
47
|
storageDiff: [HexString, HexString | null][];
|
|
48
48
|
}>;
|
|
49
49
|
dryRunHrmp(hrmp: Record<number, HorizontalMessage[]>): Promise<[HexString, HexString | null][]>;
|
|
50
|
+
getInherents(): Promise<HexString[]>;
|
|
50
51
|
}
|
package/dist/blockchain/index.js
CHANGED
|
@@ -42,8 +42,8 @@ class Blockchain {
|
|
|
42
42
|
get head() {
|
|
43
43
|
return this.#head;
|
|
44
44
|
}
|
|
45
|
-
get
|
|
46
|
-
return this.#txpool
|
|
45
|
+
get txPool() {
|
|
46
|
+
return this.#txpool;
|
|
47
47
|
}
|
|
48
48
|
async getBlockAt(number) {
|
|
49
49
|
if (number === undefined) {
|
|
@@ -115,7 +115,7 @@ class Blockchain {
|
|
|
115
115
|
this.#txpool.submitExtrinsic(extrinsic);
|
|
116
116
|
return (0, util_crypto_1.blake2AsHex)(extrinsic, 256);
|
|
117
117
|
}
|
|
118
|
-
throw
|
|
118
|
+
throw validity.asErr;
|
|
119
119
|
}
|
|
120
120
|
async newBlock(params) {
|
|
121
121
|
await this.#txpool.buildBlock(params);
|
|
@@ -142,5 +142,10 @@ class Blockchain {
|
|
|
142
142
|
const inherents = await this.#inherentProvider.createInherents(head, { horizontalMessages: hrmp });
|
|
143
143
|
return (0, block_builder_1.dryRunInherents)(head, inherents);
|
|
144
144
|
}
|
|
145
|
+
async getInherents() {
|
|
146
|
+
await this.api.isReady;
|
|
147
|
+
const inherents = await this.#inherentProvider.createInherents(this.head);
|
|
148
|
+
return inherents;
|
|
149
|
+
}
|
|
145
150
|
}
|
|
146
151
|
exports.Blockchain = Blockchain;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { EventEmitter } from 'node:stream';
|
|
1
3
|
import { HexString } from '@polkadot/util/types';
|
|
2
4
|
import { Block } from './block';
|
|
3
5
|
import { Blockchain } from '.';
|
|
4
6
|
import { InherentProvider } from './inherent';
|
|
7
|
+
export declare const APPLY_EXTRINSIC_ERROR = "TxPool::ApplyExtrinsicError";
|
|
5
8
|
export declare enum BuildBlockMode {
|
|
6
9
|
Batch = 0,
|
|
7
10
|
Instant = 1,
|
|
@@ -23,6 +26,7 @@ export interface BuildBlockParams {
|
|
|
23
26
|
}
|
|
24
27
|
export declare class TxPool {
|
|
25
28
|
#private;
|
|
29
|
+
readonly event: EventEmitter;
|
|
26
30
|
constructor(chain: Blockchain, inherentProvider: InherentProvider, mode?: BuildBlockMode);
|
|
27
31
|
get pendingExtrinsics(): HexString[];
|
|
28
32
|
submitExtrinsic(extrinsic: HexString): void;
|
|
@@ -3,11 +3,13 @@ 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.TxPool = exports.BuildBlockMode = void 0;
|
|
6
|
+
exports.TxPool = exports.BuildBlockMode = exports.APPLY_EXTRINSIC_ERROR = void 0;
|
|
7
7
|
const rxjs_1 = require("rxjs");
|
|
8
|
+
const node_stream_1 = require("node:stream");
|
|
8
9
|
const operators_1 = require("rxjs/operators");
|
|
9
10
|
const lodash_1 = __importDefault(require("lodash"));
|
|
10
11
|
const block_builder_1 = require("./block-builder");
|
|
12
|
+
exports.APPLY_EXTRINSIC_ERROR = 'TxPool::ApplyExtrinsicError';
|
|
11
13
|
var BuildBlockMode;
|
|
12
14
|
(function (BuildBlockMode) {
|
|
13
15
|
BuildBlockMode[BuildBlockMode["Batch"] = 0] = "Batch";
|
|
@@ -19,6 +21,7 @@ class TxPool {
|
|
|
19
21
|
#pool = [];
|
|
20
22
|
#mode;
|
|
21
23
|
#inherentProvider;
|
|
24
|
+
event = new node_stream_1.EventEmitter();
|
|
22
25
|
#last;
|
|
23
26
|
#lastBuildBlockPromise = Promise.resolve();
|
|
24
27
|
constructor(chain, inherentProvider, mode = BuildBlockMode.Batch) {
|
|
@@ -62,7 +65,9 @@ class TxPool {
|
|
|
62
65
|
const head = this.#chain.head;
|
|
63
66
|
const extrinsics = this.#pool.splice(0);
|
|
64
67
|
const inherents = await this.#inherentProvider.createInherents(head, params?.inherent);
|
|
65
|
-
const [newBlock, pendingExtrinsics] = await (0, block_builder_1.buildBlock)(head, inherents, extrinsics)
|
|
68
|
+
const [newBlock, pendingExtrinsics] = await (0, block_builder_1.buildBlock)(head, inherents, extrinsics, (extrinsic, error) => {
|
|
69
|
+
this.event.emit(exports.APPLY_EXTRINSIC_ERROR, [extrinsic, error]);
|
|
70
|
+
});
|
|
66
71
|
this.#pool.push(...pendingExtrinsics);
|
|
67
72
|
await this.#chain.setHead(newBlock);
|
|
68
73
|
}
|
package/dist/cli.js
CHANGED
|
@@ -12,6 +12,7 @@ const _1 = require(".");
|
|
|
12
12
|
const schema_1 = require("./schema");
|
|
13
13
|
const decoder_1 = require("./utils/decoder");
|
|
14
14
|
const dry_run_1 = require("./dry-run");
|
|
15
|
+
const dry_run_preimage_1 = require("./dry-run-preimage");
|
|
15
16
|
const utils_1 = require("./utils");
|
|
16
17
|
const run_block_1 = require("./run-block");
|
|
17
18
|
const processConfig = async (path) => {
|
|
@@ -78,12 +79,15 @@ const defaultOptions = {
|
|
|
78
79
|
extrinsic: {
|
|
79
80
|
desc: 'Extrinsic or call to dry run. If you pass call here then address is required to fake signature',
|
|
80
81
|
string: true,
|
|
81
|
-
required: true,
|
|
82
82
|
},
|
|
83
83
|
address: {
|
|
84
84
|
desc: 'Address to fake sign extrinsic',
|
|
85
85
|
string: true,
|
|
86
86
|
},
|
|
87
|
+
preimage: {
|
|
88
|
+
desc: 'Preimage to dry run',
|
|
89
|
+
string: true,
|
|
90
|
+
},
|
|
87
91
|
at: {
|
|
88
92
|
desc: 'Block hash to dry run',
|
|
89
93
|
string: true,
|
|
@@ -99,7 +103,13 @@ const defaultOptions = {
|
|
|
99
103
|
desc: 'Open generated html',
|
|
100
104
|
},
|
|
101
105
|
}), async (argv) => {
|
|
102
|
-
|
|
106
|
+
const config = await processArgv(argv);
|
|
107
|
+
if (config.preimage) {
|
|
108
|
+
await (0, dry_run_preimage_1.dryRunPreimage)(config);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
await (0, dry_run_1.dryRun)(config);
|
|
112
|
+
}
|
|
103
113
|
})
|
|
104
114
|
.command('dev', 'Dev mode', (yargs) => yargs.options({
|
|
105
115
|
...defaultOptions,
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dryRunPreimage = void 0;
|
|
4
|
+
const util_crypto_1 = require("@polkadot/util-crypto");
|
|
5
|
+
const util_1 = require("@polkadot/util");
|
|
6
|
+
const logger_1 = require("./logger");
|
|
7
|
+
const generate_html_diff_1 = require("./utils/generate-html-diff");
|
|
8
|
+
const block_builder_1 = require("./blockchain/block-builder");
|
|
9
|
+
const open_html_1 = require("./utils/open-html");
|
|
10
|
+
const executor_1 = require("./executor");
|
|
11
|
+
const set_storage_1 = require("./utils/set-storage");
|
|
12
|
+
const setup_1 = require("./setup");
|
|
13
|
+
const dryRunPreimage = async (argv) => {
|
|
14
|
+
const context = await (0, setup_1.setup)(argv);
|
|
15
|
+
const extrinsic = argv['preimage'];
|
|
16
|
+
const block = context.chain.head;
|
|
17
|
+
const registry = await block.registry;
|
|
18
|
+
const header = await (0, block_builder_1.newHeader)(block);
|
|
19
|
+
const data = (0, util_1.hexToU8a)(extrinsic);
|
|
20
|
+
const hash = (0, util_crypto_1.blake2AsHex)(data, 256);
|
|
21
|
+
await (0, set_storage_1.setStorage)(context.chain, {
|
|
22
|
+
Preimage: {
|
|
23
|
+
PreimageFor: [[[[hash, data.byteLength]], extrinsic]],
|
|
24
|
+
StatusFor: [
|
|
25
|
+
[
|
|
26
|
+
[hash],
|
|
27
|
+
{
|
|
28
|
+
Requested: {
|
|
29
|
+
count: 1,
|
|
30
|
+
len: data.byteLength,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
Scheduler: {
|
|
37
|
+
Agenda: [
|
|
38
|
+
[
|
|
39
|
+
[block.number + 1],
|
|
40
|
+
[
|
|
41
|
+
{
|
|
42
|
+
maybeId: '0x64656d6f637261633a0000000000000000000000000000000000000000000000',
|
|
43
|
+
priority: 63,
|
|
44
|
+
call: {
|
|
45
|
+
Lookup: {
|
|
46
|
+
hash: hash,
|
|
47
|
+
len: data.byteLength,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
origin: { system: { Root: null } },
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
],
|
|
54
|
+
],
|
|
55
|
+
Lookup: [[['0x64656d6f637261633a0000000000000000000000000000000000000000000000'], [block.number + 1, 0]]],
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
const calls = [['Core_initialize_block', [header.toHex()]]];
|
|
59
|
+
for (const inherent of await block.chain.getInherents()) {
|
|
60
|
+
calls.push(['BlockBuilder_apply_extrinsic', [inherent]]);
|
|
61
|
+
}
|
|
62
|
+
calls.push(['BlockBuilder_finalize_block', []]);
|
|
63
|
+
logger_1.defaultLogger.info({ preimage: registry.createType('Call', data).toHuman() }, 'Dry run preimage');
|
|
64
|
+
const result = await (0, executor_1.runTask)({
|
|
65
|
+
wasm: await block.wasm,
|
|
66
|
+
calls,
|
|
67
|
+
storage: [],
|
|
68
|
+
mockSignatureHost: false,
|
|
69
|
+
allowUnresolvedImports: false,
|
|
70
|
+
}, (0, executor_1.taskHandler)(block));
|
|
71
|
+
if (result.Error) {
|
|
72
|
+
throw new Error(result.Error);
|
|
73
|
+
}
|
|
74
|
+
const filePath = await (0, generate_html_diff_1.generateHtmlDiffPreviewFile)(block, result.Call.storageDiff, hash);
|
|
75
|
+
console.log(`Generated preview ${filePath}`);
|
|
76
|
+
if (argv['open']) {
|
|
77
|
+
(0, open_html_1.openHtml)(filePath);
|
|
78
|
+
}
|
|
79
|
+
// if dry-run preimage has extrinsic arguments then dry-run extrinsic
|
|
80
|
+
// this is usefull to test something after preimage is applied
|
|
81
|
+
if (argv['extrinsic']) {
|
|
82
|
+
await context.chain.newBlock();
|
|
83
|
+
const input = argv['address'] ? { call: argv['extrinsic'], address: argv['address'] } : argv['extrinsic'];
|
|
84
|
+
const { outcome, storageDiff } = await context.chain.dryRunExtrinsic(input);
|
|
85
|
+
if (outcome.isErr) {
|
|
86
|
+
throw new Error(outcome.asErr.toString());
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
logger_1.defaultLogger.info(outcome.toHuman(), 'dry_run_outcome');
|
|
90
|
+
}
|
|
91
|
+
const filePath = await (0, generate_html_diff_1.generateHtmlDiffPreviewFile)(context.chain.head, storageDiff, (0, util_crypto_1.blake2AsHex)(argv['extrinsic'], 256));
|
|
92
|
+
console.log(`Generated preview ${filePath}`);
|
|
93
|
+
if (argv['open']) {
|
|
94
|
+
(0, open_html_1.openHtml)(filePath);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
process.exit(0);
|
|
98
|
+
};
|
|
99
|
+
exports.dryRunPreimage = dryRunPreimage;
|
package/dist/logger.js
CHANGED
|
@@ -11,10 +11,13 @@ exports.defaultLogger = (0, pino_1.default)({
|
|
|
11
11
|
target: 'pino-pretty',
|
|
12
12
|
},
|
|
13
13
|
});
|
|
14
|
-
const
|
|
14
|
+
const innerTruncate = (level = 0) => (val) => {
|
|
15
15
|
if (val == null) {
|
|
16
16
|
return val;
|
|
17
17
|
}
|
|
18
|
+
if (level > 5) {
|
|
19
|
+
return '( Too Deep )';
|
|
20
|
+
}
|
|
18
21
|
switch (typeof val) {
|
|
19
22
|
case 'string':
|
|
20
23
|
if (val.length > 66) {
|
|
@@ -25,11 +28,12 @@ const truncate = (val) => {
|
|
|
25
28
|
}
|
|
26
29
|
case 'object':
|
|
27
30
|
if (Array.isArray(val)) {
|
|
28
|
-
return val.map(
|
|
31
|
+
return val.map(innerTruncate(level + 1));
|
|
29
32
|
}
|
|
30
|
-
return Object.fromEntries(Object.entries(val).map(([k, v]) => [k, (
|
|
33
|
+
return Object.fromEntries(Object.entries(val.toJSON ? val.toJSON() : val).map(([k, v]) => [k, innerTruncate(level + 1)(v)]));
|
|
31
34
|
default:
|
|
32
35
|
return val;
|
|
33
36
|
}
|
|
34
37
|
};
|
|
38
|
+
const truncate = (val) => innerTruncate(0)(val);
|
|
35
39
|
exports.truncate = truncate;
|
|
@@ -1,19 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const txpool_1 = require("../../blockchain/txpool");
|
|
3
4
|
const shared_1 = require("../../rpc/shared");
|
|
4
5
|
const logger_1 = require("../../logger");
|
|
5
6
|
const logger = logger_1.defaultLogger.child({ name: 'rpc-author' });
|
|
6
7
|
const handlers = {
|
|
7
8
|
author_submitExtrinsic: async (context, [extrinsic]) => {
|
|
8
9
|
return context.chain.submitExtrinsic(extrinsic).catch((error) => {
|
|
9
|
-
|
|
10
|
+
const code = error.isInvalid ? 1010 : 1011;
|
|
11
|
+
throw new shared_1.ResponseError(code, error.toString());
|
|
10
12
|
});
|
|
11
13
|
},
|
|
12
14
|
author_submitAndWatchExtrinsic: async (context, [extrinsic], { subscribe, unsubscribe }) => {
|
|
13
15
|
let update = (_block) => { };
|
|
14
16
|
const id = context.chain.headState.subscribeHead((block) => update(block));
|
|
15
17
|
const callback = subscribe('author_extrinsicUpdate', id, () => context.chain.headState.unsubscribeHead(id));
|
|
18
|
+
const onExtrinsicFail = ([failedExtrinsic, error]) => {
|
|
19
|
+
if (failedExtrinsic === extrinsic) {
|
|
20
|
+
callback(error.toJSON());
|
|
21
|
+
done(id);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
context.chain.txPool.event.on(txpool_1.APPLY_EXTRINSIC_ERROR, onExtrinsicFail);
|
|
25
|
+
const done = (id) => {
|
|
26
|
+
context.chain.txPool.event.removeListener(txpool_1.APPLY_EXTRINSIC_ERROR, onExtrinsicFail);
|
|
27
|
+
unsubscribe(id);
|
|
28
|
+
};
|
|
16
29
|
update = async (block) => {
|
|
30
|
+
const extrisnics = await block.extrinsics;
|
|
31
|
+
if (!extrisnics.includes(extrinsic))
|
|
32
|
+
return;
|
|
17
33
|
logger.debug({ block: block.hash }, 'author_extrinsicUpdate');
|
|
18
34
|
// for now just assume tx is always included on next block
|
|
19
35
|
callback({
|
|
@@ -22,7 +38,7 @@ const handlers = {
|
|
|
22
38
|
callback({
|
|
23
39
|
Finalized: block.hash,
|
|
24
40
|
});
|
|
25
|
-
|
|
41
|
+
done(id);
|
|
26
42
|
};
|
|
27
43
|
context.chain
|
|
28
44
|
.submitExtrinsic(extrinsic)
|
|
@@ -33,8 +49,8 @@ const handlers = {
|
|
|
33
49
|
})
|
|
34
50
|
.catch((error) => {
|
|
35
51
|
logger.error({ error }, 'ExtrinsicFailed');
|
|
36
|
-
callback(
|
|
37
|
-
|
|
52
|
+
callback(error.toJSON());
|
|
53
|
+
done(id);
|
|
38
54
|
});
|
|
39
55
|
return id;
|
|
40
56
|
},
|
|
@@ -42,7 +58,7 @@ const handlers = {
|
|
|
42
58
|
unsubscribe(subid);
|
|
43
59
|
},
|
|
44
60
|
author_pendingExtrinsics: async (context) => {
|
|
45
|
-
return context.chain.pendingExtrinsics;
|
|
61
|
+
return context.chain.txPool.pendingExtrinsics;
|
|
46
62
|
},
|
|
47
63
|
};
|
|
48
64
|
exports.default = handlers;
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const shared_1 = require("../shared");
|
|
4
|
+
const processHeader = (header) => {
|
|
5
|
+
const res = header.toJSON();
|
|
6
|
+
res.number = '0x' + res.number.toString(16); // number is hex format
|
|
7
|
+
return res;
|
|
8
|
+
};
|
|
4
9
|
const handlers = {
|
|
5
10
|
chain_getBlockHash: async (context, [blockNumber]) => {
|
|
6
11
|
const block = await context.chain.getBlockAt(blockNumber);
|
|
@@ -14,7 +19,7 @@ const handlers = {
|
|
|
14
19
|
if (!block) {
|
|
15
20
|
throw new shared_1.ResponseError(1, `Block ${hash} not found`);
|
|
16
21
|
}
|
|
17
|
-
return await block.header;
|
|
22
|
+
return processHeader(await block.header);
|
|
18
23
|
},
|
|
19
24
|
chain_getBlock: async (context, [hash]) => {
|
|
20
25
|
const block = await context.chain.getBlock(hash);
|
|
@@ -37,7 +42,7 @@ const handlers = {
|
|
|
37
42
|
const id = context.chain.headState.subscribeHead(() => update());
|
|
38
43
|
const callback = subscribe('chain_newHead', id, () => context.chain.headState.unsubscribeHead(id));
|
|
39
44
|
update = async () => {
|
|
40
|
-
callback(await context.chain.head.header);
|
|
45
|
+
callback(processHeader(await context.chain.head.header));
|
|
41
46
|
};
|
|
42
47
|
update();
|
|
43
48
|
return id;
|
|
@@ -45,9 +50,9 @@ const handlers = {
|
|
|
45
50
|
chain_subscribeFinalizedHeads: async (context, _params, { subscribe }) => {
|
|
46
51
|
let update = () => { };
|
|
47
52
|
const id = context.chain.headState.subscribeHead(() => update());
|
|
48
|
-
const callback = subscribe('
|
|
53
|
+
const callback = subscribe('chain_finalizedHead', id, () => context.chain.headState.unsubscribeHead(id));
|
|
49
54
|
update = async () => {
|
|
50
|
-
callback(await context.chain.head.header);
|
|
55
|
+
callback(processHeader(await context.chain.head.header));
|
|
51
56
|
};
|
|
52
57
|
update();
|
|
53
58
|
return id;
|
|
@@ -59,6 +64,7 @@ const handlers = {
|
|
|
59
64
|
const alias = {
|
|
60
65
|
chain_subscribeNewHeads: handlers.chain_subscribeNewHead,
|
|
61
66
|
chain_unsubscribeNewHeads: handlers.chain_unsubscribeNewHead,
|
|
67
|
+
chain_unsubscribeFinalizedHeads: handlers.chain_unsubscribeNewHead,
|
|
62
68
|
};
|
|
63
69
|
exports.default = {
|
|
64
70
|
...handlers,
|
package/dist/server.js
CHANGED
|
@@ -76,6 +76,7 @@ const createServer = async (handler, port) => {
|
|
|
76
76
|
subscriptions[subid] = onCancel;
|
|
77
77
|
return (data) => {
|
|
78
78
|
if (subscriptions[subid]) {
|
|
79
|
+
logger.trace({ method, subid, data: (0, logger_1.truncate)(data) }, 'Subscription notification');
|
|
79
80
|
send({
|
|
80
81
|
jsonrpc: '2.0',
|
|
81
82
|
method,
|
|
@@ -128,7 +129,11 @@ const createServer = async (handler, port) => {
|
|
|
128
129
|
}, 'Received message');
|
|
129
130
|
try {
|
|
130
131
|
const resp = await handler(req, subscriptionManager);
|
|
131
|
-
logger.trace(
|
|
132
|
+
logger.trace({
|
|
133
|
+
id: req.id,
|
|
134
|
+
method: req.method,
|
|
135
|
+
result: (0, logger_1.truncate)(resp),
|
|
136
|
+
}, 'Sending response for request');
|
|
132
137
|
send({
|
|
133
138
|
id: req.id,
|
|
134
139
|
jsonrpc: '2.0',
|
package/dist/utils/decoder.js
CHANGED
|
@@ -100,6 +100,10 @@ const decodeStorageDiff = async (block, diff) => {
|
|
|
100
100
|
lodash_1.default.merge(oldState, await (0, exports.decodeKeyValue)(block, key, (await block.get(key))));
|
|
101
101
|
lodash_1.default.merge(newState, await (0, exports.decodeKeyValue)(block, key, value));
|
|
102
102
|
}
|
|
103
|
-
|
|
103
|
+
const oldStateWithoutEvents = lodash_1.default.cloneDeep(oldState);
|
|
104
|
+
if (oldStateWithoutEvents['system']?.['events']) {
|
|
105
|
+
oldStateWithoutEvents['system']['events'] = [];
|
|
106
|
+
}
|
|
107
|
+
return [oldState, newState, diffPatcher.diff(oldStateWithoutEvents, newState)];
|
|
104
108
|
};
|
|
105
109
|
exports.decodeStorageDiff = decodeStorageDiff;
|
|
@@ -28,12 +28,14 @@ function objectToStorageItems(meta, storage) {
|
|
|
28
28
|
throw Error(`Cannot find storage ${storageName} in pallet ${sectionName}`);
|
|
29
29
|
if (storageEntry.meta.type.isPlain) {
|
|
30
30
|
const key = new types_1.StorageKey(meta.registry, [storageEntry]);
|
|
31
|
-
|
|
31
|
+
const type = storageEntry.meta.modifier.isOptional ? `Option<${key.outputType}>` : key.outputType;
|
|
32
|
+
storageItems.push([key.toHex(), storage ? meta.registry.createType(type, storage).toHex() : null]);
|
|
32
33
|
}
|
|
33
34
|
else {
|
|
34
35
|
for (const [keys, value] of storage) {
|
|
35
36
|
const key = new types_1.StorageKey(meta.registry, [storageEntry, keys]);
|
|
36
|
-
|
|
37
|
+
const type = storageEntry.meta.modifier.isOptional ? `Option<${key.outputType}>` : key.outputType;
|
|
38
|
+
storageItems.push([key.toHex(), value ? meta.registry.createType(type, value).toHex() : null]);
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@acala-network/chopsticks",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.12",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"author": "Bryan Chen <xlchen1291@gmail.com>",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"dev:moonbeam": "ts-node-dev --transpile-only --inspect --notify=false src/cli.ts -- dev --config=configs/moonbeam.yml"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@acala-network/chopsticks-executor": "0.3.
|
|
32
|
+
"@acala-network/chopsticks-executor": "0.3.12",
|
|
33
33
|
"@polkadot/api": "^9.12.1",
|
|
34
34
|
"@polkadot/rpc-provider": "^9.12.1",
|
|
35
35
|
"@polkadot/types": "^9.12.1",
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
"@types/node": "^18.11.18",
|
|
58
58
|
"@types/ws": "^8.5.4",
|
|
59
59
|
"@types/yargs": "^17.0.20",
|
|
60
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
61
|
-
"@typescript-eslint/parser": "^5.
|
|
60
|
+
"@typescript-eslint/eslint-plugin": "^5.50.0",
|
|
61
|
+
"@typescript-eslint/parser": "^5.50.0",
|
|
62
62
|
"eslint": "^8.32.0",
|
|
63
63
|
"eslint-config-prettier": "^8.6.0",
|
|
64
64
|
"eslint-plugin-import": "^2.27.5",
|
package/template/diff.html
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
div#app {
|
|
15
15
|
margin: 0 !important;
|
|
16
|
-
padding:
|
|
16
|
+
padding: 0 !important;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
.diff {
|
|
@@ -24,6 +24,36 @@
|
|
|
24
24
|
line-height: 150%;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
.diff > button {
|
|
28
|
+
position: absolute;
|
|
29
|
+
display: none;
|
|
30
|
+
left: 50%;
|
|
31
|
+
top: 50%;
|
|
32
|
+
translate: -50% -50%;
|
|
33
|
+
background: #fff;
|
|
34
|
+
border: none;
|
|
35
|
+
border-radius: 50%;
|
|
36
|
+
padding: 10px;
|
|
37
|
+
cursor: pointer;
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
opacity: 80%;
|
|
40
|
+
width: 40px;
|
|
41
|
+
height: 40px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.diff > button > img {
|
|
45
|
+
width: 100%;
|
|
46
|
+
height: 100%;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.diff > button:hover {
|
|
50
|
+
opacity: 100%;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.diff:hover > button {
|
|
54
|
+
display: block;
|
|
55
|
+
}
|
|
56
|
+
|
|
27
57
|
.diffWrap {
|
|
28
58
|
position: relative;
|
|
29
59
|
z-index: 1;
|
|
@@ -37,30 +67,36 @@
|
|
|
37
67
|
|
|
38
68
|
.diffAdd {
|
|
39
69
|
color: darkseagreen;
|
|
70
|
+
display: inline-flex;
|
|
40
71
|
}
|
|
41
72
|
|
|
42
73
|
.diffRemove {
|
|
43
74
|
text-decoration: line-through;
|
|
44
75
|
text-decoration-thickness: 1px;
|
|
45
76
|
color: red;
|
|
77
|
+
display: inline-flex;
|
|
46
78
|
}
|
|
47
79
|
|
|
48
80
|
.diffUpdateFrom {
|
|
49
81
|
text-decoration: line-through;
|
|
50
82
|
text-decoration-thickness: 1px;
|
|
51
83
|
color: red;
|
|
84
|
+
display: inline-flex;
|
|
52
85
|
}
|
|
53
86
|
|
|
54
87
|
.diffUpdateTo {
|
|
55
88
|
color: darkseagreen;
|
|
89
|
+
display: inline-flex;
|
|
56
90
|
}
|
|
57
91
|
|
|
58
92
|
.diffUpdateArrow {
|
|
59
93
|
color: #ccc;
|
|
60
94
|
}
|
|
95
|
+
|
|
61
96
|
.unchanged {
|
|
62
97
|
color: #666;
|
|
63
98
|
}
|
|
99
|
+
|
|
64
100
|
.delta {
|
|
65
101
|
color: #ccc;
|
|
66
102
|
font-size: 12px;
|
|
@@ -94,76 +130,82 @@
|
|
|
94
130
|
return { className: className.join(' ') }
|
|
95
131
|
}
|
|
96
132
|
|
|
97
|
-
function valueRenderer(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
133
|
+
function valueRenderer(viewPartial) {
|
|
134
|
+
return function (raw, value, ...keys) {
|
|
135
|
+
const modifyPath = keys.reverse().join('.')
|
|
136
|
+
const removePath = keys.map(x => Number.isInteger(parseInt(x)) ? '_' + x : x).join('.')
|
|
137
|
+
const isDelta = _.has(delta, modifyPath) || _.has(delta, removePath)
|
|
101
138
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
139
|
+
function renderSpan(name, body, raw) {
|
|
140
|
+
return (
|
|
141
|
+
<span key={name} {...styling(['diff', name])}>
|
|
142
|
+
{body}
|
|
143
|
+
{_.isObjectLike(raw) ? <button onClick={() => viewPartial({ [modifyPath]: raw })}><img src='data:image/svg+xml;base64,PCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KDTwhLS0gVXBsb2FkZWQgdG86IFNWRyBSZXBvLCB3d3cuc3ZncmVwby5jb20sIFRyYW5zZm9ybWVkIGJ5OiBTVkcgUmVwbyBNaXhlciBUb29scyAtLT4KPHN2ZyBmaWxsPSIjMDAwMDAwIiBoZWlnaHQ9IjY0cHgiIHdpZHRoPSI2NHB4IiB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMjQyLjEzMyAyNDIuMTMzIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHJva2U9IiMwMDAwMDAiPgoNPGcgaWQ9IlNWR1JlcG9fYmdDYXJyaWVyIiBzdHJva2Utd2lkdGg9IjAiLz4KDTxnIGlkPSJTVkdSZXBvX3RyYWNlckNhcnJpZXIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgoNPGcgaWQ9IlNWR1JlcG9faWNvbkNhcnJpZXIiPiA8ZyBpZD0iWE1MSURfMjVfIj4gPHBhdGggaWQ9IlhNTElEXzI2XyIgZD0iTTg5LjI0NywxMzEuNjczbC00Ny43MzIsNDcuNzNsLTE1LjkwOS0xNS45MWMtNC4yOS00LjI5MS0xMC43NDItNS41NzItMTYuMzQ3LTMuMjUyIEMzLjY1NCwxNjIuNTYzLDAsMTY4LjAzMywwLDE3NC4xdjUzLjAzMmMwLDguMjg0LDYuNzE2LDE1LDE1LDE1bDUzLjAzMywwLjAwMWMwLjAwNy0wLjAwMSwwLjAxMi0wLjAwMSwwLjAxOSwwIGM4LjI4NSwwLDE1LTYuNzE2LDE1LTE1YzAtNC4zNzctMS44NzUtOC4zMTYtNC44NjUtMTEuMDU5bC0xNS40NTgtMTUuNDU4bDQ3LjczLTQ3LjcyOWM1Ljg1OC01Ljg1OCw1Ljg1OC0xNS4zNTUsMC0yMS4yMTMgQzEwNC42MDMsMTI1LjgxNSw5NS4xMDQsMTI1LjgxNiw4OS4yNDcsMTMxLjY3M3oiLz4gPHBhdGggaWQ9IlhNTElEXzI4XyIgZD0iTTIyNy4xMzMsMEgxNzQuMWMtNi4wNjcsMC0xMS41MzYsMy42NTUtMTMuODU4LDkuMjZjLTIuMzIxLDUuNjA1LTEuMDM4LDEyLjA1NywzLjI1MiwxNi4zNDdsMTUuOTExLDE1LjkxMSBsLTQ3LjcyOSw0Ny43M2MtNS44NTgsNS44NTgtNS44NTgsMTUuMzU1LDAsMjEuMjEzYzIuOTI5LDIuOTI5LDYuNzY4LDQuMzkzLDEwLjYwNiw0LjM5M2MzLjgzOSwwLDcuNjc4LTEuNDY0LDEwLjYwNi00LjM5NCBsNDcuNzMtNDcuNzNsMTUuOTA5LDE1LjkxYzIuODY5LDIuODcsNi43MDYsNC4zOTQsMTAuNjA5LDQuMzk0YzEuOTMzLDAsMy44ODItMC4zNzMsNS43MzctMS4xNDIgYzUuNjA1LTIuMzIyLDkuMjYtNy43OTIsOS4yNi0xMy44NThWMTVDMjQyLjEzMyw2LjcxNiwyMzUuNDE3LDAsMjI3LjEzMywweiIvPiA8L2c+IDwvZz4KDTwvc3ZnPg==' /></button> : null}
|
|
144
|
+
</span>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
109
147
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
148
|
+
function renderDelta(value) {
|
|
149
|
+
if (/^\d+(,\d+)*$/.test(value[0]) && /^\d+(,\d+)*$/.test(value[1])) {
|
|
150
|
+
const oldValue = parseInt(value[0].replace(/,/g, ''))
|
|
151
|
+
const newValue = parseInt(value[1].replace(/,/g, ''))
|
|
152
|
+
if (oldValue > 0 && newValue > 0) {
|
|
153
|
+
const delta = Number(newValue - oldValue)
|
|
154
|
+
return (<span className="delta" >{delta > 0 ? '+' : ''}{delta.toLocaleString()}</span>)
|
|
155
|
+
}
|
|
117
156
|
}
|
|
118
157
|
}
|
|
119
|
-
}
|
|
120
158
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
+
if (isDelta && Array.isArray(value)) {
|
|
160
|
+
switch (value.length) {
|
|
161
|
+
case 0:
|
|
162
|
+
return (
|
|
163
|
+
<span {...styling('diffWrap')}>
|
|
164
|
+
{renderSpan('diff', '[]')}
|
|
165
|
+
</span>
|
|
166
|
+
)
|
|
167
|
+
case 1:
|
|
168
|
+
return (
|
|
169
|
+
<span {...styling('diffWrap')}>
|
|
170
|
+
{renderSpan(
|
|
171
|
+
'diffAdd',
|
|
172
|
+
stringifyAndShrink(value[0]),
|
|
173
|
+
value[0]
|
|
174
|
+
)}
|
|
175
|
+
</span>
|
|
176
|
+
);
|
|
177
|
+
case 2:
|
|
178
|
+
return (
|
|
179
|
+
<span {...styling('diffWrap')}>
|
|
180
|
+
{renderSpan(
|
|
181
|
+
'diffUpdateFrom',
|
|
182
|
+
stringifyAndShrink(value[0]),
|
|
183
|
+
value[0]
|
|
184
|
+
)}
|
|
185
|
+
{renderSpan('diffUpdateArrow', ' => ')}
|
|
186
|
+
{renderSpan(
|
|
187
|
+
'diffUpdateTo',
|
|
188
|
+
stringifyAndShrink(value[1]),
|
|
189
|
+
value[1]
|
|
190
|
+
)}
|
|
191
|
+
{renderDelta(value)}
|
|
192
|
+
</span>
|
|
193
|
+
);
|
|
194
|
+
case 3:
|
|
195
|
+
return (
|
|
196
|
+
<span {...styling('diffWrap')}>
|
|
197
|
+
{renderSpan('diffRemove', stringifyAndShrink(value[0]), value[0])}
|
|
198
|
+
</span>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
159
201
|
}
|
|
160
|
-
}
|
|
161
202
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
203
|
+
return (
|
|
204
|
+
<span {...styling('diffWrap')}>
|
|
205
|
+
{renderSpan('unchanged', stringifyAndShrink(value), value)}
|
|
206
|
+
</span>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
167
209
|
};
|
|
168
210
|
|
|
169
211
|
function prepareDelta(value) {
|
|
@@ -208,30 +250,49 @@
|
|
|
208
250
|
class App extends React.Component {
|
|
209
251
|
constructor(props) {
|
|
210
252
|
super(props);
|
|
211
|
-
this.state = { showUnchanged: false };
|
|
253
|
+
this.state = { showUnchanged: false, partial: null };
|
|
212
254
|
}
|
|
213
255
|
|
|
214
256
|
toggle = (e) => {
|
|
215
257
|
this.setState(state => {
|
|
216
|
-
return { showUnchanged: !state.showUnchanged }
|
|
258
|
+
return { ...state, showUnchanged: !state.showUnchanged }
|
|
259
|
+
})
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
viewPartial = (value) => {
|
|
263
|
+
this.setState(state => {
|
|
264
|
+
return { ...state, partial: _.isEqual(state.partial, value) ? null : value }
|
|
217
265
|
})
|
|
218
266
|
}
|
|
219
267
|
|
|
220
268
|
render() {
|
|
221
269
|
return (
|
|
222
270
|
<div>
|
|
223
|
-
<
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
271
|
+
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
|
272
|
+
<div style={{ flex: 1, padding: '0 10px', overflow: 'hidden', overflowY: 'scroll', height: '100vh' }}>
|
|
273
|
+
<input type="checkbox" onChange={this.toggle} id="show_unchanged" />
|
|
274
|
+
<label for="show_unchanged" style={{ fontSize: '12px', color: 'white' }}>Show Unchanged</label>
|
|
275
|
+
<ReactJsonTree.JSONTree
|
|
276
|
+
theme={theme}
|
|
277
|
+
invertTheme={false}
|
|
278
|
+
data={this.state.showUnchanged ? _.merge(_.cloneDeep(left), delta) : delta}
|
|
279
|
+
valueRenderer={valueRenderer(this.viewPartial)}
|
|
280
|
+
postprocessValue={prepareDelta}
|
|
281
|
+
isCustomNode={Array.isArray}
|
|
282
|
+
shouldExpandNodeInitially={expandFirstLevel}
|
|
283
|
+
hideRoot
|
|
284
|
+
/>
|
|
285
|
+
</div>
|
|
286
|
+
{this.state.partial ? <div style={{ flex: 1, padding: '0 10px', overflow: 'hidden', overflowY: 'scroll', height: '100vh' }}>
|
|
287
|
+
<ReactJsonTree.JSONTree
|
|
288
|
+
theme={theme}
|
|
289
|
+
invertTheme={false}
|
|
290
|
+
data={this.state.partial}
|
|
291
|
+
shouldExpandNodeInitially={() => true}
|
|
292
|
+
hideRoot
|
|
293
|
+
/>
|
|
294
|
+
</div> : null}
|
|
295
|
+
</div>
|
|
235
296
|
</div>
|
|
236
297
|
);
|
|
237
298
|
}
|