@5ive-tech/cli 1.0.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.
Files changed (139) hide show
  1. package/README.md +226 -0
  2. package/dist/assets/vm/five_vm_wasm.d.ts +762 -0
  3. package/dist/assets/vm/five_vm_wasm.js +3754 -0
  4. package/dist/assets/vm/five_vm_wasm_bg.wasm +0 -0
  5. package/dist/assets/vm/five_vm_wasm_bg.wasm.d.ts +247 -0
  6. package/dist/assets/vm/package.json +11 -0
  7. package/dist/cli.d.ts +47 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +343 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/commands/analyze.d.ts +3 -0
  12. package/dist/commands/analyze.d.ts.map +1 -0
  13. package/dist/commands/analyze.js +435 -0
  14. package/dist/commands/analyze.js.map +1 -0
  15. package/dist/commands/build.d.ts +3 -0
  16. package/dist/commands/build.d.ts.map +1 -0
  17. package/dist/commands/build.js +66 -0
  18. package/dist/commands/build.js.map +1 -0
  19. package/dist/commands/compile.d.ts +3 -0
  20. package/dist/commands/compile.d.ts.map +1 -0
  21. package/dist/commands/compile.js +872 -0
  22. package/dist/commands/compile.js.map +1 -0
  23. package/dist/commands/config.d.ts +3 -0
  24. package/dist/commands/config.d.ts.map +1 -0
  25. package/dist/commands/config.js +431 -0
  26. package/dist/commands/config.js.map +1 -0
  27. package/dist/commands/deploy-and-execute.d.ts +3 -0
  28. package/dist/commands/deploy-and-execute.d.ts.map +1 -0
  29. package/dist/commands/deploy-and-execute.js +317 -0
  30. package/dist/commands/deploy-and-execute.js.map +1 -0
  31. package/dist/commands/deploy.d.ts +21 -0
  32. package/dist/commands/deploy.d.ts.map +1 -0
  33. package/dist/commands/deploy.js +806 -0
  34. package/dist/commands/deploy.js.map +1 -0
  35. package/dist/commands/donate.d.ts +4 -0
  36. package/dist/commands/donate.d.ts.map +1 -0
  37. package/dist/commands/donate.js +104 -0
  38. package/dist/commands/donate.js.map +1 -0
  39. package/dist/commands/execute.d.ts +6 -0
  40. package/dist/commands/execute.d.ts.map +1 -0
  41. package/dist/commands/execute.js +749 -0
  42. package/dist/commands/execute.js.map +1 -0
  43. package/dist/commands/fmt.d.ts +3 -0
  44. package/dist/commands/fmt.d.ts.map +1 -0
  45. package/dist/commands/fmt.js +327 -0
  46. package/dist/commands/fmt.js.map +1 -0
  47. package/dist/commands/help.d.ts +6 -0
  48. package/dist/commands/help.d.ts.map +1 -0
  49. package/dist/commands/help.js +224 -0
  50. package/dist/commands/help.js.map +1 -0
  51. package/dist/commands/index.d.ts +45 -0
  52. package/dist/commands/index.d.ts.map +1 -0
  53. package/dist/commands/index.js +119 -0
  54. package/dist/commands/index.js.map +1 -0
  55. package/dist/commands/init.d.ts +3 -0
  56. package/dist/commands/init.d.ts.map +1 -0
  57. package/dist/commands/init.js +887 -0
  58. package/dist/commands/init.js.map +1 -0
  59. package/dist/commands/local.d.ts +3 -0
  60. package/dist/commands/local.d.ts.map +1 -0
  61. package/dist/commands/local.js +703 -0
  62. package/dist/commands/local.js.map +1 -0
  63. package/dist/commands/namespace.d.ts +3 -0
  64. package/dist/commands/namespace.d.ts.map +1 -0
  65. package/dist/commands/namespace.js +328 -0
  66. package/dist/commands/namespace.js.map +1 -0
  67. package/dist/commands/template.d.ts +4 -0
  68. package/dist/commands/template.d.ts.map +1 -0
  69. package/dist/commands/template.js +486 -0
  70. package/dist/commands/template.js.map +1 -0
  71. package/dist/commands/test.d.ts +6 -0
  72. package/dist/commands/test.d.ts.map +1 -0
  73. package/dist/commands/test.js +890 -0
  74. package/dist/commands/test.js.map +1 -0
  75. package/dist/commands/version.d.ts +6 -0
  76. package/dist/commands/version.d.ts.map +1 -0
  77. package/dist/commands/version.js +339 -0
  78. package/dist/commands/version.js.map +1 -0
  79. package/dist/config/ConfigManager.d.ts +69 -0
  80. package/dist/config/ConfigManager.d.ts.map +1 -0
  81. package/dist/config/ConfigManager.js +261 -0
  82. package/dist/config/ConfigManager.js.map +1 -0
  83. package/dist/config/index.d.ts +10 -0
  84. package/dist/config/index.d.ts.map +1 -0
  85. package/dist/config/index.js +21 -0
  86. package/dist/config/index.js.map +1 -0
  87. package/dist/config/types.d.ts +35 -0
  88. package/dist/config/types.d.ts.map +1 -0
  89. package/dist/config/types.js +105 -0
  90. package/dist/config/types.js.map +1 -0
  91. package/dist/index.d.ts +3 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +29 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/project/ProjectLoader.d.ts +12 -0
  96. package/dist/project/ProjectLoader.d.ts.map +1 -0
  97. package/dist/project/ProjectLoader.js +115 -0
  98. package/dist/project/ProjectLoader.js.map +1 -0
  99. package/dist/types.d.ts +334 -0
  100. package/dist/types.d.ts.map +1 -0
  101. package/dist/types.js +2 -0
  102. package/dist/types.js.map +1 -0
  103. package/dist/utils/AccountFixtureGenerator.d.ts +48 -0
  104. package/dist/utils/AccountFixtureGenerator.d.ts.map +1 -0
  105. package/dist/utils/AccountFixtureGenerator.js +265 -0
  106. package/dist/utils/AccountFixtureGenerator.js.map +1 -0
  107. package/dist/utils/FiveFileManager.d.ts +96 -0
  108. package/dist/utils/FiveFileManager.d.ts.map +1 -0
  109. package/dist/utils/FiveFileManager.js +329 -0
  110. package/dist/utils/FiveFileManager.js.map +1 -0
  111. package/dist/utils/ascii-art.d.ts +72 -0
  112. package/dist/utils/ascii-art.d.ts.map +1 -0
  113. package/dist/utils/ascii-art.js +314 -0
  114. package/dist/utils/ascii-art.js.map +1 -0
  115. package/dist/utils/cli-ui.d.ts +39 -0
  116. package/dist/utils/cli-ui.d.ts.map +1 -0
  117. package/dist/utils/cli-ui.js +75 -0
  118. package/dist/utils/cli-ui.js.map +1 -0
  119. package/dist/utils/fileUtils.d.ts +25 -0
  120. package/dist/utils/fileUtils.d.ts.map +1 -0
  121. package/dist/utils/fileUtils.js +50 -0
  122. package/dist/utils/fileUtils.js.map +1 -0
  123. package/dist/utils/logger.d.ts +53 -0
  124. package/dist/utils/logger.d.ts.map +1 -0
  125. package/dist/utils/logger.js +287 -0
  126. package/dist/utils/logger.js.map +1 -0
  127. package/dist/wasm/compiler.d.ts +101 -0
  128. package/dist/wasm/compiler.d.ts.map +1 -0
  129. package/dist/wasm/compiler.js +906 -0
  130. package/dist/wasm/compiler.js.map +1 -0
  131. package/dist/wasm/loader.d.ts +2 -0
  132. package/dist/wasm/loader.d.ts.map +1 -0
  133. package/dist/wasm/loader.js +90 -0
  134. package/dist/wasm/loader.js.map +1 -0
  135. package/dist/wasm/vm.d.ts +32 -0
  136. package/dist/wasm/vm.d.ts.map +1 -0
  137. package/dist/wasm/vm.js +440 -0
  138. package/dist/wasm/vm.js.map +1 -0
  139. package/package.json +100 -0
@@ -0,0 +1,703 @@
1
+ // Local WASM execution commands.
2
+ import { readFile, readdir, stat } from 'fs/promises';
3
+ import { join, extname, basename } from 'path';
4
+ import ora from 'ora';
5
+ import { FiveSDK } from '@5ive-tech/sdk';
6
+ import { section, success as uiSuccess, error as uiError } from '../utils/cli-ui.js';
7
+ // Parent for local subcommands.
8
+ export const localCommand = {
9
+ name: 'local',
10
+ description: 'Local WASM execution',
11
+ aliases: ['l'],
12
+ options: [
13
+ {
14
+ flags: '-f, --function <call>',
15
+ description: 'Function call: add(5, 3) or function name/index'
16
+ },
17
+ {
18
+ flags: '-p, --params <values...>',
19
+ description: 'Function parameters (space-separated or JSON)'
20
+ },
21
+ {
22
+ flags: '--debug',
23
+ description: 'Enable debug output'
24
+ },
25
+ {
26
+ flags: '--trace',
27
+ description: 'Enable execution trace'
28
+ },
29
+ {
30
+ flags: '--max-cu <units>',
31
+ description: 'Max compute units (default: 1000000)'
32
+ },
33
+ {
34
+ flags: '--max-compute-units <units>',
35
+ description: 'Alias for --max-cu'
36
+ },
37
+ {
38
+ flags: '--format <format>',
39
+ description: 'Output format: text, json (default: text)'
40
+ }
41
+ ],
42
+ arguments: [
43
+ {
44
+ name: 'subcommand',
45
+ description: 'Local subcommand (execute, test, compile)',
46
+ required: true
47
+ },
48
+ {
49
+ name: 'args',
50
+ description: 'Arguments for the subcommand',
51
+ required: false,
52
+ variadic: true
53
+ }
54
+ ],
55
+ examples: [
56
+ {
57
+ command: 'five local execute script.bin',
58
+ description: 'Execute script locally using WASM VM'
59
+ },
60
+ {
61
+ command: 'five local execute script.five add 5 3',
62
+ description: 'Execute add function with parameters 5 and 3'
63
+ },
64
+ {
65
+ command: 'five local execute script.five test',
66
+ description: 'Execute test function with no parameters'
67
+ },
68
+ {
69
+ command: 'five local execute script.five 2',
70
+ description: 'Execute function at index 2'
71
+ },
72
+ {
73
+ command: 'five local test',
74
+ description: 'Run local test suite with WASM VM'
75
+ }
76
+ ],
77
+ handler: async (args, options, context) => {
78
+ const { logger } = context;
79
+ const flatArgs = args ?? [];
80
+ if (flatArgs.length === 0) {
81
+ showLocalHelp(logger);
82
+ return;
83
+ }
84
+ const [subcommand, ...rawSubArgs] = flatArgs;
85
+ const subArgs = rawSubArgs.flatMap((arg) => {
86
+ if (Array.isArray(arg)) {
87
+ return arg.filter((value) => typeof value === 'string');
88
+ }
89
+ return typeof arg === 'string' ? [arg] : [];
90
+ });
91
+ // Local WASM execution mode
92
+ if (context.options.verbose) {
93
+ logger.info('Local WASM VM');
94
+ }
95
+ try {
96
+ switch (subcommand) {
97
+ case 'execute':
98
+ case 'exec':
99
+ case 'run':
100
+ await executeLocalSubcommand(subArgs, options, context);
101
+ break;
102
+ case 'test':
103
+ case 't':
104
+ await testLocalSubcommand(subArgs, options, context);
105
+ break;
106
+ case 'compile':
107
+ case 'c':
108
+ await compileLocalSubcommand(subArgs, options, context);
109
+ break;
110
+ default:
111
+ logger.error(`Unknown local subcommand: ${subcommand}`);
112
+ showLocalHelp(logger);
113
+ process.exit(1);
114
+ }
115
+ }
116
+ catch (error) {
117
+ logger.error(`Local ${subcommand} failed:`, error);
118
+ throw error;
119
+ }
120
+ }
121
+ };
122
+ /**
123
+ * Local execute subcommand - execute bytecode in local WASM VM
124
+ */
125
+ async function executeLocalSubcommand(args, options, context) {
126
+ const { logger } = context;
127
+ if (options.debug) {
128
+ logger.debug(`local execute args: ${JSON.stringify(args)}`);
129
+ }
130
+ if (args.length === 0) {
131
+ console.log(uiError('No file specified for execution'));
132
+ console.log(section('Usage'));
133
+ console.log(' five local execute <file> [function] [params...]');
134
+ console.log(section('Arguments'));
135
+ console.log(' <file> .five/.bin/.v file to execute');
136
+ console.log(' [function] Function name or index');
137
+ console.log(' [params...] Function parameters (space-separated)');
138
+ console.log(section('Options'));
139
+ console.log(' --debug Enable debug output');
140
+ console.log(' --trace Enable execution trace');
141
+ console.log(' --max-cu <n> Max compute units (default: 1000000)');
142
+ console.log(' --format <f> Output format: text, json (default: text)');
143
+ console.log(section('Examples'));
144
+ console.log(' five local execute script.five add 5 3');
145
+ console.log(' five local execute script.five test');
146
+ console.log(' five local execute script.five 2');
147
+ return;
148
+ }
149
+ const inputFile = args[0];
150
+ const rawFunctionName = args.length > 1 ? args[1] : undefined;
151
+ // Convert numeric strings to numbers for function index
152
+ let functionName = rawFunctionName;
153
+ if (rawFunctionName && /^\d+$/.test(rawFunctionName)) {
154
+ functionName = parseInt(rawFunctionName, 10);
155
+ }
156
+ const functionParams = args.length > 2 ? args.slice(2) : [];
157
+ if (options.debug) {
158
+ logger.debug(`local execute parsed: ${JSON.stringify({ inputFile, functionName, functionParams })}`);
159
+ }
160
+ // Initialize for local execution
161
+ if (context.options.verbose) {
162
+ const spinner = ora('Preparing local execution...').start();
163
+ spinner.text = 'Loading Five SDK...';
164
+ spinner.succeed('Five SDK ready for local execution');
165
+ }
166
+ try {
167
+ // Use space-separated syntax: file function param1 param2 ...
168
+ let functionRef = functionName || options.function || options.functionIndex || 0;
169
+ const parameters = functionParams.length > 0 ?
170
+ (Array.isArray(functionParams) ? functionParams.map(parseValue) : [parseValue(functionParams)]) :
171
+ parseParameters(options.params);
172
+ const debug = Boolean(options.debug || options.trace);
173
+ const trace = Boolean(options.trace);
174
+ const maxCU = options.maxCu || options.maxComputeUnits || 1000000;
175
+ const format = options.format || 'text';
176
+ // Keep function reference as-is - let the compiler handle function name resolution
177
+ // The Five compiler can resolve function names to indices properly
178
+ // Debug parameter parsing
179
+ if (debug) {
180
+ logger.debug(`local function: ${functionRef}`);
181
+ logger.debug(`local params: ${JSON.stringify(parameters)}`);
182
+ }
183
+ let result;
184
+ if (extname(inputFile) === '.five') {
185
+ // Handle .five files with embedded ABI
186
+ logger.info(`Executing Five file: ${inputFile}`);
187
+ const fileContent = await readFile(inputFile, 'utf8');
188
+ const { bytecode, abi } = await FiveSDK.loadFiveFile(fileContent);
189
+ // FUNCTION RESOLUTION FIX: Systematic approach for .five files
190
+ let resolvedFunctionRef = functionRef;
191
+ if (functionRef === undefined) {
192
+ // SYSTEMATIC APPROACH: Public functions always have the lowest indices (0, 1, 2...)
193
+ // So we always default to function index 0, which is guaranteed to be public if any functions exist
194
+ resolvedFunctionRef = 0;
195
+ if (debug && abi && abi.functions) {
196
+ let functionName = 'function_0';
197
+ if (Array.isArray(abi.functions)) {
198
+ // FIVEABI format: find function with index 0
199
+ const func0 = abi.functions.find((f) => f.index === 0);
200
+ if (func0 && func0.name)
201
+ functionName = func0.name;
202
+ }
203
+ else {
204
+ // SimpleABI format: find function with index 0
205
+ const func0Entry = Object.entries(abi.functions).find(([_, f]) => f.index === 0);
206
+ if (func0Entry)
207
+ functionName = func0Entry[0];
208
+ }
209
+ logger.debug(`auto-detected public function: ${functionName} (index 0)`);
210
+ }
211
+ }
212
+ result = await FiveSDK.executeLocally(bytecode, resolvedFunctionRef, parameters, {
213
+ debug,
214
+ trace,
215
+ computeUnitLimit: maxCU,
216
+ abi // Pass ABI for function name resolution
217
+ });
218
+ }
219
+ else if (extname(inputFile) === '.v') {
220
+ // Compile and execute Five source file
221
+ logger.info(`Compiling and executing Five source: ${inputFile}`);
222
+ const sourceCode = await readFile(inputFile, 'utf8');
223
+ result = await FiveSDK.execute(sourceCode, functionRef, parameters, {
224
+ debug,
225
+ trace,
226
+ optimize: true,
227
+ computeUnitLimit: maxCU
228
+ });
229
+ // Show compilation info
230
+ if ('compilation' in result && result.compilation) {
231
+ console.log(`Compilation: ${result.compilation.success ? 'OK' : 'FAIL'}`);
232
+ if ('bytecodeSize' in result && result.bytecodeSize) {
233
+ console.log(`Bytecode size: ${result.bytecodeSize} bytes`);
234
+ }
235
+ }
236
+ }
237
+ else {
238
+ // Execute existing bytecode file
239
+ logger.info(`Executing bytecode: ${inputFile}`);
240
+ const bytecode = await readFile(inputFile);
241
+ result = await FiveSDK.executeLocally(new Uint8Array(bytecode), functionRef, parameters, {
242
+ debug,
243
+ trace,
244
+ computeUnitLimit: maxCU
245
+ });
246
+ }
247
+ // Validate execution result structure
248
+ if (typeof result !== 'object' || result === null) {
249
+ logger.error('Invalid execution result from SDK');
250
+ process.exit(1);
251
+ }
252
+ if (typeof result.success !== 'boolean') {
253
+ logger.error('Execution result missing success field');
254
+ process.exit(1);
255
+ }
256
+ // Display results
257
+ displayLocalExecutionResult(result, { format, trace, debug }, logger);
258
+ // CRITICAL FIX: Check execution success and exit with proper code
259
+ if (!result.success) {
260
+ if (context.options.verbose) {
261
+ logger.error(`Execution failed: ${result.error || 'Unknown error'}`);
262
+ }
263
+ process.exit(1); // EXIT WITH FAILURE CODE
264
+ }
265
+ }
266
+ catch (error) {
267
+ if (context.options.verbose) {
268
+ ora().fail('Local execution failed');
269
+ }
270
+ process.exit(1); // Also ensure exceptions exit with failure
271
+ }
272
+ }
273
+ /**
274
+ * Local test subcommand - run test suite locally
275
+ */
276
+ async function testLocalSubcommand(args, options, context) {
277
+ const { logger } = context;
278
+ const testPath = args[0] || './tests';
279
+ const pattern = options.pattern || '**/*.test.json';
280
+ const filter = options.filter;
281
+ const verbose = options.verbose || options.debug;
282
+ const format = options.format || 'text';
283
+ logger.info('Running local test suite with WASM VM');
284
+ logger.info(`Test path: ${testPath}`);
285
+ logger.info(`Pattern: ${pattern}`);
286
+ const spinner = ora('Discovering test files...').start();
287
+ try {
288
+ // Discover test files
289
+ const testFiles = await discoverTestFiles(testPath, pattern);
290
+ if (testFiles.length === 0) {
291
+ spinner.warn('No test files found');
292
+ logger.warn(`No test files matching pattern "${pattern}" found in ${testPath}`);
293
+ logger.info('Expected test file format: JSON files with .test.json extension');
294
+ return;
295
+ }
296
+ spinner.succeed(`Found ${testFiles.length} test file(s)`);
297
+ // Run tests
298
+ const results = await runLocalTests(testFiles, {
299
+ filter,
300
+ verbose,
301
+ maxCU: options.maxCu || 1000000,
302
+ timeout: options.timeout || 30000
303
+ }, logger);
304
+ // Display results
305
+ displayTestResults(results, { format, verbose }, logger);
306
+ // Exit with error if any tests failed
307
+ const failed = results.some(r => !r.passed);
308
+ if (failed) {
309
+ process.exit(1);
310
+ }
311
+ }
312
+ catch (error) {
313
+ spinner.fail('Test discovery failed');
314
+ throw error;
315
+ }
316
+ }
317
+ /**
318
+ * Local compile subcommand - alias for main compile command
319
+ */
320
+ async function compileLocalSubcommand(args, options, context) {
321
+ const { logger } = context;
322
+ if (args.length === 0) {
323
+ logger.error('No file specified for compilation');
324
+ logger.info('Usage: five local compile <file> [options]');
325
+ logger.info('This is an alias for: five compile <file>');
326
+ return;
327
+ }
328
+ logger.info('Compiling locally (same as five compile)');
329
+ // Import and delegate to compile command
330
+ const { compileCommand } = await import('./compile.js');
331
+ await compileCommand.handler(args, options, context);
332
+ }
333
+ /**
334
+ * Show local command help
335
+ */
336
+ function showLocalHelp(logger) {
337
+ logger.info('Local Five VM Commands (WASM execution, no config needed):');
338
+ logger.info('');
339
+ logger.info('Subcommands:');
340
+ logger.info(' execute <file> Execute bytecode or source file locally');
341
+ logger.info(' test [pattern] Run local test suite');
342
+ logger.info(' compile <file> Compile source file (alias for main compile)');
343
+ logger.info('');
344
+ logger.info('Examples:');
345
+ logger.info(' five local execute script.bin');
346
+ logger.info(' five local execute script.v --function 1 --parameters "[10, 5]"');
347
+ logger.info(' five local test --pattern "*.test.json" --verbose');
348
+ logger.info(' five local compile script.v --optimize');
349
+ logger.info('');
350
+ logger.info('Features:');
351
+ logger.info(' • Always uses WASM VM (no network required)');
352
+ logger.info(' • No configuration dependencies');
353
+ logger.info(' • Fast development iteration');
354
+ logger.info(' • Compute unit tracking');
355
+ logger.info(' • Debug and trace support');
356
+ }
357
+ /**
358
+ * Parse function call syntax: add(5, 3) -> { name: 'add', params: [5, 3] }
359
+ */
360
+ function parseFunctionCall(functionOption) {
361
+ if (typeof functionOption === 'number') {
362
+ return { functionRef: functionOption, parameters: [] };
363
+ }
364
+ const functionStr = functionOption.toString().trim();
365
+ // Check if it's a function call syntax: name(params...)
366
+ const callMatch = functionStr.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*\(\s*(.*?)\s*\)$/);
367
+ if (callMatch) {
368
+ const [, functionName, paramsStr] = callMatch;
369
+ let parameters = [];
370
+ if (paramsStr.trim()) {
371
+ // Parse comma-separated parameters
372
+ try {
373
+ // Split by comma and parse each parameter
374
+ const paramStrs = paramsStr.split(',').map(p => p.trim());
375
+ parameters = paramStrs.map(parseValue);
376
+ }
377
+ catch (error) {
378
+ throw new Error(`Failed to parse function parameters in "${functionStr}": ${error}`);
379
+ }
380
+ }
381
+ return { functionRef: functionName, parameters };
382
+ }
383
+ // Check if it's a plain number (function index)
384
+ const numericValue = parseInt(functionStr, 10);
385
+ if (!isNaN(numericValue) && numericValue.toString() === functionStr) {
386
+ return { functionRef: numericValue, parameters: [] };
387
+ }
388
+ // Plain function name
389
+ return { functionRef: functionStr, parameters: [] };
390
+ }
391
+ /**
392
+ * Parse parameters from various formats: space-separated, JSON string, or array
393
+ */
394
+ function parseParameters(paramsOption) {
395
+ if (!paramsOption) {
396
+ return [];
397
+ }
398
+ try {
399
+ // Handle arrays directly (from --params a b c)
400
+ if (Array.isArray(paramsOption)) {
401
+ return paramsOption.map(parseValue);
402
+ }
403
+ // Handle string input
404
+ if (typeof paramsOption === 'string') {
405
+ // JSON array format
406
+ if (paramsOption.startsWith('[') || paramsOption.startsWith('{')) {
407
+ return JSON.parse(paramsOption);
408
+ }
409
+ // Single parameter
410
+ return [parseValue(paramsOption)];
411
+ }
412
+ // Single value
413
+ return [parseValue(paramsOption)];
414
+ }
415
+ catch (error) {
416
+ throw new Error(`Failed to parse parameters: ${error}. Use space-separated values like: -p 10 5, or JSON format: --parameters "[10, 5]"`);
417
+ }
418
+ }
419
+ /**
420
+ * Parse a single parameter value, converting strings to appropriate types
421
+ */
422
+ function parseValue(value) {
423
+ if (typeof value !== 'string') {
424
+ return value;
425
+ }
426
+ // Try to parse as number
427
+ const numValue = Number(value);
428
+ if (!isNaN(numValue) && isFinite(numValue)) {
429
+ return numValue;
430
+ }
431
+ // Try to parse as boolean
432
+ if (value.toLowerCase() === 'true')
433
+ return true;
434
+ if (value.toLowerCase() === 'false')
435
+ return false;
436
+ // Return as string
437
+ return value;
438
+ }
439
+ /**
440
+ * Display local execution results with clear formatting
441
+ */
442
+ function displayLocalExecutionResult(result, options, logger) {
443
+ // Only show header in verbose mode
444
+ if (options.verbose || options.debug) {
445
+ console.log('\n' + section('Local Execution'));
446
+ }
447
+ if (result.success) {
448
+ console.log(uiSuccess('Execution succeeded'));
449
+ if (result.result !== undefined) {
450
+ console.log(` Result: ${JSON.stringify(result.result)}`);
451
+ }
452
+ if (result.executionTime) {
453
+ console.log(` Time: ${result.executionTime}ms`);
454
+ }
455
+ if (result.computeUnitsUsed !== undefined) {
456
+ console.log(` Compute units: ${result.computeUnitsUsed.toLocaleString()}`);
457
+ }
458
+ if (result.logs && result.logs.length > 0) {
459
+ console.log('\n' + section('Logs'));
460
+ result.logs.forEach((log) => {
461
+ console.log(` ${log}`);
462
+ });
463
+ }
464
+ if (result.trace && options.trace) {
465
+ console.log('\n' + section('Trace'));
466
+ if (Array.isArray(result.trace)) {
467
+ result.trace.slice(0, 10).forEach((step, i) => {
468
+ console.log(` ${i}: ${JSON.stringify(step)}`);
469
+ });
470
+ if (result.trace.length > 10) {
471
+ console.log(` ... and ${result.trace.length - 10} more steps`);
472
+ }
473
+ }
474
+ }
475
+ }
476
+ else {
477
+ console.log(uiError(result.error || 'Execution failed'));
478
+ // Enhanced VM debugging information for runtime errors
479
+ if (result.vmState || result.debug) {
480
+ const vmInfo = result.vmState || result.debug || result;
481
+ console.log('\n' + section('VM Debug'));
482
+ if (vmInfo.instructionPointer !== undefined) {
483
+ console.log(` Instruction Pointer: 0x${vmInfo.instructionPointer.toString(16).toUpperCase().padStart(4, '0')}`);
484
+ }
485
+ if (vmInfo.stoppedAtOpcode !== undefined) {
486
+ console.log(` Stopped at Opcode: 0x${vmInfo.stoppedAtOpcode.toString(16).toUpperCase().padStart(2, '0')} (${vmInfo.stoppedAtOpcode})`);
487
+ }
488
+ if (vmInfo.stoppedAtOpcodeName) {
489
+ console.log(` Opcode Name: ${vmInfo.stoppedAtOpcodeName}`);
490
+ }
491
+ if (vmInfo.errorMessage) {
492
+ console.log(` VM Error: ${vmInfo.errorMessage}`);
493
+ }
494
+ if (vmInfo.finalStack && Array.isArray(vmInfo.finalStack)) {
495
+ console.log(` Final Stack (${vmInfo.finalStack.length} items):`);
496
+ vmInfo.finalStack.slice(-5).forEach((item, i) => {
497
+ const index = vmInfo.finalStack.length - 5 + i;
498
+ console.log(` [${index}]: ${JSON.stringify(item)}`);
499
+ });
500
+ if (vmInfo.finalStack.length > 5) {
501
+ console.log(` ... (${vmInfo.finalStack.length - 5} more items)`);
502
+ }
503
+ }
504
+ if (vmInfo.executionContext) {
505
+ console.log(` Execution Context: ${vmInfo.executionContext}`);
506
+ }
507
+ }
508
+ if (result.compilationErrors && result.compilationErrors.length > 0) {
509
+ console.log('\n Compilation errors:');
510
+ result.compilationErrors.forEach((error) => {
511
+ console.log(` - ${error.message || error}`);
512
+ });
513
+ }
514
+ }
515
+ // JSON format output
516
+ if (options.format === 'json') {
517
+ console.log('\n' + section('JSON Output'));
518
+ console.log(JSON.stringify(result, null, 2));
519
+ }
520
+ }
521
+ /**
522
+ * Discover test files based on pattern
523
+ */
524
+ async function discoverTestFiles(testPath, pattern) {
525
+ const testFiles = [];
526
+ try {
527
+ const stats = await stat(testPath);
528
+ if (stats.isFile()) {
529
+ // Single test file
530
+ if (testPath.endsWith('.test.json')) {
531
+ testFiles.push(testPath);
532
+ }
533
+ }
534
+ else if (stats.isDirectory()) {
535
+ // Directory - recursively find test files
536
+ const files = await readdir(testPath, { recursive: true });
537
+ for (const file of files) {
538
+ if (typeof file === 'string' && file.endsWith('.test.json')) {
539
+ testFiles.push(join(testPath, file));
540
+ }
541
+ }
542
+ }
543
+ }
544
+ catch (error) {
545
+ // Directory/file doesn't exist - that's ok
546
+ }
547
+ return testFiles;
548
+ }
549
+ /**
550
+ * Run tests locally using WASM VM
551
+ */
552
+ async function runLocalTests(testFiles, options, logger) {
553
+ const results = [];
554
+ for (const testFile of testFiles) {
555
+ logger.info(`Running tests from: ${testFile}`);
556
+ try {
557
+ const content = await readFile(testFile, 'utf8');
558
+ const testSuite = JSON.parse(content);
559
+ const suiteName = testSuite.name || basename(testFile, '.test.json');
560
+ const testCases = testSuite.tests || testSuite.testCases || [];
561
+ // Apply filter if specified
562
+ let filteredTests = testCases;
563
+ if (options.filter) {
564
+ filteredTests = testCases.filter((test) => test.name?.includes(options.filter) || suiteName.includes(options.filter));
565
+ }
566
+ for (const testCase of filteredTests) {
567
+ const result = await runSingleLocalTest(testCase, options, logger);
568
+ results.push(result);
569
+ if (options.verbose) {
570
+ displaySingleTestResult(result, logger);
571
+ }
572
+ }
573
+ }
574
+ catch (error) {
575
+ results.push({
576
+ name: `Load ${basename(testFile)}`,
577
+ passed: false,
578
+ duration: 0,
579
+ error: `Failed to load test file: ${error}`
580
+ });
581
+ }
582
+ }
583
+ return results;
584
+ }
585
+ /**
586
+ * Run a single test case locally
587
+ */
588
+ async function runSingleLocalTest(testCase, options, logger) {
589
+ const startTime = Date.now();
590
+ try {
591
+ // Load bytecode
592
+ const bytecode = await readFile(testCase.bytecode);
593
+ // Parse input parameters
594
+ let parameters = [];
595
+ if (testCase.input) {
596
+ try {
597
+ const inputData = await readFile(testCase.input, 'utf8');
598
+ parameters = JSON.parse(inputData);
599
+ }
600
+ catch {
601
+ parameters = [];
602
+ }
603
+ }
604
+ // Execute with timeout
605
+ const executionPromise = FiveSDK.executeLocally(new Uint8Array(bytecode), testCase.functionIndex || 0, parameters, {
606
+ debug: options.verbose,
607
+ trace: options.verbose,
608
+ computeUnitLimit: options.maxCU
609
+ });
610
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Test timeout')), options.timeout));
611
+ const result = await Promise.race([executionPromise, timeoutPromise]);
612
+ const duration = Date.now() - startTime;
613
+ // Validate result
614
+ const passed = validateTestResult(result, testCase.expected || {});
615
+ return {
616
+ name: testCase.name,
617
+ passed,
618
+ duration,
619
+ details: options.verbose ? result : undefined
620
+ };
621
+ }
622
+ catch (error) {
623
+ const duration = Date.now() - startTime;
624
+ // Check if error was expected
625
+ const passed = testCase.expected?.success === false &&
626
+ testCase.expected?.error !== undefined &&
627
+ error instanceof Error &&
628
+ error.message.includes(testCase.expected.error);
629
+ return {
630
+ name: testCase.name,
631
+ passed,
632
+ duration,
633
+ error: error instanceof Error ? error.message : 'Unknown error'
634
+ };
635
+ }
636
+ }
637
+ /**
638
+ * Validate test result against expected outcome
639
+ */
640
+ function validateTestResult(result, expected) {
641
+ // Check success/failure
642
+ if (result.success !== expected.success) {
643
+ return false;
644
+ }
645
+ // If expecting success, check result value
646
+ if (expected.success && expected.result !== undefined) {
647
+ if (JSON.stringify(result.result) !== JSON.stringify(expected.result)) {
648
+ return false;
649
+ }
650
+ }
651
+ // Check compute units limit
652
+ if (expected.maxComputeUnits && result.computeUnitsUsed > expected.maxComputeUnits) {
653
+ return false;
654
+ }
655
+ return true;
656
+ }
657
+ /**
658
+ * Display single test result
659
+ */
660
+ function displaySingleTestResult(result, logger) {
661
+ const status = result.passed ? 'OK' : 'FAIL';
662
+ const duration = `(${result.duration}ms)`;
663
+ console.log(` ${status} ${result.name} ${duration}`);
664
+ if (!result.passed && result.error) {
665
+ console.log(` Error: ${result.error}`);
666
+ }
667
+ }
668
+ /**
669
+ * Display comprehensive test results
670
+ */
671
+ function displayTestResults(results, options, logger) {
672
+ if (options.format === 'json') {
673
+ console.log(JSON.stringify(results, null, 2));
674
+ return;
675
+ }
676
+ console.log('\n' + section('Local Tests'));
677
+ const passed = results.filter(r => r.passed).length;
678
+ const total = results.length;
679
+ const totalDuration = results.reduce((sum, r) => sum + r.duration, 0);
680
+ // Summary
681
+ console.log(`\nSummary:`);
682
+ console.log(` Total: ${total} tests`);
683
+ console.log(` Passed: ${passed}`);
684
+ const failed = total - passed;
685
+ if (failed > 0) {
686
+ console.log(` Failed: ${failed}`);
687
+ // Show failed tests
688
+ if (!options.verbose) {
689
+ console.log('\nFailed tests:');
690
+ results.filter(r => !r.passed).forEach(result => {
691
+ console.log(` FAIL ${result.name}: ${result.error || 'Test failed'}`);
692
+ });
693
+ }
694
+ }
695
+ console.log(` Duration: ${totalDuration}ms`);
696
+ if (failed === 0) {
697
+ console.log(uiSuccess('All tests passed'));
698
+ }
699
+ else {
700
+ console.log(uiError(`${failed} test(s) failed`));
701
+ }
702
+ }
703
+ //# sourceMappingURL=local.js.map