qa360 1.0.3 → 1.1.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.
Files changed (108) hide show
  1. package/dist/commands/history.js +1 -1
  2. package/dist/commands/pack.js +1 -1
  3. package/dist/commands/run.d.ts +1 -1
  4. package/dist/commands/run.d.ts.map +1 -1
  5. package/dist/commands/run.js +1 -1
  6. package/dist/commands/secrets.js +1 -1
  7. package/dist/commands/serve.js +1 -1
  8. package/dist/commands/verify.js +1 -1
  9. package/dist/core/adapters/gitleaks-secrets.d.ts +115 -0
  10. package/dist/core/adapters/gitleaks-secrets.d.ts.map +1 -0
  11. package/dist/core/adapters/gitleaks-secrets.js +410 -0
  12. package/dist/core/adapters/k6-perf.d.ts +86 -0
  13. package/dist/core/adapters/k6-perf.d.ts.map +1 -0
  14. package/dist/core/adapters/k6-perf.js +398 -0
  15. package/dist/core/adapters/osv-deps.d.ts +124 -0
  16. package/dist/core/adapters/osv-deps.d.ts.map +1 -0
  17. package/dist/core/adapters/osv-deps.js +372 -0
  18. package/dist/core/adapters/playwright-api.d.ts +82 -0
  19. package/dist/core/adapters/playwright-api.d.ts.map +1 -0
  20. package/dist/core/adapters/playwright-api.js +252 -0
  21. package/dist/core/adapters/playwright-ui.d.ts +115 -0
  22. package/dist/core/adapters/playwright-ui.d.ts.map +1 -0
  23. package/dist/core/adapters/playwright-ui.js +346 -0
  24. package/dist/core/adapters/semgrep-sast.d.ts +100 -0
  25. package/dist/core/adapters/semgrep-sast.d.ts.map +1 -0
  26. package/dist/core/adapters/semgrep-sast.js +322 -0
  27. package/dist/core/adapters/zap-dast.d.ts +134 -0
  28. package/dist/core/adapters/zap-dast.d.ts.map +1 -0
  29. package/dist/core/adapters/zap-dast.js +424 -0
  30. package/dist/core/hooks/compose.d.ts +62 -0
  31. package/dist/core/hooks/compose.d.ts.map +1 -0
  32. package/dist/core/hooks/compose.js +225 -0
  33. package/dist/core/hooks/runner.d.ts +69 -0
  34. package/dist/core/hooks/runner.d.ts.map +1 -0
  35. package/dist/core/hooks/runner.js +303 -0
  36. package/dist/core/index.d.ts +74 -0
  37. package/dist/core/index.d.ts.map +1 -0
  38. package/dist/core/index.js +39 -0
  39. package/dist/core/pack/migrator.d.ts +52 -0
  40. package/dist/core/pack/migrator.d.ts.map +1 -0
  41. package/dist/core/pack/migrator.js +304 -0
  42. package/dist/core/pack/validator.d.ts +43 -0
  43. package/dist/core/pack/validator.d.ts.map +1 -0
  44. package/dist/core/pack/validator.js +292 -0
  45. package/dist/core/proof/bundle.d.ts +138 -0
  46. package/dist/core/proof/bundle.d.ts.map +1 -0
  47. package/dist/core/proof/bundle.js +160 -0
  48. package/dist/core/proof/canonicalize.d.ts +48 -0
  49. package/dist/core/proof/canonicalize.d.ts.map +1 -0
  50. package/dist/core/proof/canonicalize.js +105 -0
  51. package/dist/core/proof/index.d.ts +14 -0
  52. package/dist/core/proof/index.d.ts.map +1 -0
  53. package/dist/core/proof/index.js +18 -0
  54. package/dist/core/proof/schema.d.ts +218 -0
  55. package/dist/core/proof/schema.d.ts.map +1 -0
  56. package/dist/core/proof/schema.js +263 -0
  57. package/dist/core/proof/signer.d.ts +112 -0
  58. package/dist/core/proof/signer.d.ts.map +1 -0
  59. package/dist/core/proof/signer.js +226 -0
  60. package/dist/core/proof/verifier.d.ts +98 -0
  61. package/dist/core/proof/verifier.d.ts.map +1 -0
  62. package/dist/core/proof/verifier.js +302 -0
  63. package/dist/core/runner/phase3-runner.d.ts +102 -0
  64. package/dist/core/runner/phase3-runner.d.ts.map +1 -0
  65. package/dist/core/runner/phase3-runner.js +471 -0
  66. package/dist/core/secrets/crypto.d.ts +76 -0
  67. package/dist/core/secrets/crypto.d.ts.map +1 -0
  68. package/dist/core/secrets/crypto.js +225 -0
  69. package/dist/core/secrets/manager.d.ts +77 -0
  70. package/dist/core/secrets/manager.d.ts.map +1 -0
  71. package/dist/core/secrets/manager.js +219 -0
  72. package/dist/core/security/redaction-patterns-extended.d.ts +28 -0
  73. package/dist/core/security/redaction-patterns-extended.d.ts.map +1 -0
  74. package/dist/core/security/redaction-patterns-extended.js +247 -0
  75. package/dist/core/security/redactor.d.ts +72 -0
  76. package/dist/core/security/redactor.d.ts.map +1 -0
  77. package/dist/core/security/redactor.js +279 -0
  78. package/dist/core/serve/diagnostics-collector.d.ts +33 -0
  79. package/dist/core/serve/diagnostics-collector.d.ts.map +1 -0
  80. package/dist/core/serve/diagnostics-collector.js +149 -0
  81. package/dist/core/serve/health-checker.d.ts +45 -0
  82. package/dist/core/serve/health-checker.d.ts.map +1 -0
  83. package/dist/core/serve/health-checker.js +219 -0
  84. package/dist/core/serve/index.d.ts +9 -0
  85. package/dist/core/serve/index.d.ts.map +1 -0
  86. package/dist/core/serve/index.js +8 -0
  87. package/dist/core/serve/metrics-collector.d.ts +25 -0
  88. package/dist/core/serve/metrics-collector.d.ts.map +1 -0
  89. package/dist/core/serve/metrics-collector.js +322 -0
  90. package/dist/core/serve/process-manager.d.ts +37 -0
  91. package/dist/core/serve/process-manager.d.ts.map +1 -0
  92. package/dist/core/serve/process-manager.js +213 -0
  93. package/dist/core/serve/server.d.ts +37 -0
  94. package/dist/core/serve/server.d.ts.map +1 -0
  95. package/dist/core/serve/server.js +191 -0
  96. package/dist/core/types/pack-v1.d.ts +162 -0
  97. package/dist/core/types/pack-v1.d.ts.map +1 -0
  98. package/dist/core/types/pack-v1.js +5 -0
  99. package/dist/core/types/trust-score.d.ts +70 -0
  100. package/dist/core/types/trust-score.d.ts.map +1 -0
  101. package/dist/core/types/trust-score.js +191 -0
  102. package/dist/core/vault/cas.d.ts +87 -0
  103. package/dist/core/vault/cas.d.ts.map +1 -0
  104. package/dist/core/vault/cas.js +255 -0
  105. package/dist/core/vault/index.d.ts +205 -0
  106. package/dist/core/vault/index.d.ts.map +1 -0
  107. package/dist/core/vault/index.js +631 -0
  108. package/package.json +12 -6
@@ -0,0 +1,225 @@
1
+ /**
2
+ * QA360 Docker Compose Helper
3
+ * Safe Docker Compose operations with health checks and error handling
4
+ */
5
+ import { spawn } from 'child_process';
6
+ import { existsSync } from 'fs';
7
+ import { join } from 'path';
8
+ import chalk from 'chalk';
9
+ export class ComposeHelper {
10
+ workingDir;
11
+ composeFile;
12
+ isUp = false;
13
+ constructor(options) {
14
+ this.workingDir = options.workingDir;
15
+ this.composeFile = options.composeFile || 'services.yml';
16
+ }
17
+ /**
18
+ * Start services with Docker Compose
19
+ */
20
+ async up(timeout = 120000) {
21
+ // Idempotence check
22
+ if (this.isUp) {
23
+ console.log(chalk.yellow(' āš ļø Services already up, skipping'));
24
+ return;
25
+ }
26
+ const composeFilePath = join(this.workingDir, this.composeFile);
27
+ // Validate compose file exists
28
+ if (!existsSync(composeFilePath)) {
29
+ const error = new Error(`Compose file not found: ${composeFilePath}`);
30
+ error.code = 'QH001';
31
+ error.suggestion = `Create ${this.composeFile} in project root or set hooks.compose_file in pack.yml`;
32
+ throw error;
33
+ }
34
+ try {
35
+ // Check if Docker is available
36
+ await this.checkDockerAvailable();
37
+ // Execute compose up
38
+ const result = await this.executeCompose(['up', '-d'], timeout);
39
+ if (result.success) {
40
+ this.isUp = true;
41
+ console.log(chalk.green(' 🐳 Services started successfully'));
42
+ }
43
+ else {
44
+ const error = new Error(`Compose up failed: ${result.error}`);
45
+ error.code = 'QH002';
46
+ error.suggestion = 'Check Docker daemon is running and compose file syntax is valid';
47
+ throw error;
48
+ }
49
+ }
50
+ catch (error) {
51
+ if (error instanceof Error && 'code' in error) {
52
+ throw error;
53
+ }
54
+ const composeError = new Error(`Compose operation failed: ${error}`);
55
+ composeError.code = 'QH003';
56
+ composeError.suggestion = 'Verify Docker installation and compose file configuration';
57
+ throw composeError;
58
+ }
59
+ }
60
+ /**
61
+ * Stop and remove services
62
+ */
63
+ async down(timeout = 60000) {
64
+ try {
65
+ const result = await this.executeCompose(['down', '-v'], timeout);
66
+ if (result.success) {
67
+ this.isUp = false;
68
+ console.log(chalk.green(' 🐳 Services stopped and cleaned up'));
69
+ }
70
+ else {
71
+ console.log(chalk.yellow(` āš ļø Compose down completed with warnings: ${result.error}`));
72
+ this.isUp = false; // Consider it down even if there were warnings
73
+ }
74
+ }
75
+ catch (error) {
76
+ // Don't throw on down errors - just log and continue
77
+ console.log(chalk.yellow(` āš ļø Compose down error (continuing): ${error}`));
78
+ this.isUp = false;
79
+ }
80
+ }
81
+ /**
82
+ * Get service status
83
+ */
84
+ async getStatus() {
85
+ try {
86
+ const result = await this.executeCompose(['ps', '--format', 'json'], 10000);
87
+ if (result.success && result.output) {
88
+ const services = result.output
89
+ .split('\n')
90
+ .filter(line => line.trim())
91
+ .map(line => {
92
+ try {
93
+ const service = JSON.parse(line);
94
+ return {
95
+ service: service.Name || service.Service || 'unknown',
96
+ status: service.State || service.Status || 'unknown',
97
+ ports: service.Ports ? service.Ports.split(',').map((p) => p.trim()) : []
98
+ };
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ })
104
+ .filter((service) => service !== null);
105
+ return services;
106
+ }
107
+ }
108
+ catch (error) {
109
+ console.log(chalk.yellow(` āš ļø Could not get service status: ${error}`));
110
+ }
111
+ return [];
112
+ }
113
+ /**
114
+ * Check if Docker is available
115
+ */
116
+ async checkDockerAvailable() {
117
+ try {
118
+ const result = await this.executeCommand('docker --version', 5000);
119
+ if (!result.success) {
120
+ const error = new Error('Docker not available');
121
+ error.code = 'QH004';
122
+ error.suggestion = 'Install Docker Desktop or Docker Engine';
123
+ throw error;
124
+ }
125
+ }
126
+ catch (error) {
127
+ const dockerError = new Error('Docker daemon not running');
128
+ dockerError.code = 'QH005';
129
+ dockerError.suggestion = 'Start Docker Desktop or Docker daemon service';
130
+ throw dockerError;
131
+ }
132
+ }
133
+ /**
134
+ * Execute Docker Compose command
135
+ */
136
+ async executeCompose(args, timeout) {
137
+ const composeFilePath = join(this.workingDir, this.composeFile);
138
+ const command = `docker compose -f "${composeFilePath}" ${args.join(' ')}`;
139
+ return this.executeCommand(command, timeout);
140
+ }
141
+ /**
142
+ * Execute shell command with timeout
143
+ */
144
+ async executeCommand(command, timeout) {
145
+ return new Promise((resolve) => {
146
+ let output = '';
147
+ let error = '';
148
+ const child = spawn(command, [], {
149
+ cwd: this.workingDir,
150
+ shell: true,
151
+ stdio: ['ignore', 'pipe', 'pipe']
152
+ });
153
+ const timer = setTimeout(() => {
154
+ child.kill('SIGKILL');
155
+ resolve({
156
+ success: false,
157
+ output,
158
+ error: `Command timed out after ${timeout}ms`
159
+ });
160
+ }, timeout);
161
+ if (child.stdout) {
162
+ child.stdout.on('data', (data) => {
163
+ output += data.toString();
164
+ });
165
+ }
166
+ if (child.stderr) {
167
+ child.stderr.on('data', (data) => {
168
+ error += data.toString();
169
+ });
170
+ }
171
+ child.on('close', (code) => {
172
+ clearTimeout(timer);
173
+ resolve({
174
+ success: code === 0,
175
+ output,
176
+ error: error || undefined
177
+ });
178
+ });
179
+ child.on('error', (err) => {
180
+ clearTimeout(timer);
181
+ resolve({
182
+ success: false,
183
+ output,
184
+ error: err.message
185
+ });
186
+ });
187
+ });
188
+ }
189
+ /**
190
+ * Validate compose file syntax
191
+ */
192
+ async validateComposeFile() {
193
+ const errors = [];
194
+ const composeFilePath = join(this.workingDir, this.composeFile);
195
+ if (!existsSync(composeFilePath)) {
196
+ errors.push(`Compose file not found: ${composeFilePath}`);
197
+ return { valid: false, errors };
198
+ }
199
+ try {
200
+ const result = await this.executeCompose(['config', '--quiet'], 10000);
201
+ if (!result.success) {
202
+ errors.push(`Compose file validation failed: ${result.error}`);
203
+ }
204
+ }
205
+ catch (error) {
206
+ errors.push(`Could not validate compose file: ${error}`);
207
+ }
208
+ return {
209
+ valid: errors.length === 0,
210
+ errors
211
+ };
212
+ }
213
+ /**
214
+ * Get compose file path
215
+ */
216
+ getComposeFilePath() {
217
+ return join(this.workingDir, this.composeFile);
218
+ }
219
+ /**
220
+ * Check if services are currently up
221
+ */
222
+ isServicesUp() {
223
+ return this.isUp;
224
+ }
225
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * QA360 Hooks Runner
3
+ * Executes declarative hooks with timeouts, failure handling, and security
4
+ */
5
+ import { PackHooks, PackExecution } from '../types/pack-v1.js';
6
+ import { SecurityRedactor } from '../security/redactor.js';
7
+ export interface HookExecutionResult {
8
+ success: boolean;
9
+ duration: number;
10
+ output: string;
11
+ error?: string;
12
+ code?: number;
13
+ }
14
+ export interface HookRunnerOptions {
15
+ workingDir: string;
16
+ hooks: PackHooks;
17
+ execution?: PackExecution;
18
+ redactor?: SecurityRedactor;
19
+ }
20
+ export declare class HooksRunner {
21
+ private workingDir;
22
+ private hooks;
23
+ private execution;
24
+ private redactor;
25
+ private composeHelper;
26
+ constructor(options: HookRunnerOptions);
27
+ /**
28
+ * Execute hooks for a specific lifecycle phase
29
+ */
30
+ executeHooks(phase: keyof PackHooks): Promise<HookExecutionResult[]>;
31
+ /**
32
+ * Execute a single hook action
33
+ */
34
+ private executeHook;
35
+ /**
36
+ * Execute a shell command
37
+ */
38
+ private executeCommand;
39
+ /**
40
+ * Execute Docker Compose command
41
+ */
42
+ private executeCompose;
43
+ /**
44
+ * Execute wait-on for service readiness
45
+ */
46
+ private executeWaitOn;
47
+ /**
48
+ * Wait for HTTP endpoint to be ready
49
+ */
50
+ private waitForHttp;
51
+ /**
52
+ * Wait for TCP port to be ready
53
+ */
54
+ private waitForTcp;
55
+ /**
56
+ * Get human-readable description of hook action
57
+ */
58
+ private getHookDescription;
59
+ /**
60
+ * Execute all lifecycle hooks in order
61
+ */
62
+ executeLifecycle(): Promise<{
63
+ beforeAll: HookExecutionResult[];
64
+ beforeEach: HookExecutionResult[];
65
+ afterEach: HookExecutionResult[];
66
+ afterAll: HookExecutionResult[];
67
+ }>;
68
+ }
69
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/core/hooks/runner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EAAc,SAAS,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAG3D,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,SAAS,CAAC;IACjB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,QAAQ,CAAC,EAAE,gBAAgB,CAAC;CAC7B;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,KAAK,CAAY;IACzB,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,QAAQ,CAAmB;IACnC,OAAO,CAAC,aAAa,CAAgB;gBAEzB,OAAO,EAAE,iBAAiB;IAWtC;;OAEG;IACG,YAAY,CAAC,KAAK,EAAE,MAAM,SAAS,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAiE1E;;OAEG;YACW,WAAW;IAczB;;OAEG;YACW,cAAc;IAgE5B;;OAEG;YACW,cAAc;IAyB5B;;OAEG;YACW,aAAa;IAyB3B;;OAEG;YACW,WAAW;IAwBzB;;OAEG;YACW,UAAU;IAyCxB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAW1B;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC;QAChC,SAAS,EAAE,mBAAmB,EAAE,CAAC;QACjC,UAAU,EAAE,mBAAmB,EAAE,CAAC;QAClC,SAAS,EAAE,mBAAmB,EAAE,CAAC;QACjC,QAAQ,EAAE,mBAAmB,EAAE,CAAC;KACjC,CAAC;CAgBH"}
@@ -0,0 +1,303 @@
1
+ /**
2
+ * QA360 Hooks Runner
3
+ * Executes declarative hooks with timeouts, failure handling, and security
4
+ */
5
+ import { spawn } from 'child_process';
6
+ import chalk from 'chalk';
7
+ import { SecurityRedactor } from '../security/redactor.js';
8
+ import { ComposeHelper } from './compose.js';
9
+ export class HooksRunner {
10
+ workingDir;
11
+ hooks;
12
+ execution;
13
+ redactor;
14
+ composeHelper;
15
+ constructor(options) {
16
+ this.workingDir = options.workingDir;
17
+ this.hooks = options.hooks;
18
+ this.execution = options.execution || {};
19
+ this.redactor = options.redactor || SecurityRedactor.forLogs();
20
+ this.composeHelper = new ComposeHelper({
21
+ workingDir: this.workingDir,
22
+ composeFile: this.execution.compose_file || 'services.yml'
23
+ });
24
+ }
25
+ /**
26
+ * Execute hooks for a specific lifecycle phase
27
+ */
28
+ async executeHooks(phase) {
29
+ const hooks = this.hooks[phase];
30
+ if (!hooks || hooks.length === 0) {
31
+ return [];
32
+ }
33
+ console.log(chalk.blue(`šŸ”— Executing ${phase} hooks (${hooks.length} actions)`));
34
+ const results = [];
35
+ for (let i = 0; i < hooks.length; i++) {
36
+ const hook = hooks[i];
37
+ const startTime = Date.now();
38
+ try {
39
+ console.log(chalk.gray(` ${i + 1}/${hooks.length} ${this.getHookDescription(hook)}`));
40
+ const result = await this.executeHook(hook);
41
+ const duration = Date.now() - startTime;
42
+ const hookResult = {
43
+ success: result.success,
44
+ duration,
45
+ output: this.redactor.redact(result.output),
46
+ error: result.error ? this.redactor.redact(result.error) : undefined,
47
+ code: result.code
48
+ };
49
+ results.push(hookResult);
50
+ if (result.success) {
51
+ console.log(chalk.green(` āœ… Completed in ${duration}ms`));
52
+ }
53
+ else {
54
+ console.log(chalk.red(` āŒ Failed after ${duration}ms (exit ${result.code})`));
55
+ if (this.execution.on_failure === 'stop') {
56
+ console.log(chalk.yellow(` šŸ›‘ Stopping execution (on_failure: stop)`));
57
+ break;
58
+ }
59
+ else {
60
+ console.log(chalk.yellow(` āš ļø Continuing execution (on_failure: continue)`));
61
+ }
62
+ }
63
+ }
64
+ catch (error) {
65
+ const duration = Date.now() - startTime;
66
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
67
+ console.log(chalk.red(` šŸ’„ Hook failed: ${errorMessage}`));
68
+ results.push({
69
+ success: false,
70
+ duration,
71
+ output: '',
72
+ error: this.redactor.redact(errorMessage)
73
+ });
74
+ if (this.execution.on_failure === 'stop') {
75
+ break;
76
+ }
77
+ }
78
+ }
79
+ return results;
80
+ }
81
+ /**
82
+ * Execute a single hook action
83
+ */
84
+ async executeHook(hook) {
85
+ const timeout = hook.timeout || this.execution.hook_timeout_ms || 180000; // 3 minutes default
86
+ if ('run' in hook) {
87
+ return this.executeCommand(hook.run, timeout);
88
+ }
89
+ else if ('compose' in hook) {
90
+ return this.executeCompose(hook.compose, timeout);
91
+ }
92
+ else if ('wait_on' in hook) {
93
+ return this.executeWaitOn(hook.wait_on, timeout, hook.method || 'http');
94
+ }
95
+ else {
96
+ throw new Error('Unknown hook action type');
97
+ }
98
+ }
99
+ /**
100
+ * Execute a shell command
101
+ */
102
+ async executeCommand(command, timeout) {
103
+ return new Promise((resolve) => {
104
+ const startTime = Date.now();
105
+ let output = '';
106
+ let error = '';
107
+ const options = {
108
+ cwd: this.workingDir,
109
+ shell: true,
110
+ stdio: ['ignore', 'pipe', 'pipe']
111
+ };
112
+ const child = spawn(command, [], options);
113
+ // Timeout handler
114
+ const timer = setTimeout(() => {
115
+ child.kill('SIGKILL');
116
+ resolve({
117
+ success: false,
118
+ duration: Date.now() - startTime,
119
+ output,
120
+ error: `Command timed out after ${timeout}ms`,
121
+ code: -1
122
+ });
123
+ }, timeout);
124
+ // Collect output
125
+ if (child.stdout) {
126
+ child.stdout.on('data', (data) => {
127
+ output += data.toString();
128
+ });
129
+ }
130
+ if (child.stderr) {
131
+ child.stderr.on('data', (data) => {
132
+ error += data.toString();
133
+ });
134
+ }
135
+ // Handle completion
136
+ child.on('close', (code) => {
137
+ clearTimeout(timer);
138
+ resolve({
139
+ success: code === 0,
140
+ duration: Date.now() - startTime,
141
+ output,
142
+ error: error || undefined,
143
+ code: code || 0
144
+ });
145
+ });
146
+ child.on('error', (err) => {
147
+ clearTimeout(timer);
148
+ resolve({
149
+ success: false,
150
+ duration: Date.now() - startTime,
151
+ output,
152
+ error: err.message,
153
+ code: -1
154
+ });
155
+ });
156
+ });
157
+ }
158
+ /**
159
+ * Execute Docker Compose command
160
+ */
161
+ async executeCompose(action, timeout) {
162
+ const startTime = Date.now();
163
+ try {
164
+ if (action === 'up') {
165
+ await this.composeHelper.up(timeout);
166
+ }
167
+ else {
168
+ await this.composeHelper.down(timeout);
169
+ }
170
+ return {
171
+ success: true,
172
+ duration: Date.now() - startTime,
173
+ output: `Docker Compose ${action} completed successfully`
174
+ };
175
+ }
176
+ catch (error) {
177
+ return {
178
+ success: false,
179
+ duration: Date.now() - startTime,
180
+ output: '',
181
+ error: error instanceof Error ? error.message : 'Compose command failed'
182
+ };
183
+ }
184
+ }
185
+ /**
186
+ * Execute wait-on for service readiness
187
+ */
188
+ async executeWaitOn(target, timeout, method) {
189
+ const startTime = Date.now();
190
+ try {
191
+ if (method === 'http') {
192
+ await this.waitForHttp(target, timeout);
193
+ }
194
+ else {
195
+ await this.waitForTcp(target, timeout);
196
+ }
197
+ return {
198
+ success: true,
199
+ duration: Date.now() - startTime,
200
+ output: `Service ${target} is ready (${method})`
201
+ };
202
+ }
203
+ catch (error) {
204
+ return {
205
+ success: false,
206
+ duration: Date.now() - startTime,
207
+ output: '',
208
+ error: error instanceof Error ? error.message : 'Wait-on failed'
209
+ };
210
+ }
211
+ }
212
+ /**
213
+ * Wait for HTTP endpoint to be ready
214
+ */
215
+ async waitForHttp(url, timeout) {
216
+ const startTime = Date.now();
217
+ const interval = 1000; // Check every second
218
+ while (Date.now() - startTime < timeout) {
219
+ try {
220
+ const response = await fetch(url, {
221
+ method: 'GET',
222
+ signal: AbortSignal.timeout(5000) // 5s per request
223
+ });
224
+ if (response.ok) {
225
+ return; // Success
226
+ }
227
+ }
228
+ catch (error) {
229
+ // Continue waiting
230
+ }
231
+ await new Promise(resolve => setTimeout(resolve, interval));
232
+ }
233
+ throw new Error(`HTTP endpoint ${url} not ready after ${timeout}ms`);
234
+ }
235
+ /**
236
+ * Wait for TCP port to be ready
237
+ */
238
+ async waitForTcp(target, timeout) {
239
+ const [host, portStr] = target.split(':');
240
+ const port = parseInt(portStr, 10);
241
+ if (!host || !port) {
242
+ throw new Error(`Invalid TCP target format: ${target} (expected host:port)`);
243
+ }
244
+ const startTime = Date.now();
245
+ const interval = 1000;
246
+ while (Date.now() - startTime < timeout) {
247
+ try {
248
+ const net = await import('net');
249
+ const socket = new net.Socket();
250
+ await new Promise((resolve, reject) => {
251
+ socket.setTimeout(2000);
252
+ socket.on('connect', () => {
253
+ socket.destroy();
254
+ resolve();
255
+ });
256
+ socket.on('timeout', () => {
257
+ socket.destroy();
258
+ reject(new Error('Connection timeout'));
259
+ });
260
+ socket.on('error', reject);
261
+ socket.connect(port, host);
262
+ });
263
+ return; // Success
264
+ }
265
+ catch (error) {
266
+ // Continue waiting
267
+ }
268
+ await new Promise(resolve => setTimeout(resolve, interval));
269
+ }
270
+ throw new Error(`TCP port ${target} not ready after ${timeout}ms`);
271
+ }
272
+ /**
273
+ * Get human-readable description of hook action
274
+ */
275
+ getHookDescription(hook) {
276
+ if ('run' in hook) {
277
+ return `Run: ${hook.run.substring(0, 50)}${hook.run.length > 50 ? '...' : ''}`;
278
+ }
279
+ else if ('compose' in hook) {
280
+ return `Compose: ${hook.compose}`;
281
+ }
282
+ else if ('wait_on' in hook) {
283
+ return `Wait-on: ${hook.wait_on} (${hook.method || 'http'})`;
284
+ }
285
+ return 'Unknown action';
286
+ }
287
+ /**
288
+ * Execute all lifecycle hooks in order
289
+ */
290
+ async executeLifecycle() {
291
+ const results = {
292
+ beforeAll: await this.executeHooks('beforeAll'),
293
+ beforeEach: await this.executeHooks('beforeEach'),
294
+ afterEach: await this.executeHooks('afterEach'),
295
+ afterAll: await this.executeHooks('afterAll')
296
+ };
297
+ // Summary
298
+ const totalHooks = Object.values(results).flat().length;
299
+ const successfulHooks = Object.values(results).flat().filter(r => r.success).length;
300
+ console.log(chalk.blue(`\nšŸ”— Hooks Summary: ${successfulHooks}/${totalHooks} successful`));
301
+ return results;
302
+ }
303
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * QA360 Core Engine
3
+ *
4
+ * The heart of QA360 - handles proof generation, cryptographic signatures,
5
+ * and evidence vault for verifiable quality assurance.
6
+ */
7
+ export interface Pack {
8
+ name: string;
9
+ version: string;
10
+ description?: string;
11
+ adapters: string[];
12
+ tests: TestSpec[];
13
+ hooks?: PackHooks;
14
+ }
15
+ export interface TestSpec {
16
+ name: string;
17
+ adapter: string;
18
+ config: Record<string, any>;
19
+ }
20
+ export interface PackHooks {
21
+ beforeAll?: string[];
22
+ afterAll?: string[];
23
+ beforeEach?: string[];
24
+ afterEach?: string[];
25
+ }
26
+ export interface Proof {
27
+ id: string;
28
+ pack: Pack;
29
+ results: TestResult[];
30
+ signature: string;
31
+ trustScore: number;
32
+ timestamp: string;
33
+ metadata: ProofMetadata;
34
+ }
35
+ export interface TestResult {
36
+ testName: string;
37
+ adapter: string;
38
+ status: 'passed' | 'failed' | 'skipped';
39
+ duration: number;
40
+ evidence: Evidence[];
41
+ error?: string;
42
+ }
43
+ export interface Evidence {
44
+ type: 'screenshot' | 'log' | 'metric' | 'artifact';
45
+ data: string | Buffer;
46
+ metadata: Record<string, any>;
47
+ }
48
+ export interface ProofMetadata {
49
+ environment: string;
50
+ platform: string;
51
+ qa360Version: string;
52
+ generatedBy: string;
53
+ }
54
+ export declare class QA360Core {
55
+ constructor();
56
+ generateProof(pack: Pack, results: TestResult[]): Promise<Proof>;
57
+ verifyProof(proof: Proof): Promise<boolean>;
58
+ calculateTrustScore(results: TestResult[]): Promise<number>;
59
+ }
60
+ export declare const VERSION = "0.9.0-core";
61
+ export * from './proof/index.js';
62
+ export { EvidenceVault } from './vault/index.js';
63
+ export type { RunRecord, GateRecord, FindingRecord } from './vault/index.js';
64
+ export { SecurityRedactor } from './security/redactor.js';
65
+ export { SecretsManager } from './secrets/manager.js';
66
+ export { SecretsCrypto } from './secrets/crypto.js';
67
+ export { PackValidator } from './pack/validator.js';
68
+ export { PackMigrator } from './pack/migrator.js';
69
+ export type { PackConfigV1 } from './types/pack-v1.js';
70
+ export { QA360Server } from './serve/server.js';
71
+ export type { ServeConfig } from './serve/server.js';
72
+ export { Phase3Runner } from './runner/phase3-runner.js';
73
+ export type { Phase3RunnerOptions, Phase3RunResult, GateResult } from './runner/phase3-runner.js';
74
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"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"}