@objectstack/cli 0.8.1 โ†’ 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @objectstack/cli
2
2
 
3
+ ## 0.8.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [555e6a7]
8
+ - @objectstack/spec@0.8.2
9
+ - @objectstack/core@0.8.2
10
+ - @objectstack/plugin-hono-server@0.8.2
11
+
3
12
  ## 0.8.1
4
13
 
5
14
  ### Patch Changes
package/dist/bin.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-2YXVEYO7.js";
5
5
 
6
6
  // src/bin.ts
7
- import { Command as Command5 } from "commander";
7
+ import { Command as Command6 } from "commander";
8
8
 
9
9
  // src/commands/dev.ts
10
10
  import { Command } from "commander";
@@ -529,12 +529,85 @@ var serveCommand = new Command4("serve").description("Start ObjectStack server w
529
529
  }
530
530
  });
531
531
 
532
+ // src/commands/test.ts
533
+ import { Command as Command5 } from "commander";
534
+ import chalk5 from "chalk";
535
+ import path4 from "path";
536
+ import fs4 from "fs";
537
+ import { QA as CoreQA } from "@objectstack/core";
538
+ var testCommand = new Command5("test:run").description("Run Quality Protocol test scenarios").argument("[files]", 'Glob pattern for test files (e.g. "qa/*.test.json")', "qa/*.test.json").option("--url <url>", "Target base URL", "http://localhost:3000").option("--token <token>", "Authentication token").action(async (filesPattern, options) => {
539
+ console.log(chalk5.bold(`
540
+ \u{1F9EA} ObjectStack Quality Protocol Runner`));
541
+ console.log(chalk5.dim(`-------------------------------------`));
542
+ console.log(`Target: ${chalk5.blue(options.url)}`);
543
+ const adapter = new CoreQA.HttpTestAdapter(options.url, options.token);
544
+ const runner = new CoreQA.TestRunner(adapter);
545
+ const cwd = process.cwd();
546
+ const testFiles = [];
547
+ if (fs4.existsSync(filesPattern)) {
548
+ testFiles.push(filesPattern);
549
+ } else {
550
+ const dir = path4.dirname(filesPattern);
551
+ const ext = path4.extname(filesPattern);
552
+ if (fs4.existsSync(dir)) {
553
+ const files = fs4.readdirSync(dir).filter((f) => f.endsWith(ext) || f.endsWith(".json"));
554
+ files.forEach((f) => testFiles.push(path4.join(dir, f)));
555
+ }
556
+ }
557
+ if (testFiles.length === 0) {
558
+ console.warn(chalk5.yellow(`No test files found matching: ${filesPattern}`));
559
+ return;
560
+ }
561
+ console.log(`Found ${testFiles.length} test suites.`);
562
+ let totalPassed = 0;
563
+ let totalFailed = 0;
564
+ for (const file of testFiles) {
565
+ console.log(`
566
+ \u{1F4C4} Running suite: ${chalk5.bold(path4.basename(file))}`);
567
+ try {
568
+ const content = fs4.readFileSync(file, "utf-8");
569
+ const suite = JSON.parse(content);
570
+ const results = await runner.runSuite(suite);
571
+ for (const result of results) {
572
+ const icon = result.passed ? "\u2705" : "\u274C";
573
+ console.log(` ${icon} Scenario: ${result.scenarioId} (${result.duration}ms)`);
574
+ if (!result.passed) {
575
+ console.error(chalk5.red(` Error: ${result.error}`));
576
+ result.steps.forEach((step) => {
577
+ if (!step.passed) {
578
+ console.error(chalk5.red(` Step Failed: ${step.stepName}`));
579
+ if (step.output) console.error(` Output:`, step.output);
580
+ if (step.error) console.error(` Error:`, step.error);
581
+ }
582
+ });
583
+ totalFailed++;
584
+ } else {
585
+ totalPassed++;
586
+ }
587
+ }
588
+ } catch (e) {
589
+ console.error(chalk5.red(`Failed to load or run suite ${file}: ${e}`));
590
+ totalFailed++;
591
+ }
592
+ }
593
+ console.log(chalk5.dim(`
594
+ -------------------------------------`));
595
+ if (totalFailed > 0) {
596
+ console.log(chalk5.red(`FAILED: ${totalFailed} scenarios failed. ${totalPassed} passed.`));
597
+ process.exit(1);
598
+ } else {
599
+ console.log(chalk5.green(`SUCCESS: All ${totalPassed} scenarios passed.`));
600
+ process.exit(0);
601
+ }
602
+ });
603
+
532
604
  // src/bin.ts
533
- var program = new Command5();
605
+ var program = new Command6();
534
606
  program.name("objectstack").description("CLI for ObjectStack Protocol - Development Tools for Microkernel Architecture").version("0.8.0");
535
607
  program.addCommand(compileCommand);
536
608
  program.addCommand(serveCommand);
537
609
  program.addCommand(devCommand);
538
610
  program.addCommand(doctorCommand);
539
611
  program.addCommand(createCommand);
612
+ program.addCommand(testCommand);
540
613
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@objectstack/cli",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "Command Line Interface for ObjectStack Protocol",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -22,12 +22,12 @@
22
22
  "commander": "^11.1.0",
23
23
  "tsx": "^4.7.1",
24
24
  "zod": "^4.3.6",
25
- "@objectstack/spec": "0.8.1",
26
- "@objectstack/core": "0.8.1",
27
- "@objectstack/plugin-hono-server": "0.8.1"
25
+ "@objectstack/spec": "0.9.0",
26
+ "@objectstack/core": "0.9.0",
27
+ "@objectstack/plugin-hono-server": "0.9.0"
28
28
  },
29
29
  "peerDependencies": {
30
- "@objectstack/core": "^0.8.1"
30
+ "@objectstack/core": "^0.9.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^25.1.0",
package/src/bin.ts CHANGED
@@ -4,6 +4,7 @@ import { devCommand } from './commands/dev.js';
4
4
  import { doctorCommand } from './commands/doctor.js';
5
5
  import { createCommand } from './commands/create.js';
6
6
  import { serveCommand } from './commands/serve.js';
7
+ import { testCommand } from './commands/test.js';
7
8
 
8
9
  const program = new Command();
9
10
 
@@ -18,5 +19,6 @@ program.addCommand(serveCommand);
18
19
  program.addCommand(devCommand);
19
20
  program.addCommand(doctorCommand);
20
21
  program.addCommand(createCommand);
22
+ program.addCommand(testCommand);
21
23
 
22
24
  program.parse(process.argv);
@@ -0,0 +1,92 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import path from 'path';
4
+ import fs from 'fs';
5
+ import { QA as CoreQA } from '@objectstack/core';
6
+ import { QA } from '@objectstack/spec';
7
+
8
+ export const testCommand = new Command('test:run')
9
+ .description('Run Quality Protocol test scenarios')
10
+ .argument('[files]', 'Glob pattern for test files (e.g. "qa/*.test.json")', 'qa/*.test.json')
11
+ .option('--url <url>', 'Target base URL', 'http://localhost:3000')
12
+ .option('--token <token>', 'Authentication token')
13
+ .action(async (filesPattern, options) => {
14
+ console.log(chalk.bold(`\n๐Ÿงช ObjectStack Quality Protocol Runner`));
15
+ console.log(chalk.dim(`-------------------------------------`));
16
+ console.log(`Target: ${chalk.blue(options.url)}`);
17
+
18
+ // 1. Setup Runner
19
+ const adapter = new CoreQA.HttpTestAdapter(options.url, options.token);
20
+ const runner = new CoreQA.TestRunner(adapter);
21
+
22
+ // 2. Find Files (Simple implementation for now)
23
+ // TODO: Use glob
24
+ const cwd = process.cwd();
25
+ const testFiles: string[] = [];
26
+
27
+ // Very basic file finding for demo - assume explicit path or check local dir
28
+ if (fs.existsSync(filesPattern)) {
29
+ testFiles.push(filesPattern);
30
+ } else {
31
+ // Simple directory scan
32
+ const dir = path.dirname(filesPattern);
33
+ const ext = path.extname(filesPattern);
34
+ if (fs.existsSync(dir)) {
35
+ const files = fs.readdirSync(dir).filter(f => f.endsWith(ext) || f.endsWith('.json'));
36
+ files.forEach(f => testFiles.push(path.join(dir, f)));
37
+ }
38
+ }
39
+
40
+ if (testFiles.length === 0) {
41
+ console.warn(chalk.yellow(`No test files found matching: ${filesPattern}`));
42
+ // Create a demo test file if none exist?
43
+ return;
44
+ }
45
+
46
+ console.log(`Found ${testFiles.length} test suites.`);
47
+
48
+ // 3. Run Tests
49
+ let totalPassed = 0;
50
+ let totalFailed = 0;
51
+
52
+ for (const file of testFiles) {
53
+ console.log(`\n๐Ÿ“„ Running suite: ${chalk.bold(path.basename(file))}`);
54
+ try {
55
+ const content = fs.readFileSync(file, 'utf-8');
56
+ const suite = JSON.parse(content) as QA.TestSuite; // Should validate with Zod
57
+
58
+ const results = await runner.runSuite(suite);
59
+
60
+ for (const result of results) {
61
+ const icon = result.passed ? 'โœ…' : 'โŒ';
62
+ console.log(` ${icon} Scenario: ${result.scenarioId} (${result.duration}ms)`);
63
+ if (!result.passed) {
64
+ console.error(chalk.red(` Error: ${result.error}`));
65
+ result.steps.forEach(step => {
66
+ if (!step.passed) {
67
+ console.error(chalk.red(` Step Failed: ${step.stepName}`));
68
+ if (step.output) console.error(` Output:`, step.output);
69
+ if (step.error) console.error(` Error:`, step.error);
70
+ }
71
+ });
72
+ totalFailed++;
73
+ } else {
74
+ totalPassed++;
75
+ }
76
+ }
77
+ } catch (e) {
78
+ console.error(chalk.red(`Failed to load or run suite ${file}: ${e}`));
79
+ totalFailed++; // Count suite failure
80
+ }
81
+ }
82
+
83
+ // 4. Summary
84
+ console.log(chalk.dim(`\n-------------------------------------`));
85
+ if (totalFailed > 0) {
86
+ console.log(chalk.red(`FAILED: ${totalFailed} scenarios failed. ${totalPassed} passed.`));
87
+ process.exit(1);
88
+ } else {
89
+ console.log(chalk.green(`SUCCESS: All ${totalPassed} scenarios passed.`));
90
+ process.exit(0);
91
+ }
92
+ });