meridian-core 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -0
- package/dist/index.js +1217 -4
- package/package.json +10 -11
- package/dist/cli.d.ts +0 -14
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -46
- package/dist/cli.js.map +0 -1
- package/dist/commands/analyze.d.ts +0 -8
- package/dist/commands/analyze.d.ts.map +0 -1
- package/dist/commands/analyze.js +0 -78
- package/dist/commands/analyze.js.map +0 -1
- package/dist/commands/field.d.ts +0 -8
- package/dist/commands/field.d.ts.map +0 -1
- package/dist/commands/field.js +0 -46
- package/dist/commands/field.js.map +0 -1
- package/dist/commands/gravity.d.ts +0 -8
- package/dist/commands/gravity.d.ts.map +0 -1
- package/dist/commands/gravity.js +0 -47
- package/dist/commands/gravity.js.map +0 -1
- package/dist/commands/trace.d.ts +0 -8
- package/dist/commands/trace.d.ts.map +0 -1
- package/dist/commands/trace.js +0 -37
- package/dist/commands/trace.js.map +0 -1
- package/dist/commands/version.d.ts +0 -8
- package/dist/commands/version.d.ts.map +0 -1
- package/dist/commands/version.js +0 -21
- package/dist/commands/version.js.map +0 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/errors.d.ts +0 -21
- package/dist/lib/errors.d.ts.map +0 -1
- package/dist/lib/errors.js +0 -37
- package/dist/lib/errors.js.map +0 -1
- package/dist/lib/input.d.ts +0 -11
- package/dist/lib/input.d.ts.map +0 -1
- package/dist/lib/input.js +0 -38
- package/dist/lib/input.js.map +0 -1
- package/dist/lib/manifest.d.ts +0 -10
- package/dist/lib/manifest.d.ts.map +0 -1
- package/dist/lib/manifest.js +0 -35
- package/dist/lib/manifest.js.map +0 -1
- package/dist/lib/options.d.ts +0 -26
- package/dist/lib/options.d.ts.map +0 -1
- package/dist/lib/options.js +0 -44
- package/dist/lib/options.js.map +0 -1
- package/dist/lib/output.d.ts +0 -32
- package/dist/lib/output.d.ts.map +0 -1
- package/dist/lib/output.js +0 -156
- package/dist/lib/output.js.map +0 -1
- package/src/cli.ts +0 -54
- package/src/commands/analyze.ts +0 -103
- package/src/commands/field.ts +0 -66
- package/src/commands/gravity.ts +0 -70
- package/src/commands/trace.ts +0 -51
- package/src/commands/version.ts +0 -21
- package/src/index.ts +0 -7
- package/src/lib/errors.ts +0 -42
- package/src/lib/input.test.ts +0 -38
- package/src/lib/input.ts +0 -43
- package/src/lib/manifest.test.ts +0 -60
- package/src/lib/manifest.ts +0 -41
- package/src/lib/options.test.ts +0 -27
- package/src/lib/options.ts +0 -47
- package/src/lib/output.ts +0 -180
- package/tsconfig.json +0 -10
- package/vitest.config.ts +0 -8
package/src/lib/errors.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import pc from 'picocolors';
|
|
2
|
-
import type { MeridianError } from '@meridian/core';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Check if a value is a structured MeridianError.
|
|
6
|
-
*
|
|
7
|
-
* @param value - Value to check
|
|
8
|
-
* @returns True if the value matches the MeridianError shape
|
|
9
|
-
*/
|
|
10
|
-
export function isMeridianError(value: unknown): value is MeridianError {
|
|
11
|
-
return (
|
|
12
|
-
typeof value === 'object' &&
|
|
13
|
-
value !== null &&
|
|
14
|
-
'layer' in value &&
|
|
15
|
-
'code' in value &&
|
|
16
|
-
'hint' in value
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Print a MeridianError to stderr and exit the process with code 1.
|
|
22
|
-
*
|
|
23
|
-
* @param error - Structured MeridianError to report
|
|
24
|
-
*/
|
|
25
|
-
export function failWithMeridianError(error: MeridianError): never {
|
|
26
|
-
console.error(pc.red(pc.bold(`✖ [${error.layer}] ${error.code}`)));
|
|
27
|
-
console.error(pc.red(error.error));
|
|
28
|
-
console.error(pc.dim(`hint: ${error.hint}`));
|
|
29
|
-
process.exit(1);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Print a generic error to stderr and exit the process with code 1.
|
|
34
|
-
*
|
|
35
|
-
* @param err - Error or unknown thrown value
|
|
36
|
-
*/
|
|
37
|
-
export function failWithError(err: unknown): never {
|
|
38
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
39
|
-
console.error(pc.red(pc.bold('✖ Error')));
|
|
40
|
-
console.error(pc.red(message));
|
|
41
|
-
process.exit(1);
|
|
42
|
-
}
|
package/src/lib/input.test.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { writeFile, mkdtemp, rm } from 'node:fs/promises';
|
|
3
|
-
import { tmpdir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
import { resolveTxInput } from './input.js';
|
|
6
|
-
|
|
7
|
-
describe('resolveTxInput', () => {
|
|
8
|
-
it('returns the trimmed positional argument when provided', async () => {
|
|
9
|
-
const result = await resolveTxInput(' AAAA-fake-xdr ', undefined);
|
|
10
|
-
expect(result).toBe('AAAA-fake-xdr');
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('reads and trims XDR from a file when --file is provided', async () => {
|
|
14
|
-
const dir = await mkdtemp(join(tmpdir(), 'meridian-cli-'));
|
|
15
|
-
const filePath = join(dir, 'tx.xdr');
|
|
16
|
-
await writeFile(filePath, ' file-based-xdr\n');
|
|
17
|
-
|
|
18
|
-
try {
|
|
19
|
-
const result = await resolveTxInput(undefined, filePath);
|
|
20
|
-
expect(result).toBe('file-based-xdr');
|
|
21
|
-
} finally {
|
|
22
|
-
await rm(dir, { recursive: true, force: true });
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('prefers --file over the positional argument', async () => {
|
|
27
|
-
const dir = await mkdtemp(join(tmpdir(), 'meridian-cli-'));
|
|
28
|
-
const filePath = join(dir, 'tx.xdr');
|
|
29
|
-
await writeFile(filePath, 'from-file');
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
const result = await resolveTxInput('from-arg', filePath);
|
|
33
|
-
expect(result).toBe('from-file');
|
|
34
|
-
} finally {
|
|
35
|
-
await rm(dir, { recursive: true, force: true });
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
});
|
package/src/lib/input.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Read all data from stdin as a UTF-8 string.
|
|
5
|
-
*
|
|
6
|
-
* @returns Trimmed stdin contents
|
|
7
|
-
*/
|
|
8
|
-
async function readStdin(): Promise<string> {
|
|
9
|
-
const chunks: Buffer[] = [];
|
|
10
|
-
for await (const chunk of process.stdin) {
|
|
11
|
-
chunks.push(Buffer.from(chunk));
|
|
12
|
-
}
|
|
13
|
-
return Buffer.concat(chunks).toString('utf-8').trim();
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Resolve the transaction XDR from a positional argument, a file path, or stdin.
|
|
18
|
-
* Precedence: --file > positional arg > stdin.
|
|
19
|
-
*
|
|
20
|
-
* @param txArg - Positional tx argument, if provided
|
|
21
|
-
* @param filePath - Path to a file containing the base64 XDR, if provided
|
|
22
|
-
* @returns Trimmed base64-encoded transaction XDR
|
|
23
|
-
* @throws If no XDR could be resolved from any source
|
|
24
|
-
*/
|
|
25
|
-
export async function resolveTxInput(txArg?: string, filePath?: string): Promise<string> {
|
|
26
|
-
if (filePath) {
|
|
27
|
-
const contents = await readFile(filePath, 'utf-8');
|
|
28
|
-
return contents.trim();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (txArg && txArg.trim().length > 0) {
|
|
32
|
-
return txArg.trim();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (!process.stdin.isTTY) {
|
|
36
|
-
const fromStdin = await readStdin();
|
|
37
|
-
if (fromStdin.length > 0) return fromStdin;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
throw new Error(
|
|
41
|
-
'No transaction XDR provided. Pass it as an argument, via --file <path>, or pipe it over stdin.',
|
|
42
|
-
);
|
|
43
|
-
}
|
package/src/lib/manifest.test.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { writeFile, mkdtemp, rm } from 'node:fs/promises';
|
|
3
|
-
import { tmpdir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
import { loadManifest } from './manifest.js';
|
|
6
|
-
|
|
7
|
-
describe('loadManifest', () => {
|
|
8
|
-
it('returns undefined when no path is given', async () => {
|
|
9
|
-
const result = await loadManifest(undefined);
|
|
10
|
-
expect(result).toBeUndefined();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('parses a valid ecosystem manifest file', async () => {
|
|
14
|
-
const dir = await mkdtemp(join(tmpdir(), 'meridian-cli-'));
|
|
15
|
-
const filePath = join(dir, 'manifest.json');
|
|
16
|
-
const manifest = {
|
|
17
|
-
name: 'test-ecosystem',
|
|
18
|
-
version: '1.0.0',
|
|
19
|
-
contracts: [{ name: 'foo', address: 'CFOO', network: 'testnet' }],
|
|
20
|
-
};
|
|
21
|
-
await writeFile(filePath, JSON.stringify(manifest));
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
const result = await loadManifest(filePath);
|
|
25
|
-
expect(result).toEqual(manifest);
|
|
26
|
-
} finally {
|
|
27
|
-
await rm(dir, { recursive: true, force: true });
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('throws for a missing file', async () => {
|
|
32
|
-
await expect(loadManifest('/nonexistent/manifest.json')).rejects.toThrow(
|
|
33
|
-
/Failed to read ecosystem manifest/,
|
|
34
|
-
);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('throws for invalid JSON', async () => {
|
|
38
|
-
const dir = await mkdtemp(join(tmpdir(), 'meridian-cli-'));
|
|
39
|
-
const filePath = join(dir, 'manifest.json');
|
|
40
|
-
await writeFile(filePath, '{ not valid json');
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
await expect(loadManifest(filePath)).rejects.toThrow(/not valid JSON/);
|
|
44
|
-
} finally {
|
|
45
|
-
await rm(dir, { recursive: true, force: true });
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('throws when contracts field is missing', async () => {
|
|
50
|
-
const dir = await mkdtemp(join(tmpdir(), 'meridian-cli-'));
|
|
51
|
-
const filePath = join(dir, 'manifest.json');
|
|
52
|
-
await writeFile(filePath, JSON.stringify({ name: 'x', version: '1.0.0' }));
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
await expect(loadManifest(filePath)).rejects.toThrow(/must be an object with a "contracts"/);
|
|
56
|
-
} finally {
|
|
57
|
-
await rm(dir, { recursive: true, force: true });
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
});
|
package/src/lib/manifest.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import type { EcosystemManifest } from '@meridian/core';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Load and parse an ecosystem manifest JSON file.
|
|
6
|
-
*
|
|
7
|
-
* @param filePath - Path to the manifest JSON file
|
|
8
|
-
* @returns Parsed EcosystemManifest, or undefined if no path was given
|
|
9
|
-
* @throws If the file cannot be read or does not contain valid JSON
|
|
10
|
-
*/
|
|
11
|
-
export async function loadManifest(filePath?: string): Promise<EcosystemManifest | undefined> {
|
|
12
|
-
if (!filePath) return undefined;
|
|
13
|
-
|
|
14
|
-
let raw: string;
|
|
15
|
-
try {
|
|
16
|
-
raw = await readFile(filePath, 'utf-8');
|
|
17
|
-
} catch (err) {
|
|
18
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
19
|
-
throw new Error(`Failed to read ecosystem manifest at ${filePath}: ${message}`);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
let parsed: unknown;
|
|
23
|
-
try {
|
|
24
|
-
parsed = JSON.parse(raw);
|
|
25
|
-
} catch (err) {
|
|
26
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
27
|
-
throw new Error(`Ecosystem manifest at ${filePath} is not valid JSON: ${message}`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
typeof parsed !== 'object' ||
|
|
32
|
-
parsed === null ||
|
|
33
|
-
!Array.isArray((parsed as Record<string, unknown>).contracts)
|
|
34
|
-
) {
|
|
35
|
-
throw new Error(
|
|
36
|
-
`Ecosystem manifest at ${filePath} must be an object with a "contracts" array.`,
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return parsed as EcosystemManifest;
|
|
41
|
-
}
|
package/src/lib/options.test.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { parseNetwork, parseThreshold } from './options.js';
|
|
3
|
-
|
|
4
|
-
describe('parseNetwork', () => {
|
|
5
|
-
it('accepts mainnet and testnet', () => {
|
|
6
|
-
expect(parseNetwork('mainnet')).toBe('mainnet');
|
|
7
|
-
expect(parseNetwork('testnet')).toBe('testnet');
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it('rejects invalid networks', () => {
|
|
11
|
-
expect(() => parseNetwork('devnet')).toThrow();
|
|
12
|
-
});
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
describe('parseThreshold', () => {
|
|
16
|
-
it('accepts numbers within 0 and 1', () => {
|
|
17
|
-
expect(parseThreshold('0')).toBe(0);
|
|
18
|
-
expect(parseThreshold('0.75')).toBe(0.75);
|
|
19
|
-
expect(parseThreshold('1')).toBe(1);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('rejects out-of-range and non-numeric values', () => {
|
|
23
|
-
expect(() => parseThreshold('1.5')).toThrow();
|
|
24
|
-
expect(() => parseThreshold('-0.1')).toThrow();
|
|
25
|
-
expect(() => parseThreshold('abc')).toThrow();
|
|
26
|
-
});
|
|
27
|
-
});
|
package/src/lib/options.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { Command, InvalidArgumentError } from 'commander';
|
|
2
|
-
import type { Network } from '@meridian/core';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Parse and validate a --network option value.
|
|
6
|
-
*
|
|
7
|
-
* @param value - Raw CLI argument
|
|
8
|
-
* @returns Validated Network
|
|
9
|
-
* @throws InvalidArgumentError if the value is not "mainnet" or "testnet"
|
|
10
|
-
*/
|
|
11
|
-
export function parseNetwork(value: string): Network {
|
|
12
|
-
if (value !== 'mainnet' && value !== 'testnet') {
|
|
13
|
-
throw new InvalidArgumentError('Network must be "mainnet" or "testnet".');
|
|
14
|
-
}
|
|
15
|
-
return value;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Parse and validate a --confidence-threshold option value.
|
|
20
|
-
*
|
|
21
|
-
* @param value - Raw CLI argument
|
|
22
|
-
* @returns Parsed float between 0 and 1
|
|
23
|
-
* @throws InvalidArgumentError if the value is not a number in range
|
|
24
|
-
*/
|
|
25
|
-
export function parseThreshold(value: string): number {
|
|
26
|
-
const parsed = Number.parseFloat(value);
|
|
27
|
-
if (Number.isNaN(parsed) || parsed < 0 || parsed > 1) {
|
|
28
|
-
throw new InvalidArgumentError('Confidence threshold must be a number between 0 and 1.');
|
|
29
|
-
}
|
|
30
|
-
return parsed;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Attach the options shared by every layer command (network, RPC, input, output).
|
|
35
|
-
*
|
|
36
|
-
* @param command - Commander command to extend
|
|
37
|
-
* @returns The same command, for chaining
|
|
38
|
-
*/
|
|
39
|
-
export function withCommonOptions(command: Command): Command {
|
|
40
|
-
return command
|
|
41
|
-
.argument('[tx]', 'Base64-encoded transaction XDR')
|
|
42
|
-
.option('-n, --network <network>', 'Stellar network (mainnet | testnet)', parseNetwork, 'testnet')
|
|
43
|
-
.option('--rpc-url <url>', 'Override the Soroban RPC endpoint (else read from env)')
|
|
44
|
-
.option('-f, --file <path>', 'Read the transaction XDR from a file instead of an argument')
|
|
45
|
-
.option('-e, --ecosystem <path>', 'Path to an ecosystem manifest JSON file')
|
|
46
|
-
.option('--json', 'Print raw JSON instead of a formatted report');
|
|
47
|
-
}
|
package/src/lib/output.ts
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import pc from 'picocolors';
|
|
2
|
-
import type {
|
|
3
|
-
AnalyzeResponse,
|
|
4
|
-
FieldResult,
|
|
5
|
-
GravityResult,
|
|
6
|
-
TraceResult,
|
|
7
|
-
Verdict,
|
|
8
|
-
} from '@meridian/core';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Print any value as pretty-printed JSON.
|
|
12
|
-
*
|
|
13
|
-
* @param value - Value to serialize
|
|
14
|
-
*/
|
|
15
|
-
export function printJson(value: unknown): void {
|
|
16
|
-
console.log(JSON.stringify(value, null, 2));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Colorize a verdict badge.
|
|
21
|
-
*
|
|
22
|
-
* @param verdict - MERIDIAN verdict
|
|
23
|
-
* @returns Colorized verdict string
|
|
24
|
-
*/
|
|
25
|
-
function verdictBadge(verdict: Verdict): string {
|
|
26
|
-
switch (verdict) {
|
|
27
|
-
case 'CLEAR':
|
|
28
|
-
return pc.bgGreen(pc.black(' CLEAR '));
|
|
29
|
-
case 'WARN':
|
|
30
|
-
return pc.bgYellow(pc.black(' WARN '));
|
|
31
|
-
case 'ABORT':
|
|
32
|
-
return pc.bgRed(pc.white(pc.bold(' ABORT ')));
|
|
33
|
-
default:
|
|
34
|
-
return verdict;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Print a section header.
|
|
40
|
-
*
|
|
41
|
-
* @param title - Section title
|
|
42
|
-
*/
|
|
43
|
-
function section(title: string): void {
|
|
44
|
-
console.log('');
|
|
45
|
-
console.log(pc.bold(pc.cyan(`── ${title} `.padEnd(48, '─'))));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Print a labeled key/value line.
|
|
50
|
-
*
|
|
51
|
-
* @param label - Field label
|
|
52
|
-
* @param value - Field value
|
|
53
|
-
*/
|
|
54
|
-
function field(label: string, value: unknown): void {
|
|
55
|
-
console.log(` ${pc.dim(label + ':')} ${value}`);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Print a TraceResult in human-readable form.
|
|
60
|
-
*
|
|
61
|
-
* @param trace - TRACE layer result
|
|
62
|
-
*/
|
|
63
|
-
export function printTrace(trace: TraceResult): void {
|
|
64
|
-
section('TRACE');
|
|
65
|
-
field('success', trace.success ? pc.green('true') : pc.red('false'));
|
|
66
|
-
if (trace.staleness_warning) {
|
|
67
|
-
field('staleness_warning', pc.yellow('true'));
|
|
68
|
-
}
|
|
69
|
-
if (trace.failure_point) {
|
|
70
|
-
const fp = trace.failure_point;
|
|
71
|
-
console.log(` ${pc.red('failure_point')}:`);
|
|
72
|
-
field(' step_index', fp.step_index);
|
|
73
|
-
if (fp.contract_id) field(' contract_id', fp.contract_id);
|
|
74
|
-
if (fp.function_name) field(' function_name', fp.function_name);
|
|
75
|
-
field(' error_code', fp.error_code);
|
|
76
|
-
field(' root_cause', fp.root_cause);
|
|
77
|
-
}
|
|
78
|
-
field('execution_path', `${trace.execution_path.length} step(s)`);
|
|
79
|
-
field('auth_entries', `${trace.auth_entries.length} entrie(s)`);
|
|
80
|
-
field(
|
|
81
|
-
'fee_estimate',
|
|
82
|
-
`total=${trace.fee_estimate.total_fee} base=${trace.fee_estimate.classic_base_fee} min_resource=${trace.fee_estimate.min_resource_fee}`,
|
|
83
|
-
);
|
|
84
|
-
field(
|
|
85
|
-
'resource_usage',
|
|
86
|
-
`cpu=${trace.resource_usage.cpu_instructions} mem=${trace.resource_usage.memory_bytes}b read=${trace.resource_usage.read_bytes}b write=${trace.resource_usage.write_bytes}b`,
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Print a FieldResult in human-readable form.
|
|
92
|
-
*
|
|
93
|
-
* @param result - FIELD layer result
|
|
94
|
-
*/
|
|
95
|
-
export function printField(result: FieldResult): void {
|
|
96
|
-
section('FIELD');
|
|
97
|
-
field('contracts_mapped', result.contracts_mapped);
|
|
98
|
-
field('manifest_coverage', `${Math.round(result.manifest_coverage * 100)}%`);
|
|
99
|
-
if (result.ttl_warnings.length > 0) {
|
|
100
|
-
console.log(` ${pc.yellow('ttl_warnings')}:`);
|
|
101
|
-
for (const warning of result.ttl_warnings) {
|
|
102
|
-
console.log(
|
|
103
|
-
` - ${warning.contract_id} (${warning.ledger_key}) ttl_remaining=${warning.ttl_remaining} [${warning.severity}]`,
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
if (result.dependency_graph.length > 0) {
|
|
108
|
-
console.log(` ${pc.dim('dependency_graph')}:`);
|
|
109
|
-
for (const node of result.dependency_graph) {
|
|
110
|
-
const label = node.name ? `${node.name} (${node.address})` : node.address;
|
|
111
|
-
const deps = node.dependencies.length > 0 ? ` → ${node.dependencies.join(', ')}` : '';
|
|
112
|
-
console.log(` - ${label}${deps}`);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Print a GravityResult in human-readable form.
|
|
119
|
-
*
|
|
120
|
-
* @param result - GRAVITY layer result
|
|
121
|
-
*/
|
|
122
|
-
export function printGravity(result: GravityResult): void {
|
|
123
|
-
section('GRAVITY');
|
|
124
|
-
field('blast_radius', result.blast_radius);
|
|
125
|
-
field('total_affected_users', result.total_affected_users);
|
|
126
|
-
field('recovery', result.recovery);
|
|
127
|
-
if (result.critical.length > 0) field('critical', pc.red(result.critical.join(', ')));
|
|
128
|
-
if (result.warning.length > 0) field('warning', pc.yellow(result.warning.join(', ')));
|
|
129
|
-
if (result.monitor.length > 0) field('monitor', pc.blue(result.monitor.join(', ')));
|
|
130
|
-
if (result.safe.length > 0) field('safe', pc.green(result.safe.join(', ')));
|
|
131
|
-
|
|
132
|
-
if (result.affected_contracts.length > 0) {
|
|
133
|
-
console.log(` ${pc.dim('affected_contracts')}:`);
|
|
134
|
-
for (const contract of result.affected_contracts) {
|
|
135
|
-
const label = contract.name ? `${contract.name} (${contract.address})` : contract.address;
|
|
136
|
-
console.log(` - [${contract.impact}] ${label} — ${contract.reason}`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Print a full AnalyzeResponse in human-readable form.
|
|
143
|
-
*
|
|
144
|
-
* @param response - Full analysis response including brief
|
|
145
|
-
*/
|
|
146
|
-
export function printAnalysis(response: AnalyzeResponse): void {
|
|
147
|
-
console.log('');
|
|
148
|
-
console.log(`${pc.bold('MERIDIAN')} v${response.version} ${verdictBadge(response.verdict)} confidence=${response.confidence}`);
|
|
149
|
-
|
|
150
|
-
printTrace(response.trace);
|
|
151
|
-
printField(response.field);
|
|
152
|
-
printGravity(response.gravity);
|
|
153
|
-
|
|
154
|
-
if (response.fix_sequence && response.fix_sequence.length > 0) {
|
|
155
|
-
section('FIX SEQUENCE');
|
|
156
|
-
for (const step of response.fix_sequence) {
|
|
157
|
-
console.log(
|
|
158
|
-
` ${pc.bold(String(step.order) + '.')} ${step.operation} — ${step.description} ${pc.dim(`(~${step.estimated_cost_stroops} stroops, ~${step.estimated_time_minutes}min)`)}`,
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (response.warnings && response.warnings.length > 0) {
|
|
164
|
-
section('WARNINGS');
|
|
165
|
-
for (const warning of response.warnings) {
|
|
166
|
-
console.log(` ${pc.yellow('⚠')} ${warning}`);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
section('BRIEF');
|
|
171
|
-
console.log(response.brief);
|
|
172
|
-
|
|
173
|
-
section('META');
|
|
174
|
-
field('analyzed_at', response.meta.analyzed_at);
|
|
175
|
-
field('network', response.meta.network);
|
|
176
|
-
field('ledger_sequence', response.meta.ledger_sequence);
|
|
177
|
-
field('simulation_stale', response.meta.simulation_stale);
|
|
178
|
-
field('processing_ms', response.meta.processing_ms);
|
|
179
|
-
console.log('');
|
|
180
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../tsconfig.base.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"outDir": "./dist",
|
|
5
|
-
"rootDir": "./src"
|
|
6
|
-
},
|
|
7
|
-
"include": ["src/**/*"],
|
|
8
|
-
"exclude": ["node_modules", "dist", "**/*.test.ts"],
|
|
9
|
-
"references": [{ "path": "../core" }, { "path": "../ai" }]
|
|
10
|
-
}
|