@factorialco/fcode-cli 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 (234) hide show
  1. package/README.md +1021 -0
  2. package/bin/dev +17 -0
  3. package/bin/dev.cmd +3 -0
  4. package/bin/run +7 -0
  5. package/bin/run.cmd +3 -0
  6. package/dist/commands/add.d.ts +6 -0
  7. package/dist/commands/add.js +22 -0
  8. package/dist/commands/clone.d.ts +26 -0
  9. package/dist/commands/clone.js +177 -0
  10. package/dist/commands/dependencies/add.d.ts +9 -0
  11. package/dist/commands/dependencies/add.js +13 -0
  12. package/dist/commands/dependencies/install.d.ts +14 -0
  13. package/dist/commands/dependencies/install.js +44 -0
  14. package/dist/commands/dependencies/pull.d.ts +12 -0
  15. package/dist/commands/dependencies/pull.js +14 -0
  16. package/dist/commands/dependencies/push.d.ts +12 -0
  17. package/dist/commands/dependencies/push.js +14 -0
  18. package/dist/commands/dependencies/reset.d.ts +9 -0
  19. package/dist/commands/dependencies/reset.js +13 -0
  20. package/dist/commands/dependencies/status.d.ts +9 -0
  21. package/dist/commands/dependencies/status.js +13 -0
  22. package/dist/commands/http.d.ts +22 -0
  23. package/dist/commands/http.js +272 -0
  24. package/dist/commands/login.d.ts +18 -0
  25. package/dist/commands/login.js +62 -0
  26. package/dist/commands/logout.d.ts +10 -0
  27. package/dist/commands/logout.js +34 -0
  28. package/dist/commands/modules/add.d.ts +9 -0
  29. package/dist/commands/modules/add.js +13 -0
  30. package/dist/commands/modules/pull.d.ts +13 -0
  31. package/dist/commands/modules/pull.js +43 -0
  32. package/dist/commands/modules/push.d.ts +13 -0
  33. package/dist/commands/modules/push.js +35 -0
  34. package/dist/commands/modules/remove.d.ts +9 -0
  35. package/dist/commands/modules/remove.js +19 -0
  36. package/dist/commands/modules/reset.d.ts +9 -0
  37. package/dist/commands/modules/reset.js +13 -0
  38. package/dist/commands/modules/status.d.ts +11 -0
  39. package/dist/commands/modules/status.js +40 -0
  40. package/dist/commands/pre-debug.d.ts +20 -0
  41. package/dist/commands/pre-debug.js +120 -0
  42. package/dist/commands/process-metadata.d.ts +18 -0
  43. package/dist/commands/process-metadata.js +63 -0
  44. package/dist/commands/processes/add.d.ts +9 -0
  45. package/dist/commands/processes/add.js +13 -0
  46. package/dist/commands/processes/install-dependencies.d.ts +14 -0
  47. package/dist/commands/processes/install-dependencies.js +30 -0
  48. package/dist/commands/processes/pull.d.ts +13 -0
  49. package/dist/commands/processes/pull.js +47 -0
  50. package/dist/commands/processes/push.d.ts +13 -0
  51. package/dist/commands/processes/push.js +39 -0
  52. package/dist/commands/processes/remove.d.ts +9 -0
  53. package/dist/commands/processes/remove.js +19 -0
  54. package/dist/commands/processes/reset.d.ts +9 -0
  55. package/dist/commands/processes/reset.js +13 -0
  56. package/dist/commands/processes/status.d.ts +11 -0
  57. package/dist/commands/processes/status.js +58 -0
  58. package/dist/commands/pull.d.ts +9 -0
  59. package/dist/commands/pull.js +32 -0
  60. package/dist/commands/push.d.ts +9 -0
  61. package/dist/commands/push.js +29 -0
  62. package/dist/commands/remote/add.d.ts +10 -0
  63. package/dist/commands/remote/add.js +32 -0
  64. package/dist/commands/remote/list.d.ts +7 -0
  65. package/dist/commands/remote/list.js +33 -0
  66. package/dist/commands/remote/set.d.ts +10 -0
  67. package/dist/commands/remote/set.js +28 -0
  68. package/dist/commands/reset.d.ts +6 -0
  69. package/dist/commands/reset.js +19 -0
  70. package/dist/commands/run.d.ts +16 -0
  71. package/dist/commands/run.js +56 -0
  72. package/dist/commands/runtime-env.d.ts +12 -0
  73. package/dist/commands/runtime-env.js +42 -0
  74. package/dist/commands/settings/get.d.ts +12 -0
  75. package/dist/commands/settings/get.js +67 -0
  76. package/dist/commands/settings/remove.d.ts +9 -0
  77. package/dist/commands/settings/remove.js +35 -0
  78. package/dist/commands/settings/set.d.ts +10 -0
  79. package/dist/commands/settings/set.js +39 -0
  80. package/dist/commands/setup-debug.d.ts +12 -0
  81. package/dist/commands/setup-debug.js +118 -0
  82. package/dist/commands/status.d.ts +6 -0
  83. package/dist/commands/status.js +22 -0
  84. package/dist/commands/team/pull.d.ts +8 -0
  85. package/dist/commands/team/pull.js +23 -0
  86. package/dist/commands/team/push.d.ts +11 -0
  87. package/dist/commands/team/push.js +41 -0
  88. package/dist/commands/team/status.d.ts +8 -0
  89. package/dist/commands/team/status.js +40 -0
  90. package/dist/commands/test/scaffold.d.ts +9 -0
  91. package/dist/commands/test/scaffold.js +61 -0
  92. package/dist/commands/test.d.ts +13 -0
  93. package/dist/commands/test.js +72 -0
  94. package/dist/commands/variables/add.d.ts +12 -0
  95. package/dist/commands/variables/add.js +21 -0
  96. package/dist/commands/variables/pull.d.ts +12 -0
  97. package/dist/commands/variables/pull.js +14 -0
  98. package/dist/commands/variables/push.d.ts +12 -0
  99. package/dist/commands/variables/push.js +14 -0
  100. package/dist/commands/variables/remove.d.ts +9 -0
  101. package/dist/commands/variables/remove.js +19 -0
  102. package/dist/commands/variables/reset.d.ts +9 -0
  103. package/dist/commands/variables/reset.js +13 -0
  104. package/dist/commands/variables/status.d.ts +12 -0
  105. package/dist/commands/variables/status.js +58 -0
  106. package/dist/config/global.d.ts +15 -0
  107. package/dist/config/global.js +77 -0
  108. package/dist/config/index.d.ts +14 -0
  109. package/dist/config/index.js +44 -0
  110. package/dist/config/remote.d.ts +15 -0
  111. package/dist/config/remote.js +60 -0
  112. package/dist/config/settings.d.ts +16 -0
  113. package/dist/config/settings.js +54 -0
  114. package/dist/config/workspace.d.ts +28 -0
  115. package/dist/config/workspace.js +158 -0
  116. package/dist/core/commands/api-client-command.d.ts +6 -0
  117. package/dist/core/commands/api-client-command.js +30 -0
  118. package/dist/core/commands/base-command.d.ts +11 -0
  119. package/dist/core/commands/base-command.js +61 -0
  120. package/dist/core/commands/dependency-command.d.ts +5 -0
  121. package/dist/core/commands/dependency-command.js +16 -0
  122. package/dist/core/commands/modules-command.d.ts +7 -0
  123. package/dist/core/commands/modules-command.js +18 -0
  124. package/dist/core/commands/processes-command.d.ts +8 -0
  125. package/dist/core/commands/processes-command.js +27 -0
  126. package/dist/core/commands/resource-command.d.ts +24 -0
  127. package/dist/core/commands/resource-command.js +92 -0
  128. package/dist/core/commands/variables-command.d.ts +5 -0
  129. package/dist/core/commands/variables-command.js +16 -0
  130. package/dist/core/dependencies/env-dependencies-command.d.ts +19 -0
  131. package/dist/core/dependencies/env-dependencies-command.js +70 -0
  132. package/dist/core/dependencies/env-process-dependencies-command.d.ts +15 -0
  133. package/dist/core/dependencies/env-process-dependencies-command.js +78 -0
  134. package/dist/core/dependencies/javascript-env-dependencies.d.ts +8 -0
  135. package/dist/core/dependencies/javascript-env-dependencies.js +77 -0
  136. package/dist/core/dependencies/python-env-dependencies.d.ts +9 -0
  137. package/dist/core/dependencies/python-env-dependencies.js +91 -0
  138. package/dist/core/errors.d.ts +55 -0
  139. package/dist/core/errors.js +144 -0
  140. package/dist/core/executors/execution-task.d.ts +29 -0
  141. package/dist/core/executors/execution-task.js +139 -0
  142. package/dist/core/executors/javascript-execution-task.d.ts +13 -0
  143. package/dist/core/executors/javascript-execution-task.js +618 -0
  144. package/dist/core/executors/python/context.d.ts +2 -0
  145. package/dist/core/executors/python/context.js +3 -0
  146. package/dist/core/executors/python/datastore.d.ts +2 -0
  147. package/dist/core/executors/python/datastore.js +4 -0
  148. package/dist/core/executors/python-execution-task.d.ts +11 -0
  149. package/dist/core/executors/python-execution-task.js +173 -0
  150. package/dist/core/executors/source-code-sanitizer.d.ts +4 -0
  151. package/dist/core/executors/source-code-sanitizer.js +45 -0
  152. package/dist/core/resource-merger.d.ts +14 -0
  153. package/dist/core/resource-merger.js +155 -0
  154. package/dist/core/resource-pusher.d.ts +12 -0
  155. package/dist/core/resource-pusher.js +133 -0
  156. package/dist/core/resource-repository.d.ts +8 -0
  157. package/dist/core/resource-repository.js +2 -0
  158. package/dist/core/types.d.ts +290 -0
  159. package/dist/core/types.js +238 -0
  160. package/dist/core/utils/dependencies.d.ts +5 -0
  161. package/dist/core/utils/dependencies.js +82 -0
  162. package/dist/core/utils/variables.d.ts +4 -0
  163. package/dist/core/utils/variables.js +10 -0
  164. package/dist/hooks/init/auth.d.ts +3 -0
  165. package/dist/hooks/init/auth.js +28 -0
  166. package/dist/hooks/init/update.d.ts +3 -0
  167. package/dist/hooks/init/update.js +42 -0
  168. package/dist/index.d.ts +1 -0
  169. package/dist/index.js +4 -0
  170. package/dist/repositories/process.d.ts +36 -0
  171. package/dist/repositories/process.js +339 -0
  172. package/dist/repositories/programming-language-manifest.d.ts +17 -0
  173. package/dist/repositories/programming-language-manifest.js +95 -0
  174. package/dist/repositories/resource.d.ts +29 -0
  175. package/dist/repositories/resource.js +79 -0
  176. package/dist/repositories/script-library.d.ts +28 -0
  177. package/dist/repositories/script-library.js +164 -0
  178. package/dist/repositories/team.d.ts +23 -0
  179. package/dist/repositories/team.js +74 -0
  180. package/dist/repositories/variable.d.ts +26 -0
  181. package/dist/repositories/variable.js +174 -0
  182. package/dist/runtime/executor.js +87 -0
  183. package/dist/runtime/executor.py +81 -0
  184. package/dist/runtime/libs/node/fcode/index.js +1 -0
  185. package/dist/runtime/libs/python/fcode/__init__.py +75 -0
  186. package/dist/runtime/libs/python/fcode/context.py +98 -0
  187. package/dist/runtime/libs/python/fcode/datastore.py +35 -0
  188. package/dist/runtime/libs/python/fcode/schedule.py +45 -0
  189. package/dist/runtime/libs/python/fcode/storage.py +271 -0
  190. package/dist/runtime/libs/python/fcode/variables.py +102 -0
  191. package/dist/services/auth-service.d.ts +12 -0
  192. package/dist/services/auth-service.js +114 -0
  193. package/dist/services/credential-prompter.d.ts +6 -0
  194. package/dist/services/credential-prompter.js +60 -0
  195. package/dist/services/fcode-graphql-client.d.ts +45 -0
  196. package/dist/services/fcode-graphql-client.js +721 -0
  197. package/dist/services/http-server.d.ts +3 -0
  198. package/dist/services/http-server.js +17 -0
  199. package/dist/services/oauth-browser-service.d.ts +9 -0
  200. package/dist/services/oauth-browser-service.js +164 -0
  201. package/dist/services/process-parameters-sampler.d.ts +2 -0
  202. package/dist/services/process-parameters-sampler.js +41 -0
  203. package/dist/services/process-runner.d.ts +22 -0
  204. package/dist/services/process-runner.js +97 -0
  205. package/dist/services/resource-checker.d.ts +8 -0
  206. package/dist/services/resource-checker.js +82 -0
  207. package/dist/services/test-runner.d.ts +17 -0
  208. package/dist/services/test-runner.js +228 -0
  209. package/dist/utils/canonical-hash.d.ts +9 -0
  210. package/dist/utils/canonical-hash.js +30 -0
  211. package/dist/utils/cli/index.d.ts +14 -0
  212. package/dist/utils/cli/index.js +50 -0
  213. package/dist/utils/cli/screen.d.ts +2 -0
  214. package/dist/utils/cli/screen.js +25 -0
  215. package/dist/utils/cli/styled/table.d.ts +23 -0
  216. package/dist/utils/cli/styled/table.js +272 -0
  217. package/dist/utils/colorize-resource.d.ts +1 -0
  218. package/dist/utils/colorize-resource.js +7 -0
  219. package/dist/utils/env-utils.d.ts +2 -0
  220. package/dist/utils/env-utils.js +16 -0
  221. package/dist/utils/file-utils.d.ts +11 -0
  222. package/dist/utils/file-utils.js +33 -0
  223. package/dist/utils/filenamify.d.ts +4 -0
  224. package/dist/utils/filenamify.js +73 -0
  225. package/dist/utils/gitignore.d.ts +17 -0
  226. package/dist/utils/gitignore.js +60 -0
  227. package/dist/utils/promise.d.ts +4 -0
  228. package/dist/utils/promise.js +38 -0
  229. package/dist/utils/url-utils.d.ts +2 -0
  230. package/dist/utils/url-utils.js +10 -0
  231. package/dist/utils/variable-warnings.d.ts +8 -0
  232. package/dist/utils/variable-warnings.js +38 -0
  233. package/oclif.manifest.json +1783 -0
  234. package/package.json +129 -0
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runProcessTests = runProcessTests;
4
+ exports.printTestResults = printTestResults;
5
+ const fs = require("fs-extra");
6
+ const path = require("node:path");
7
+ const _ = require("lodash");
8
+ const color_1 = require("@oclif/color");
9
+ const cli_1 = require("../utils/cli");
10
+ const process_runner_1 = require("./process-runner");
11
+ const errors_1 = require("../core/errors");
12
+ const variable_1 = require("../repositories/variable");
13
+ function hooksFileName(programmingLanguage) {
14
+ return programmingLanguage === 'PYTHON' ? 'testHooks.py' : 'testHooks.js';
15
+ }
16
+ function discoverTests(processSlug, programmingLanguage) {
17
+ const testsDir = path.join(process.cwd(), 'processes', processSlug, 'tests');
18
+ if (!fs.existsSync(testsDir))
19
+ return null;
20
+ const hooksFile = hooksFileName(programmingLanguage);
21
+ const entries = fs.readdirSync(testsDir, { withFileTypes: true });
22
+ const testDirs = entries
23
+ .filter((e) => e.isDirectory())
24
+ .map((e) => e.name)
25
+ .sort();
26
+ return testDirs
27
+ .map((name) => {
28
+ const dir = path.join(testsDir, name);
29
+ const inputPath = path.join(dir, 'input.json');
30
+ if (!fs.existsSync(inputPath))
31
+ return null;
32
+ const outputCandidate = path.join(dir, 'output.json');
33
+ const errorCandidate = path.join(dir, 'error.json');
34
+ const variablesCandidate = path.join(dir, 'variables.test.env');
35
+ const hooksCandidate = path.join(dir, hooksFile);
36
+ return {
37
+ name,
38
+ inputPath,
39
+ outputPath: fs.existsSync(outputCandidate) ? outputCandidate : null,
40
+ errorPath: fs.existsSync(errorCandidate) ? errorCandidate : null,
41
+ variablesPath: fs.existsSync(variablesCandidate) ? variablesCandidate : null,
42
+ hooksFilePath: fs.existsSync(hooksCandidate) ? hooksCandidate : null,
43
+ };
44
+ })
45
+ .filter((tc) => tc !== null);
46
+ }
47
+ function getGlobalTestAssets(processSlug, programmingLanguage) {
48
+ const testsDir = path.join(process.cwd(), 'processes', processSlug, 'tests');
49
+ const hooksCandidate = path.join(testsDir, hooksFileName(programmingLanguage));
50
+ const variablesCandidate = path.join(testsDir, 'variables.test.env');
51
+ return {
52
+ hooksFilePath: fs.existsSync(hooksCandidate) ? hooksCandidate : null,
53
+ variablesPath: fs.existsSync(variablesCandidate) ? variablesCandidate : null,
54
+ };
55
+ }
56
+ async function runProcessTests(fcodeProcess, options = {}) {
57
+ const { logLevel = 'ERROR', jsonLogs = false } = options;
58
+ const { slug, script: { programmingLanguage } } = fcodeProcess;
59
+ const testCases = discoverTests(slug, programmingLanguage);
60
+ if (!testCases) {
61
+ return { processSlug: slug, results: [] };
62
+ }
63
+ const globalAssets = getGlobalTestAssets(slug, programmingLanguage);
64
+ const hookOptions = { logLevel, jsonLogs };
65
+ const variableRepository = new variable_1.default();
66
+ let baseVariables = await variableRepository.findAllForExecution();
67
+ if (globalAssets.variablesPath) {
68
+ baseVariables = (0, process_runner_1.mergeVariables)(baseVariables, (0, process_runner_1.loadVariablesFromFile)(globalAssets.variablesPath));
69
+ }
70
+ const results = [];
71
+ // Run global before hook
72
+ let globalBeforeFailed = false;
73
+ if (globalAssets.hooksFilePath) {
74
+ try {
75
+ await (0, process_runner_1.runHook)(globalAssets.hooksFilePath, fcodeProcess, baseVariables, { ...hookOptions, hookFunction: 'before' });
76
+ }
77
+ catch {
78
+ globalBeforeFailed = true;
79
+ }
80
+ }
81
+ for (const testCase of testCases) {
82
+ const start = Date.now();
83
+ if (globalBeforeFailed) {
84
+ results.push({ name: testCase.name, passed: false, error: 'Global before hook failed', durationMs: 0 });
85
+ continue;
86
+ }
87
+ // Build per-test variables
88
+ const testVariables = testCase.variablesPath
89
+ ? (0, process_runner_1.mergeVariables)(baseVariables, (0, process_runner_1.loadVariablesFromFile)(testCase.variablesPath))
90
+ : baseVariables;
91
+ // Misconfiguration guard
92
+ if (testCase.outputPath && testCase.errorPath) {
93
+ results.push({ name: testCase.name, passed: false, error: 'Test misconfiguration: output.json and error.json cannot coexist', durationMs: Date.now() - start });
94
+ continue;
95
+ }
96
+ // Parse input
97
+ let parameters;
98
+ try {
99
+ parameters = JSON.parse(fs.readFileSync(testCase.inputPath, 'utf8'));
100
+ }
101
+ catch {
102
+ results.push({ name: testCase.name, passed: false, error: 'Failed to parse input.json', durationMs: Date.now() - start });
103
+ continue;
104
+ }
105
+ // Per-test before hook
106
+ if (testCase.hooksFilePath) {
107
+ try {
108
+ await (0, process_runner_1.runHook)(testCase.hooksFilePath, fcodeProcess, testVariables, { ...hookOptions, hookFunction: 'before' });
109
+ }
110
+ catch {
111
+ results.push({ name: testCase.name, passed: false, error: 'Before hook failed', durationMs: Date.now() - start });
112
+ continue;
113
+ }
114
+ }
115
+ let passed = true;
116
+ let errorMsg;
117
+ if (testCase.errorPath) {
118
+ // Expected-error test: process must fail
119
+ let expectedMessage;
120
+ try {
121
+ const errorSpec = JSON.parse(fs.readFileSync(testCase.errorPath, 'utf8'));
122
+ expectedMessage = errorSpec?.message;
123
+ }
124
+ catch {
125
+ results.push({ name: testCase.name, passed: false, error: 'Failed to parse error.json', durationMs: Date.now() - start });
126
+ if (testCase.hooksFilePath) {
127
+ await (0, process_runner_1.runHook)(testCase.hooksFilePath, fcodeProcess, testVariables, { ...hookOptions, hookFunction: 'after' }).catch(() => { });
128
+ }
129
+ continue;
130
+ }
131
+ try {
132
+ await (0, process_runner_1.runProcess)({ slug, parameters, variablesOverride: testVariables, logLevel, jsonLogs, suppressElapsedLog: true });
133
+ passed = false;
134
+ errorMsg = 'Expected process to fail but it succeeded';
135
+ }
136
+ catch (runError) {
137
+ const actualMessage = runError instanceof errors_1.ErrorExecutingProcess ? runError.message : String(runError);
138
+ if (expectedMessage && !actualMessage.includes(expectedMessage)) {
139
+ passed = false;
140
+ errorMsg = `Error message mismatch\n Expected to contain: ${expectedMessage}\n Received: ${actualMessage}`;
141
+ }
142
+ }
143
+ }
144
+ else {
145
+ // Normal test: process must succeed
146
+ let actualOutput;
147
+ try {
148
+ actualOutput = await (0, process_runner_1.runProcess)({ slug, parameters, variablesOverride: testVariables, logLevel, jsonLogs, suppressElapsedLog: true });
149
+ }
150
+ catch (error) {
151
+ passed = false;
152
+ errorMsg = `Process execution failed: ${error instanceof Error ? error.message : String(error)}`;
153
+ if (testCase.hooksFilePath) {
154
+ await (0, process_runner_1.runHook)(testCase.hooksFilePath, fcodeProcess, testVariables, { ...hookOptions, hookFunction: 'after' }).catch(() => { });
155
+ }
156
+ results.push({ name: testCase.name, passed, error: errorMsg, durationMs: Date.now() - start });
157
+ continue;
158
+ }
159
+ if (testCase.outputPath) {
160
+ try {
161
+ const expected = JSON.parse(fs.readFileSync(testCase.outputPath, 'utf8'));
162
+ if (!_.isEqual(actualOutput, expected)) {
163
+ passed = false;
164
+ errorMsg = `Expected: ${JSON.stringify(expected)}\n Received: ${JSON.stringify(actualOutput)}`;
165
+ }
166
+ }
167
+ catch {
168
+ passed = false;
169
+ errorMsg = 'Failed to parse output.json';
170
+ }
171
+ }
172
+ }
173
+ results.push({ name: testCase.name, passed, error: errorMsg, durationMs: Date.now() - start });
174
+ // Per-test after hook
175
+ if (testCase.hooksFilePath) {
176
+ await (0, process_runner_1.runHook)(testCase.hooksFilePath, fcodeProcess, testVariables, { ...hookOptions, hookFunction: 'after' }).catch(() => { });
177
+ }
178
+ }
179
+ // Global after hook
180
+ if (globalAssets.hooksFilePath) {
181
+ await (0, process_runner_1.runHook)(globalAssets.hooksFilePath, fcodeProcess, baseVariables, { ...hookOptions, hookFunction: 'after' }).catch(() => { });
182
+ }
183
+ return { processSlug: slug, results };
184
+ }
185
+ function printTestResults(suiteResults) {
186
+ const nonEmpty = suiteResults.filter((s) => s.results.length > 0);
187
+ for (const suite of nonEmpty) {
188
+ const total = suite.results.length;
189
+ const passed = suite.results.filter((r) => r.passed).length;
190
+ cli_1.cli.log(`\nTesting ${color_1.default.bold(suite.processSlug)} (${total} test${total === 1 ? '' : 's'})`);
191
+ for (const result of suite.results) {
192
+ if (result.passed) {
193
+ cli_1.cli.log(` ${color_1.default.green('✓')} ${result.name} (${result.durationMs}ms)`);
194
+ }
195
+ else {
196
+ cli_1.cli.log(` ${color_1.default.red('✗')} ${result.name}`);
197
+ if (result.error) {
198
+ for (const line of result.error.split('\n')) {
199
+ cli_1.cli.log(` ${line}`);
200
+ }
201
+ }
202
+ }
203
+ }
204
+ const failed = total - passed;
205
+ if (failed === 0) {
206
+ cli_1.cli.log(`\n ${color_1.default.green(`${passed} passed`)}`);
207
+ }
208
+ else {
209
+ cli_1.cli.log(`\n ${color_1.default.green(`${passed} passed`)}, ${color_1.default.red(`${failed} failed`)}`);
210
+ }
211
+ }
212
+ if (suiteResults.length === 0)
213
+ return;
214
+ const totalTests = suiteResults.reduce((sum, s) => sum + s.results.length, 0);
215
+ if (totalTests === 0)
216
+ return;
217
+ const totalPassed = suiteResults.reduce((sum, s) => sum + s.results.filter((r) => r.passed).length, 0);
218
+ const totalFailed = totalTests - totalPassed;
219
+ if (nonEmpty.length > 1) {
220
+ cli_1.cli.log(`\n${'─'.repeat(48)}`);
221
+ if (totalFailed === 0) {
222
+ cli_1.cli.log(`Total: ${totalTests} test${totalTests === 1 ? '' : 's'} — ${color_1.default.green(`${totalPassed} passed`)}`);
223
+ }
224
+ else {
225
+ cli_1.cli.log(`Total: ${totalTests} test${totalTests === 1 ? '' : 's'} — ${color_1.default.green(`${totalPassed} passed`)}, ${color_1.default.red(`${totalFailed} failed`)}`);
226
+ }
227
+ }
228
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Serializes a value with object keys sorted and no insignificant whitespace, so
3
+ * that two values that differ only in formatting (indentation, key order, trailing
4
+ * newline) produce an identical string. Used to hash metadata files independently
5
+ * of how they happen to be formatted on disk.
6
+ */
7
+ export declare function canonicalStringify(value: unknown): string;
8
+ /** MD5 of the canonical serialization of `value` (see {@link canonicalStringify}). */
9
+ export declare function canonicalJsonHash(value: unknown): string;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.canonicalStringify = canonicalStringify;
4
+ exports.canonicalJsonHash = canonicalJsonHash;
5
+ const crypto = require("node:crypto");
6
+ /**
7
+ * Serializes a value with object keys sorted and no insignificant whitespace, so
8
+ * that two values that differ only in formatting (indentation, key order, trailing
9
+ * newline) produce an identical string. Used to hash metadata files independently
10
+ * of how they happen to be formatted on disk.
11
+ */
12
+ function canonicalStringify(value) {
13
+ if (value === null || value === undefined) {
14
+ return 'null';
15
+ }
16
+ if (Array.isArray(value)) {
17
+ return `[${value.map((item) => canonicalStringify(item)).join(',')}]`;
18
+ }
19
+ if (typeof value === 'object') {
20
+ const entries = Object.keys(value)
21
+ .sort()
22
+ .map((key) => `${JSON.stringify(key)}:${canonicalStringify(value[key])}`);
23
+ return `{${entries.join(',')}}`;
24
+ }
25
+ return JSON.stringify(value);
26
+ }
27
+ /** MD5 of the canonical serialization of `value` (see {@link canonicalStringify}). */
28
+ function canonicalJsonHash(value) {
29
+ return crypto.createHash('md5').update(canonicalStringify(value)).digest('hex');
30
+ }
@@ -0,0 +1,14 @@
1
+ import * as Table from './styled/table';
2
+ import { CLIError } from '../../core/errors';
3
+ export declare const cli: {
4
+ action: import("@oclif/core/lib/ux/action/simple").default | import("@oclif/core/lib/ux/action/spinner").default;
5
+ confirm(message: string): Promise<boolean>;
6
+ readonly table: typeof import("./styled/table").table;
7
+ error(format: string | CLIError, ...args: string[]): void;
8
+ info(format: string, ...args: string[]): void;
9
+ log(format?: string, ...args: string[]): void;
10
+ password(message: string): Promise<string>;
11
+ prompt(message: string): Promise<string>;
12
+ };
13
+ export default cli;
14
+ export { Table };
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Table = exports.cli = void 0;
4
+ const Table = require("./styled/table");
5
+ exports.Table = Table;
6
+ const util = require("util");
7
+ const prompts_1 = require("@inquirer/prompts");
8
+ const core_1 = require("@oclif/core");
9
+ const color_1 = require("@oclif/color");
10
+ const errors_1 = require("../../core/errors");
11
+ exports.cli = {
12
+ action: core_1.ux.action,
13
+ confirm(message) {
14
+ return (0, prompts_1.confirm)({ message });
15
+ },
16
+ get table() {
17
+ if (process.env.SILENT_MODE === 'true') {
18
+ return () => { };
19
+ }
20
+ return require('./styled/table').table;
21
+ },
22
+ error(format, ...args) {
23
+ if (format instanceof errors_1.CLIError) {
24
+ const message = format.message +
25
+ (format.suggestions && format.suggestions.length > 0
26
+ ? ` Try this: ${format.suggestions.map((suggestion) => `'${suggestion}'`).join('\n')} \n`
27
+ : '');
28
+ this.info(color_1.default.red(`✖ `) + message);
29
+ }
30
+ else {
31
+ this.info(color_1.default.red(`✖ `) + format, ...args);
32
+ }
33
+ },
34
+ info(format, ...args) {
35
+ if (process.env.SILENT_MODE === 'true') {
36
+ return;
37
+ }
38
+ process.stdout.write(util.format(format, ...args) + '\n');
39
+ },
40
+ log(format, ...args) {
41
+ this.info(format || '', ...args);
42
+ },
43
+ password(message) {
44
+ return (0, prompts_1.password)({ message });
45
+ },
46
+ prompt(message) {
47
+ return (0, prompts_1.input)({ message });
48
+ },
49
+ };
50
+ exports.default = exports.cli;
@@ -0,0 +1,2 @@
1
+ export declare let stdtermwidth: number;
2
+ export declare let errtermwidth: number;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.errtermwidth = exports.stdtermwidth = void 0;
4
+ function termwidth(stream) {
5
+ if (!stream.isTTY) {
6
+ return 80;
7
+ }
8
+ const width = stream.getWindowSize()[0];
9
+ if (width < 1) {
10
+ return 80;
11
+ }
12
+ if (width < 40) {
13
+ return 40;
14
+ }
15
+ return width;
16
+ }
17
+ const columns = global.columns;
18
+ exports.stdtermwidth = columns || termwidth(process.stdout);
19
+ exports.errtermwidth = columns || termwidth(process.stderr);
20
+ process.stdout.on('resize', () => {
21
+ exports.stdtermwidth = columns || termwidth(process.stdout);
22
+ });
23
+ process.stderr.on('resize', () => {
24
+ exports.errtermwidth = columns || termwidth(process.stderr);
25
+ });
@@ -0,0 +1,23 @@
1
+ export declare function table<T extends Record<string, unknown>>(data: T[], columns: table.Columns<T>, options?: table.Options): void;
2
+ export declare namespace table {
3
+ interface Column<T extends Record<string, unknown>> {
4
+ header: string;
5
+ extended: boolean;
6
+ minWidth: number;
7
+ get(row: T): any;
8
+ }
9
+ type Columns<T extends Record<string, unknown>> = {
10
+ [key: string]: Partial<Column<T>>;
11
+ };
12
+ interface Options {
13
+ [key: string]: any;
14
+ sort?: string;
15
+ filter?: string;
16
+ columns?: string;
17
+ extended?: boolean;
18
+ 'no-truncate'?: boolean;
19
+ output?: string;
20
+ 'no-header'?: boolean;
21
+ printLine?(s: any): any;
22
+ }
23
+ }
@@ -0,0 +1,272 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.table = table;
4
+ const chalk = require("chalk");
5
+ const _ = require("lodash");
6
+ const util_1 = require("util");
7
+ const screen_1 = require("../screen");
8
+ const sw = require('string-width');
9
+ const { orderBy } = require('natural-orderby');
10
+ class Table {
11
+ constructor(data, columns, options = {}) {
12
+ this.data = data;
13
+ // assign columns
14
+ this.columns = Object.keys(columns).map((key) => {
15
+ const col = columns[key];
16
+ const extended = col.extended || false;
17
+ const get = col.get || ((row) => row[key]);
18
+ const header = typeof col.header === 'string' ? col.header : _.capitalize(key.replace(/_/g, ' '));
19
+ const minWidth = Math.max(col.minWidth || 0, sw(header) + 1);
20
+ return {
21
+ extended,
22
+ get,
23
+ header,
24
+ key,
25
+ minWidth,
26
+ };
27
+ });
28
+ // assign options
29
+ const { columns: cols, filter, csv, output, extended, sort, title, printLine } = options;
30
+ this.options = {
31
+ columns: cols,
32
+ output: csv ? 'csv' : output,
33
+ extended,
34
+ filter,
35
+ 'no-header': options['no-header'] || false,
36
+ 'no-truncate': options['no-truncate'] || false,
37
+ printLine: printLine || ((s) => process.stdout.write(s + '\n')),
38
+ rowStart: ' ',
39
+ sort,
40
+ title,
41
+ };
42
+ }
43
+ display() {
44
+ // build table rows from input array data
45
+ let rows = this.data.map((d) => {
46
+ const row = {};
47
+ for (const col of this.columns) {
48
+ let val = col.get(d);
49
+ if (typeof val !== 'string')
50
+ val = (0, util_1.inspect)(val, { breakLength: Number.POSITIVE_INFINITY });
51
+ row[col.key] = val;
52
+ }
53
+ return row;
54
+ });
55
+ // filter rows
56
+ if (this.options.filter) {
57
+ /* eslint-disable-next-line prefer-const */
58
+ let [header, regex] = this.options.filter.split('=');
59
+ const isNot = header[0] === '-';
60
+ if (isNot)
61
+ header = header.slice(1);
62
+ const col = this.findColumnFromHeader(header);
63
+ if (!col || !regex)
64
+ throw new Error('Filter flag has an invalid value');
65
+ rows = rows.filter((d) => {
66
+ const re = new RegExp(regex);
67
+ const val = d[col.key];
68
+ const match = val.match(re);
69
+ return isNot ? !match : match;
70
+ });
71
+ }
72
+ // sort rows
73
+ if (this.options.sort) {
74
+ const sorters = this.options.sort.split(',');
75
+ const sortHeaders = sorters.map((k) => (k[0] === '-' ? k.slice(1) : k));
76
+ const sortKeys = this.filterColumnsFromHeaders(sortHeaders).map((c) => {
77
+ return (v) => v[c.key];
78
+ });
79
+ const sortKeysOrder = sorters.map((k) => (k[0] === '-' ? 'desc' : 'asc'));
80
+ rows = orderBy(rows, sortKeys, sortKeysOrder);
81
+ }
82
+ // and filter columns
83
+ if (this.options.columns) {
84
+ const filters = this.options.columns.split(',');
85
+ this.columns = this.filterColumnsFromHeaders(filters);
86
+ }
87
+ else if (!this.options.extended) {
88
+ // show extented columns/properties
89
+ this.columns = this.columns.filter((c) => !c.extended);
90
+ }
91
+ this.data = rows;
92
+ switch (this.options.output) {
93
+ case 'csv':
94
+ this.outputCSV();
95
+ break;
96
+ case 'json':
97
+ this.outputJSON();
98
+ break;
99
+ default:
100
+ this.outputTable();
101
+ }
102
+ }
103
+ findColumnFromHeader(header) {
104
+ return this.columns.find((c) => c.header.toLowerCase() === header.toLowerCase());
105
+ }
106
+ filterColumnsFromHeaders(filters) {
107
+ // unique
108
+ filters = [...new Set(filters)];
109
+ const cols = [];
110
+ for (const f of filters) {
111
+ const c = this.columns.find((c) => c.header.toLowerCase() === f.toLowerCase());
112
+ if (c)
113
+ cols.push(c);
114
+ }
115
+ return cols;
116
+ }
117
+ getCSVRow(d) {
118
+ const values = this.columns.map((col) => d[col.key] || '');
119
+ const lineToBeEscaped = values.find((e) => e.includes('"') || e.includes('\n') || e.includes('\r\n') || e.includes('\r') || e.includes(','));
120
+ return values.map((e) => (lineToBeEscaped ? `"${e.replace('"', '""')}"` : e));
121
+ }
122
+ resolveColumnsToObjectArray() {
123
+ // tslint:disable-next-line:no-this-assignment
124
+ const { data, columns } = this;
125
+ return data.map((d) => {
126
+ // eslint-disable-next-line unicorn/prefer-object-from-entries
127
+ return columns.reduce((obj, col) => {
128
+ return {
129
+ ...obj,
130
+ [col.key]: d[col.key] || '',
131
+ };
132
+ }, {});
133
+ });
134
+ }
135
+ outputJSON() {
136
+ this.options.printLine(JSON.stringify(this.resolveColumnsToObjectArray(), undefined, 2));
137
+ }
138
+ outputCSV() {
139
+ // tslint:disable-next-line:no-this-assignment
140
+ const { data, columns, options } = this;
141
+ if (!options['no-header']) {
142
+ options.printLine(columns.map((c) => c.header).join(','));
143
+ }
144
+ for (const d of data) {
145
+ const row = this.getCSVRow(d);
146
+ options.printLine(row.join(','));
147
+ }
148
+ }
149
+ outputTable() {
150
+ // tslint:disable-next-line:no-this-assignment
151
+ const { data, columns, options } = this;
152
+ // column truncation
153
+ //
154
+ // find max width for each column
155
+ for (const col of columns) {
156
+ // convert multi-line cell to single longest line
157
+ // for width calculations
158
+ const widthData = data.map((row) => {
159
+ const d = row[col.key];
160
+ const manyLines = d.split('\n');
161
+ if (manyLines.length > 1) {
162
+ return '*'.repeat(Math.max(...manyLines.map((r) => sw(r))));
163
+ }
164
+ return d;
165
+ });
166
+ const widths = ['.'.padEnd(col.minWidth - 1), col.header, ...widthData.map((row) => row)].map((r) => sw(r));
167
+ col.maxWidth = Math.max(...widths) + 1;
168
+ col.width = col.maxWidth;
169
+ }
170
+ // terminal width
171
+ const maxWidth = screen_1.stdtermwidth - 2;
172
+ // truncation logic
173
+ const shouldShorten = () => {
174
+ // don't shorten if full mode
175
+ if (options['no-truncate'] || (!process.stdout.isTTY && !process.env.CLI_UX_SKIP_TTY_CHECK))
176
+ return;
177
+ // don't shorten if there is enough screen width
178
+ const dataMaxWidth = _.sumBy(columns, (c) => c.width);
179
+ const overWidth = dataMaxWidth - maxWidth;
180
+ if (overWidth <= 0)
181
+ return;
182
+ // not enough room, short all columns to minWidth
183
+ for (const col of columns) {
184
+ col.width = col.minWidth;
185
+ }
186
+ // if sum(minWidth's) is greater than term width
187
+ // nothing can be done so
188
+ // display all as minWidth
189
+ const dataMinWidth = _.sumBy(columns, (c) => c.minWidth);
190
+ if (dataMinWidth >= maxWidth)
191
+ return;
192
+ // some wiggle room left, add it back to "needy" columns
193
+ let wiggleRoom = maxWidth - dataMinWidth;
194
+ const needyCols = columns.map((c) => ({ key: c.key, needs: c.maxWidth - c.width })).sort((a, b) => a.needs - b.needs);
195
+ for (const { key, needs } of needyCols) {
196
+ if (!needs)
197
+ continue;
198
+ const col = columns.find((c) => key === c.key);
199
+ if (!col)
200
+ continue;
201
+ if (wiggleRoom > needs) {
202
+ col.width = col.width + needs;
203
+ wiggleRoom -= needs;
204
+ }
205
+ else if (wiggleRoom) {
206
+ col.width = col.width + wiggleRoom;
207
+ wiggleRoom = 0;
208
+ }
209
+ }
210
+ };
211
+ shouldShorten();
212
+ // print table title
213
+ if (options.title) {
214
+ options.printLine(options.title);
215
+ // print title divider
216
+ options.printLine(''.padEnd(columns.reduce((sum, col) => sum + col.width, 1), '='));
217
+ options.rowStart = '| ';
218
+ }
219
+ // print headers
220
+ if (!options['no-header']) {
221
+ let headers = options.rowStart;
222
+ for (const col of columns) {
223
+ const header = col.header;
224
+ headers += header.padEnd(col.width);
225
+ }
226
+ options.printLine(chalk.bold(headers));
227
+ // print header dividers
228
+ let dividers = options.rowStart;
229
+ for (const col of columns) {
230
+ const divider = ''.padEnd(col.width - 1, '─') + ' ';
231
+ dividers += divider.padEnd(col.width);
232
+ }
233
+ options.printLine(chalk.bold(dividers));
234
+ }
235
+ // print rows
236
+ for (const row of data) {
237
+ // find max number of lines
238
+ // for all cells in a row
239
+ // with multi-line strings
240
+ let numOfLines = 1;
241
+ for (const col of columns) {
242
+ const d = row[col.key];
243
+ const lines = d.split('\n').length;
244
+ if (lines > numOfLines)
245
+ numOfLines = lines;
246
+ }
247
+ // eslint-disable-next-line unicorn/no-new-array
248
+ const linesIndexess = [...new Array(numOfLines).keys()];
249
+ // print row
250
+ // including multi-lines
251
+ for (const i of linesIndexess) {
252
+ let l = options.rowStart;
253
+ for (const col of columns) {
254
+ const width = col.width;
255
+ let d = row[col.key];
256
+ d = d.split('\n')[i] || '';
257
+ const visualWidth = sw(d);
258
+ const colorWidth = d.length - visualWidth;
259
+ let cell = d.padEnd(width + colorWidth);
260
+ if (cell.length - colorWidth > width || visualWidth === width) {
261
+ cell = cell.slice(0, width - 2) + '… ';
262
+ }
263
+ l += cell;
264
+ }
265
+ options.printLine(l);
266
+ }
267
+ }
268
+ }
269
+ }
270
+ function table(data, columns, options = {}) {
271
+ new Table(data, columns, options).display();
272
+ }
@@ -0,0 +1 @@
1
+ export declare function colorizeByStatus(text: string | unknown, status: string): string | unknown;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.colorizeByStatus = colorizeByStatus;
4
+ const color_1 = require("@oclif/color");
5
+ function colorizeByStatus(text, status) {
6
+ return status.includes('removed') || status.includes('sensitivity-changed') ? color_1.default.red(text) : text;
7
+ }
@@ -0,0 +1,2 @@
1
+ export declare function escapeEnvValue(value: string): string;
2
+ export declare function unescapeEnvValue(value: string): string;