@fromeroc9/testform 1.0.2 → 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 (50) hide show
  1. package/dist/action/index.js +1 -1
  2. package/dist/action.js +60 -0
  3. package/dist/adapters/github.js +467 -0
  4. package/dist/adapters/resources.js +363 -0
  5. package/dist/cli/index.js +3 -3
  6. package/dist/commands/apply.js +390 -0
  7. package/dist/commands/destroy.js +85 -0
  8. package/dist/commands/diff.js +131 -0
  9. package/dist/commands/fmt.js +166 -0
  10. package/dist/commands/force-unlock.js +55 -0
  11. package/dist/commands/generate.js +143 -0
  12. package/dist/commands/graph.js +159 -0
  13. package/dist/commands/import.js +222 -0
  14. package/dist/commands/init.js +167 -0
  15. package/dist/commands/login.js +71 -0
  16. package/dist/commands/logout.js +20 -0
  17. package/dist/commands/plan.js +250 -0
  18. package/dist/commands/refresh.js +165 -0
  19. package/dist/commands/report.js +724 -0
  20. package/dist/commands/show.js +61 -0
  21. package/dist/commands/state.js +197 -0
  22. package/dist/commands/taint.js +49 -0
  23. package/dist/commands/validate.js +128 -0
  24. package/dist/commands/workspace.js +102 -0
  25. package/dist/const.js +105 -0
  26. package/dist/core/backends/azurerm.js +201 -0
  27. package/dist/core/backends/backend.js +2 -0
  28. package/dist/core/backends/gcs.js +200 -0
  29. package/dist/core/backends/local.js +162 -0
  30. package/dist/core/backends/s3.js +224 -0
  31. package/dist/core/command-context.js +59 -0
  32. package/dist/core/config.js +131 -0
  33. package/dist/core/credentials.js +53 -0
  34. package/dist/core/parser.js +62 -0
  35. package/dist/core/parsers/base-parser.js +215 -0
  36. package/dist/core/parsers/testcase-parser.js +115 -0
  37. package/dist/core/parsers/testplan-parser.js +41 -0
  38. package/dist/core/parsers/testrun-parser.js +43 -0
  39. package/dist/core/policy.js +341 -0
  40. package/dist/core/prompt.js +109 -0
  41. package/dist/core/state.js +185 -0
  42. package/dist/core/utils.js +94 -0
  43. package/dist/core/variables.js +108 -0
  44. package/dist/core/workspace.js +56 -0
  45. package/dist/help.js +797 -0
  46. package/dist/index.js +650 -0
  47. package/dist/logger.js +134 -0
  48. package/dist/notify.js +36 -0
  49. package/dist/types.js +2 -0
  50. package/package.json +2 -2
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Pure utility functions shared across TestForm commands.
4
+ *
5
+ * Small, stateless helpers that eliminate common one-liner duplications
6
+ * found across command files.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.formatHclValue = void 0;
10
+ exports.elapsedSeconds = elapsedSeconds;
11
+ exports.formatIdentityDisplay = formatIdentityDisplay;
12
+ exports.formatResourceAddress = formatResourceAddress;
13
+ /**
14
+ * Calculates seconds elapsed since a given timestamp and returns a
15
+ * human-readable string (e.g. `"3"`).
16
+ *
17
+ * @param startTime - The start timestamp as returned by `Date.now()`.
18
+ * @returns Elapsed seconds as a fixed-point string with no decimals.
19
+ *
20
+ * @example
21
+ * const start = Date.now();
22
+ * // ... async work ...
23
+ * console.log(`Done in ${elapsedSeconds(start)}s`);
24
+ */
25
+ function elapsedSeconds(startTime) {
26
+ return ((Date.now() - startTime) / 1000).toFixed(0);
27
+ }
28
+ function formatIdentityDisplay(identity) {
29
+ const parts = identity.split('::');
30
+ if (parts.length > 0) {
31
+ let filePath = parts[0];
32
+ const lastSlash = Math.max(filePath.lastIndexOf('/'), filePath.lastIndexOf('\\'));
33
+ if (lastSlash !== -1) {
34
+ filePath = filePath.substring(lastSlash + 1);
35
+ }
36
+ const lastDot = filePath.lastIndexOf('.');
37
+ if (lastDot !== -1) {
38
+ filePath = filePath.substring(0, lastDot);
39
+ }
40
+ parts[0] = filePath;
41
+ return parts.join('::');
42
+ }
43
+ return identity;
44
+ }
45
+ /**
46
+ * Formats a resource address string.
47
+ *
48
+ * @param type - The resource type (e.g. `'github_testcase'`).
49
+ * @param identity - The resource identity (e.g. `'tc1.case.feature::@[tc1]'`).
50
+ * @returns A formatted address like `"github_testcase.tc1::@[tc1]"`.
51
+ */
52
+ function formatResourceAddress(type, identity) {
53
+ return `${type}.${formatIdentityDisplay(identity)}`;
54
+ }
55
+ const formatHclValue = (value, indentLevel) => {
56
+ const indent = ' '.repeat(indentLevel);
57
+ if (value === null || value === undefined)
58
+ return 'null';
59
+ if (typeof value === 'string') {
60
+ if (value.includes('\n')) {
61
+ const paddedValue = value.split('\n').map(line => line ? `${indent} ${line}` : `${indent} `).join('\n');
62
+ return `<<-EOT\n${paddedValue}\n${indent}EOT`;
63
+ }
64
+ return JSON.stringify(value);
65
+ }
66
+ if (Array.isArray(value)) {
67
+ if (value.length === 0)
68
+ return '[]';
69
+ const items = value.map(v => (0, exports.formatHclValue)(v, indentLevel + 1));
70
+ return `[\n${indent} ${items.join(`,\n${indent} `)},\n${indent}]`;
71
+ }
72
+ if (typeof value === 'object') {
73
+ const keys = Object.keys(value);
74
+ if (keys.length === 0)
75
+ return '{}';
76
+ let maxKeyLen = 0;
77
+ for (const k of keys) {
78
+ const strK = JSON.stringify(k);
79
+ if (strK.length > maxKeyLen)
80
+ maxKeyLen = strK.length;
81
+ }
82
+ let out = `{\n`;
83
+ for (const k of keys) {
84
+ const strK = JSON.stringify(k);
85
+ const padding = ' '.repeat(maxKeyLen - strK.length);
86
+ const val = Object.prototype.hasOwnProperty.call(value, k) ? value[k] : undefined;
87
+ out += `${indent} ${strK}${padding} = ${(0, exports.formatHclValue)(val, indentLevel + 1)}\n`;
88
+ }
89
+ out += `${indent}}`;
90
+ return out;
91
+ }
92
+ return JSON.stringify(value);
93
+ };
94
+ exports.formatHclValue = formatHclValue;
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VariableParser = void 0;
4
+ const fs_1 = require("fs");
5
+ const notify_1 = require("../notify");
6
+ class VariableParser {
7
+ vars = {};
8
+ workDir;
9
+ constructor(varArgs, varFileArgs, workDir = '.') {
10
+ this.workDir = workDir;
11
+ this.parseVarFiles(varFileArgs);
12
+ this.parseVars(varArgs);
13
+ }
14
+ parseVars(varArgs) {
15
+ if (!varArgs)
16
+ return;
17
+ const items = Array.isArray(varArgs) ? varArgs : [varArgs];
18
+ for (const item of items) {
19
+ const idx = item.indexOf('=');
20
+ if (idx === -1) {
21
+ notify_1.notify.push({
22
+ type: 'warning',
23
+ title: `Invalid variable format: ${item}`,
24
+ detail: ['Variables must be in the format key=value']
25
+ });
26
+ continue;
27
+ }
28
+ const key = item.substring(0, idx).trim();
29
+ const value = item.substring(idx + 1).trim();
30
+ this.vars[key] = value;
31
+ }
32
+ }
33
+ parseVarFiles(varFileArgs) {
34
+ if (!varFileArgs)
35
+ return;
36
+ const items = Array.isArray(varFileArgs) ? varFileArgs : [varFileArgs];
37
+ for (let filePath of items) {
38
+ const path = require('path');
39
+ filePath = path.resolve(this.workDir, filePath);
40
+ if (!(0, fs_1.existsSync)(filePath)) {
41
+ notify_1.notify.push({
42
+ type: 'error',
43
+ title: `Variable file not found: ${filePath}`,
44
+ detail: [],
45
+ close: true
46
+ });
47
+ process.exit(1);
48
+ }
49
+ try {
50
+ const content = (0, fs_1.readFileSync)(filePath, 'utf-8');
51
+ let fileVars = {};
52
+ if (filePath.endsWith('.json')) {
53
+ fileVars = JSON.parse(content);
54
+ }
55
+ else {
56
+ // Simple tfvars format parser (key = "value")
57
+ const lines = content.split('\n');
58
+ for (const line of lines) {
59
+ const trimmed = line.trim();
60
+ if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('//'))
61
+ continue;
62
+ const idx = trimmed.indexOf('=');
63
+ if (idx !== -1) {
64
+ const key = trimmed.substring(0, idx).trim();
65
+ let value = trimmed.substring(idx + 1).trim();
66
+ // Remove surrounding quotes if present
67
+ if (value.startsWith('"') && value.endsWith('"')) {
68
+ value = value.substring(1, value.length - 1);
69
+ }
70
+ else if (value.startsWith("'") && value.endsWith("'")) {
71
+ value = value.substring(1, value.length - 1);
72
+ }
73
+ fileVars[key] = value;
74
+ }
75
+ }
76
+ }
77
+ // Merge into existing vars
78
+ this.vars = { ...this.vars, ...fileVars };
79
+ }
80
+ catch (error) {
81
+ notify_1.notify.push({
82
+ type: 'error',
83
+ title: `Failed to parse variable file: ${filePath}`,
84
+ detail: [error.message],
85
+ close: true
86
+ });
87
+ process.exit(1);
88
+ }
89
+ }
90
+ }
91
+ getVars() {
92
+ return this.vars;
93
+ }
94
+ /**
95
+ * Replaces ${var.name} in the given template string
96
+ * with the corresponding values from the variables.
97
+ */
98
+ applyToTemplate(template) {
99
+ let result = template;
100
+ // Match ${var.NAME}
101
+ const regex = /\$\{var\.([a-zA-Z0-9_-]+)\}/g;
102
+ result = result.replace(regex, (match, key) => {
103
+ return this.vars[key] !== undefined ? this.vars[key] : match;
104
+ });
105
+ return result;
106
+ }
107
+ }
108
+ exports.VariableParser = VariableParser;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WorkspaceManager = void 0;
4
+ const path_1 = require("path");
5
+ const fs_1 = require("fs");
6
+ class WorkspaceManager {
7
+ envPath;
8
+ statePath;
9
+ constructor(dir) {
10
+ const testformDir = (0, path_1.join)(dir, '.testform');
11
+ if (!(0, fs_1.existsSync)(testformDir)) {
12
+ (0, fs_1.mkdirSync)(testformDir, { recursive: true });
13
+ }
14
+ this.envPath = (0, path_1.join)(testformDir, 'environment');
15
+ this.statePath = (0, path_1.join)(testformDir, 'testform.state');
16
+ }
17
+ getCurrentWorkspace() {
18
+ if ((0, fs_1.existsSync)(this.envPath) && (0, fs_1.statSync)(this.envPath).isFile()) {
19
+ const content = (0, fs_1.readFileSync)(this.envPath, 'utf8').trim();
20
+ if (content.length > 0) {
21
+ return content;
22
+ }
23
+ }
24
+ return 'default';
25
+ }
26
+ setCurrentWorkspace(name) {
27
+ (0, fs_1.writeFileSync)(this.envPath, name, 'utf8');
28
+ }
29
+ getActiveBackend() {
30
+ if ((0, fs_1.existsSync)(this.statePath) && (0, fs_1.statSync)(this.statePath).isFile()) {
31
+ try {
32
+ const content = (0, fs_1.readFileSync)(this.statePath, 'utf8');
33
+ const parsed = JSON.parse(content);
34
+ return parsed.backend || null;
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ }
40
+ return null;
41
+ }
42
+ setActiveBackend(backend) {
43
+ let stateObj = { version: 3 };
44
+ if ((0, fs_1.existsSync)(this.statePath) && (0, fs_1.statSync)(this.statePath).isFile()) {
45
+ try {
46
+ stateObj = JSON.parse((0, fs_1.readFileSync)(this.statePath, 'utf8'));
47
+ }
48
+ catch {
49
+ // Ignore parse errors, just overwrite
50
+ }
51
+ }
52
+ stateObj.backend = backend;
53
+ (0, fs_1.writeFileSync)(this.statePath, JSON.stringify(stateObj, null, 2), 'utf8');
54
+ }
55
+ }
56
+ exports.WorkspaceManager = WorkspaceManager;