movehat 0.2.2 → 0.2.4
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/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/compile.d.ts.map +1 -1
- package/dist/commands/compile.js +19 -10
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/test.js +12 -19
- package/dist/commands/test.js.map +1 -1
- package/dist/core/Publisher.d.ts.map +1 -1
- package/dist/core/Publisher.js +20 -14
- package/dist/core/Publisher.js.map +1 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +8 -5
- package/dist/core/config.js.map +1 -1
- package/dist/core/deployments.d.ts.map +1 -1
- package/dist/core/deployments.js +4 -2
- package/dist/core/deployments.js.map +1 -1
- package/dist/fork/manager.d.ts +1 -1
- package/dist/fork/manager.js +11 -11
- package/dist/fork/manager.js.map +1 -1
- package/dist/fork/server.d.ts.map +1 -1
- package/dist/fork/server.js +21 -15
- package/dist/fork/server.js.map +1 -1
- package/dist/fork/test.d.ts.map +1 -1
- package/dist/fork/test.js +3 -2
- package/dist/fork/test.js.map +1 -1
- package/dist/harness/codeObject.js +11 -8
- package/dist/harness/codeObject.js.map +1 -1
- package/dist/harness/script.d.ts.map +1 -1
- package/dist/harness/script.js +9 -6
- package/dist/harness/script.js.map +1 -1
- package/dist/helpers/setupLocalTesting.js +5 -5
- package/dist/helpers/setupLocalTesting.js.map +1 -1
- package/dist/node/LocalNodeManager.d.ts +1 -1
- package/dist/node/LocalNodeManager.d.ts.map +1 -1
- package/dist/node/LocalNodeManager.js +61 -23
- package/dist/node/LocalNodeManager.js.map +1 -1
- package/dist/node/__tests__/LocalNodeManager.test.js +110 -11
- package/dist/node/__tests__/LocalNodeManager.test.js.map +1 -1
- package/dist/ui/__tests__/logger.test.d.ts +2 -0
- package/dist/ui/__tests__/logger.test.d.ts.map +1 -0
- package/dist/ui/__tests__/logger.test.js +75 -0
- package/dist/ui/__tests__/logger.test.js.map +1 -0
- package/dist/ui/formatters.d.ts +0 -16
- package/dist/ui/formatters.d.ts.map +1 -1
- package/dist/ui/formatters.js +1 -1
- package/dist/ui/formatters.js.map +1 -1
- package/dist/ui/logger.d.ts +41 -0
- package/dist/ui/logger.d.ts.map +1 -1
- package/dist/ui/logger.js +49 -0
- package/dist/ui/logger.js.map +1 -1
- package/dist/ui/spinner.d.ts +25 -0
- package/dist/ui/spinner.d.ts.map +1 -1
- package/dist/ui/spinner.js +44 -0
- package/dist/ui/spinner.js.map +1 -1
- package/package.json +1 -1
- package/src/cli.ts +4 -0
- package/src/commands/compile.ts +24 -15
- package/src/commands/test.ts +12 -19
- package/src/core/Publisher.ts +49 -34
- package/src/core/config.ts +9 -6
- package/src/core/deployments.ts +5 -4
- package/src/fork/manager.ts +11 -11
- package/src/fork/server.ts +21 -15
- package/src/fork/test.ts +3 -2
- package/src/harness/codeObject.ts +8 -5
- package/src/harness/script.ts +7 -4
- package/src/helpers/setupLocalTesting.ts +5 -5
- package/src/node/LocalNodeManager.ts +64 -25
- package/src/node/__tests__/LocalNodeManager.test.ts +140 -14
- package/src/types/config.ts +1 -1
- package/src/ui/__tests__/logger.test.ts +89 -0
- package/src/ui/formatters.ts +1 -1
- package/src/ui/logger.ts +62 -0
- package/src/ui/spinner.ts +47 -0
package/src/types/config.ts
CHANGED
|
@@ -77,7 +77,7 @@ export interface LocalTestOptions {
|
|
|
77
77
|
|
|
78
78
|
// Common options (both modes)
|
|
79
79
|
autoFund?: boolean; // Auto-fund accounts (default: true)
|
|
80
|
-
defaultBalance?: number; // Default balance in octas (default: 100_000_000 =
|
|
80
|
+
defaultBalance?: number; // Default balance in octas (default: 100_000_000 = 1 MOVE)
|
|
81
81
|
autoDeploy?: readonly string[]; // Modules to auto-deploy (accepts readonly arrays)
|
|
82
82
|
accountLabels?: readonly string[]; // Labels for pre-generated accounts (default: ['deployer', 'alice', 'bob'])
|
|
83
83
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { configureLogger, isVerbose, divider, phase } from "../logger.js";
|
|
4
|
+
|
|
5
|
+
describe("logger — verbosity", () => {
|
|
6
|
+
let originalEnv: string | undefined;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
originalEnv = process.env.MOVEHAT_VERBOSE;
|
|
10
|
+
// Start each test from a clean slate so prior runs cannot leak.
|
|
11
|
+
delete process.env.MOVEHAT_VERBOSE;
|
|
12
|
+
configureLogger({ verbosity: "normal" });
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
if (originalEnv === undefined) {
|
|
17
|
+
delete process.env.MOVEHAT_VERBOSE;
|
|
18
|
+
} else {
|
|
19
|
+
process.env.MOVEHAT_VERBOSE = originalEnv;
|
|
20
|
+
}
|
|
21
|
+
configureLogger({ verbosity: "normal" });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("defaults to non-verbose when MOVEHAT_VERBOSE is unset and config is normal", () => {
|
|
25
|
+
expect(isVerbose()).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("returns true when MOVEHAT_VERBOSE=1 (env-driven path)", () => {
|
|
29
|
+
process.env.MOVEHAT_VERBOSE = "1";
|
|
30
|
+
expect(isVerbose()).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("returns true when configureLogger({ verbosity: 'verbose' }) is set in-process", () => {
|
|
34
|
+
configureLogger({ verbosity: "verbose" });
|
|
35
|
+
expect(isVerbose()).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("env var wins even when in-process config is normal (allows shell-script callers)", () => {
|
|
39
|
+
configureLogger({ verbosity: "normal" });
|
|
40
|
+
process.env.MOVEHAT_VERBOSE = "1";
|
|
41
|
+
expect(isVerbose()).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("non-'1' MOVEHAT_VERBOSE values do not enable verbose mode", () => {
|
|
45
|
+
process.env.MOVEHAT_VERBOSE = "true";
|
|
46
|
+
expect(isVerbose()).toBe(false);
|
|
47
|
+
process.env.MOVEHAT_VERBOSE = "0";
|
|
48
|
+
expect(isVerbose()).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("logger.phase / logger.divider", () => {
|
|
53
|
+
let logSpy: ReturnType<typeof vi.spyOn>;
|
|
54
|
+
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined);
|
|
57
|
+
configureLogger({ silent: false });
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
afterEach(() => {
|
|
61
|
+
vi.restoreAllMocks();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("phase prints three lines: top rule, indented title, bottom rule", () => {
|
|
65
|
+
phase("Local Movement node");
|
|
66
|
+
expect(logSpy).toHaveBeenCalledTimes(3);
|
|
67
|
+
|
|
68
|
+
const calls = logSpy.mock.calls.map((c: unknown[]) => String(c[0]));
|
|
69
|
+
// The title line carries the supplied text after the two-space indent.
|
|
70
|
+
expect(calls[1]).toContain("Local Movement node");
|
|
71
|
+
// Top and bottom rules should be identical (same width, same color).
|
|
72
|
+
expect(calls[0]).toBe(calls[2]);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("divider prints a single muted rule line", () => {
|
|
76
|
+
divider();
|
|
77
|
+
expect(logSpy).toHaveBeenCalledTimes(1);
|
|
78
|
+
const line = String(logSpy.mock.calls[0]?.[0] ?? "");
|
|
79
|
+
// The rule character is `━` (BOX DRAWINGS HEAVY HORIZONTAL).
|
|
80
|
+
expect(line).toMatch(/━+/);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("phase and divider are silenced when configureLogger({ silent: true })", () => {
|
|
84
|
+
configureLogger({ silent: true });
|
|
85
|
+
phase("hidden phase");
|
|
86
|
+
divider();
|
|
87
|
+
expect(logSpy).not.toHaveBeenCalled();
|
|
88
|
+
});
|
|
89
|
+
});
|
package/src/ui/formatters.ts
CHANGED
|
@@ -176,7 +176,7 @@ export const indent = (text: string, spaces: number): string => {
|
|
|
176
176
|
* console.log(divider(40, '='));
|
|
177
177
|
* // ========================================
|
|
178
178
|
*/
|
|
179
|
-
|
|
179
|
+
const divider = (
|
|
180
180
|
width: number = 60,
|
|
181
181
|
char: string = symbols.line
|
|
182
182
|
): string => {
|
package/src/ui/logger.ts
CHANGED
|
@@ -6,6 +6,14 @@ import { coloredSymbol, symbols } from './symbols.js';
|
|
|
6
6
|
*/
|
|
7
7
|
export type LogLevel = 'info' | 'success' | 'error' | 'warning' | 'debug';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Verbosity level for subprocess output and gray-prefixed chatter.
|
|
11
|
+
* - `quiet` — reserved; currently behaves like `normal`
|
|
12
|
+
* - `normal` — default; system logs only, subprocess chatter hidden
|
|
13
|
+
* - `verbose` — surface subprocess stdout with a muted gray `›` prefix
|
|
14
|
+
*/
|
|
15
|
+
export type Verbosity = 'quiet' | 'normal' | 'verbose';
|
|
16
|
+
|
|
9
17
|
/**
|
|
10
18
|
* Logger configuration options
|
|
11
19
|
*/
|
|
@@ -16,6 +24,8 @@ export interface LoggerConfig {
|
|
|
16
24
|
level?: LogLevel;
|
|
17
25
|
/** Include timestamps in log messages */
|
|
18
26
|
timestamp?: boolean;
|
|
27
|
+
/** Verbosity level for subprocess output */
|
|
28
|
+
verbosity?: Verbosity;
|
|
19
29
|
}
|
|
20
30
|
|
|
21
31
|
/**
|
|
@@ -25,8 +35,18 @@ let config: LoggerConfig = {
|
|
|
25
35
|
silent: false,
|
|
26
36
|
level: 'info',
|
|
27
37
|
timestamp: false,
|
|
38
|
+
verbosity: process.env.MOVEHAT_VERBOSE === '1' ? 'verbose' : 'normal',
|
|
28
39
|
};
|
|
29
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Whether subprocess chatter should reach the user's terminal.
|
|
43
|
+
* Honors both the in-process config (set by the `-v` CLI flag's
|
|
44
|
+
* preAction hook) and the `MOVEHAT_VERBOSE=1` env var (which lets
|
|
45
|
+
* callers opt in before the CLI parses args, e.g. in shell scripts).
|
|
46
|
+
*/
|
|
47
|
+
export const isVerbose = (): boolean =>
|
|
48
|
+
config.verbosity === 'verbose' || process.env.MOVEHAT_VERBOSE === '1';
|
|
49
|
+
|
|
30
50
|
/**
|
|
31
51
|
* Configure logger globally
|
|
32
52
|
*
|
|
@@ -186,6 +206,45 @@ export const section = (title: string): void => {
|
|
|
186
206
|
console.log(`\n${colors.brandBright(title)}`);
|
|
187
207
|
};
|
|
188
208
|
|
|
209
|
+
/**
|
|
210
|
+
* Width of the `━` rule used by `phase` and `divider`. Matched to a
|
|
211
|
+
* comfortable terminal width that fits in a side-by-side dev layout.
|
|
212
|
+
*/
|
|
213
|
+
const PHASE_RULE_WIDTH = 52;
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Single muted horizontal rule. Use to close out a phase or to
|
|
217
|
+
* visually separate output sections.
|
|
218
|
+
*/
|
|
219
|
+
export const divider = (): void => {
|
|
220
|
+
if (config.silent) return;
|
|
221
|
+
console.log(colors.muted('━'.repeat(PHASE_RULE_WIDTH)));
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Phase header — renders a muted top rule, a bold brand-colored title
|
|
226
|
+
* indented two spaces, and a muted bottom rule. Use at top-level phase
|
|
227
|
+
* boundaries (local node start, deploy flow, test orchestrator
|
|
228
|
+
* sections) so the user can visually anchor where one phase ends and
|
|
229
|
+
* the next begins.
|
|
230
|
+
*
|
|
231
|
+
* @param title - Phase title (e.g. "Local Movement node")
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* logger.phase('Local Movement node');
|
|
235
|
+
* // Renders:
|
|
236
|
+
* // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
237
|
+
* // Local Movement node
|
|
238
|
+
* // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
239
|
+
*/
|
|
240
|
+
export const phase = (title: string): void => {
|
|
241
|
+
if (config.silent) return;
|
|
242
|
+
const rule = colors.muted('━'.repeat(PHASE_RULE_WIDTH));
|
|
243
|
+
console.log(rule);
|
|
244
|
+
console.log(` ${colors.brandBright(title)}`);
|
|
245
|
+
console.log(rule);
|
|
246
|
+
};
|
|
247
|
+
|
|
189
248
|
/**
|
|
190
249
|
* Key-value pair
|
|
191
250
|
* Use for displaying structured data
|
|
@@ -235,6 +294,7 @@ export const item = (text: string, indent: number = 0): void => {
|
|
|
235
294
|
*/
|
|
236
295
|
export const logger = {
|
|
237
296
|
configure: configureLogger,
|
|
297
|
+
isVerbose,
|
|
238
298
|
info,
|
|
239
299
|
success,
|
|
240
300
|
error,
|
|
@@ -243,6 +303,8 @@ export const logger = {
|
|
|
243
303
|
plain,
|
|
244
304
|
newline,
|
|
245
305
|
section,
|
|
306
|
+
phase,
|
|
307
|
+
divider,
|
|
246
308
|
kv,
|
|
247
309
|
item,
|
|
248
310
|
};
|
package/src/ui/spinner.ts
CHANGED
|
@@ -103,6 +103,53 @@ export const withSpinner = async <T>(
|
|
|
103
103
|
}
|
|
104
104
|
};
|
|
105
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Execute async task with a spinner that updates its label with
|
|
108
|
+
* elapsed seconds while the task runs. Use for long-running phases
|
|
109
|
+
* (local node startup, publish + tx wait) where the user wants
|
|
110
|
+
* visible progress feedback in lieu of subprocess chatter.
|
|
111
|
+
*
|
|
112
|
+
* Pairs with the `§9` console-UX convention: any phase that
|
|
113
|
+
* empirically takes ≥3s in normal use should wrap its body in
|
|
114
|
+
* `withTimedSpinner` so the terminal never goes silent while work
|
|
115
|
+
* happens.
|
|
116
|
+
*
|
|
117
|
+
* @param label - Stable label shown next to the spinner (e.g. "Starting node")
|
|
118
|
+
* @param task - Async function to execute
|
|
119
|
+
* @param indent - Number of spaces to indent (default: 0)
|
|
120
|
+
* @returns Promise resolving to task result
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* await withTimedSpinner('Starting local node', async () => {
|
|
124
|
+
* await this.waitForReady(60_000);
|
|
125
|
+
* });
|
|
126
|
+
* // Renders: ⠋ Starting local node — 0.0s ... ⠼ Starting local node — 14.2s
|
|
127
|
+
* // On success: ✔ Starting local node (14.2s)
|
|
128
|
+
* // On error: ✖ <error.message>
|
|
129
|
+
*/
|
|
130
|
+
export const withTimedSpinner = async <T>(
|
|
131
|
+
label: string,
|
|
132
|
+
task: () => Promise<T>,
|
|
133
|
+
indent: number = 0
|
|
134
|
+
): Promise<T> => {
|
|
135
|
+
const start = Date.now();
|
|
136
|
+
const spin = spinner({ text: `${label} — 0.0s`, indent });
|
|
137
|
+
const timer = setInterval(() => {
|
|
138
|
+
spin.text = `${label} — ${((Date.now() - start) / 1000).toFixed(1)}s`;
|
|
139
|
+
}, 500);
|
|
140
|
+
try {
|
|
141
|
+
const result = await task();
|
|
142
|
+
spin.succeed(`${label} (${((Date.now() - start) / 1000).toFixed(1)}s)`);
|
|
143
|
+
return result;
|
|
144
|
+
} catch (error) {
|
|
145
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
146
|
+
spin.fail(errMsg);
|
|
147
|
+
throw error;
|
|
148
|
+
} finally {
|
|
149
|
+
clearInterval(timer);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
106
153
|
/**
|
|
107
154
|
* Spinner chain for sequential operations
|
|
108
155
|
* Manages multiple spinners in sequence
|