-
Notifications
You must be signed in to change notification settings - Fork 1
v1.0.10 #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
v1.0.10 #40
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,8 +1,14 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const axios = require('axios'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const pkg = require('../../package.json'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const https = require('node:https'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const http = require('node:http'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Identifies requests as coming from the CLI (e.g. "vnext-workflow-cli/1.0.0") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const USER_AGENT = `vnext-workflow-cli/${pkg.version}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Create axios instance with custom agents for both HTTP and HTTPS | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const apiClient = axios.create({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| httpAgent: new http.Agent({ keepAlive: true }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| httpsAgent: new https.Agent({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rejectUnauthorized: false // Allow self-signed certificates | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚨 issue (security): Disabling TLS verification ( This setting causes the client to trust any certificate, including invalid or malicious ones. If you only need this for local/self‑signed development, please gate it behind configuration and keep strict verification as the default. Alternatively, support a custom CA bundle for self‑signed certs instead of disabling verification entirely. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+2
to
+11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This block introduces two important issues:
Recommendation:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Tests the API connection | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -11,7 +17,7 @@ const USER_AGENT = `vnext-workflow-cli/${pkg.version}`; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function testApiConnection(baseUrl) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await axios.get(`${baseUrl}/health`, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await apiClient.get(`${baseUrl}/health`, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timeout: 5000, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| headers: { 'User-Agent': USER_AGENT } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
18
to
22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (bug_risk): USER_AGENT is no longer defined, which will throw at runtime before the request is made. This reference remains from before the package.json import was removed, so testApiConnection will now throw a ReferenceError before the health check runs. Please either restore a USER_AGENT constant (ideally shared with other API calls) or drop this header to align with existing apiClient usage. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -31,11 +37,10 @@ async function publishComponent(baseUrl, componentData) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const url = `${baseUrl}/api/v1/definitions/publish`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await axios.post(url, componentData, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await apiClient.post(url, componentData, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| headers: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'accept': '*/*', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Content-Type': 'application/json', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'User-Agent': USER_AGENT | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Content-Type': 'application/json' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timeout: 30000 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -93,7 +98,7 @@ async function publishComponent(baseUrl, componentData) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function reinitializeSystem(baseUrl, version) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const url = `${baseUrl}/api/${version}/definitions/re-initialize`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await axios.get(url, { timeout: 10000, headers: { 'User-Agent': USER_AGENT } }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await apiClient.get(url, { timeout: 10000 }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -150,6 +150,86 @@ function listDiscovered(discovered, componentTypes) { | |||||||||||||||
| return results; | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| /** | ||||||||||||||||
| * Resolves a folder name to a list of directories to update. | ||||||||||||||||
| * | ||||||||||||||||
| * Two resolution modes (in order): | ||||||||||||||||
| * a) Exact path: if `name` resolves to an existing directory (absolute, or | ||||||||||||||||
| * relative to projectRoot, or relative to componentsRoot), that single | ||||||||||||||||
| * directory is returned. | ||||||||||||||||
| * b) Feature name: otherwise, `name` is treated as a feature folder name and | ||||||||||||||||
| * matched against every discovered component-type root. Every | ||||||||||||||||
| * `<componentRoot>/<name>` that exists as a directory is collected, so a | ||||||||||||||||
| * feature spread across Workflows/, Views/, Schemas/, … is gathered. | ||||||||||||||||
| * | ||||||||||||||||
| * @param {string} projectRoot - Project root folder | ||||||||||||||||
| * @param {string} name - Folder name or relative/absolute path | ||||||||||||||||
| * @returns {Promise<string[]>} Absolute directory paths (empty if nothing matched) | ||||||||||||||||
| */ | ||||||||||||||||
| async function resolveFeatureFolders(projectRoot, name) { | ||||||||||||||||
| const isDir = (p) => fs.existsSync(p) && fs.statSync(p).isDirectory(); | ||||||||||||||||
|
Comment on lines
+169
to
+170
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To prevent potential runtime crashes (e.g.,
Suggested change
|
||||||||||||||||
|
|
||||||||||||||||
| // a) Exact-path resolution | ||||||||||||||||
| const candidates = []; | ||||||||||||||||
| if (path.isAbsolute(name)) { | ||||||||||||||||
| candidates.push(name); | ||||||||||||||||
| } else { | ||||||||||||||||
| candidates.push(path.join(projectRoot, name)); | ||||||||||||||||
| try { | ||||||||||||||||
| candidates.push(path.join(getComponentsRoot(projectRoot), name)); | ||||||||||||||||
| } catch (error) { | ||||||||||||||||
| // componentsRoot may be unavailable; ignore and fall through | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| for (const candidate of candidates) { | ||||||||||||||||
| if (isDir(candidate)) { | ||||||||||||||||
| return [path.resolve(candidate)]; | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| // b) Feature-name match across discovered component roots | ||||||||||||||||
| const discovered = await discoverComponents(projectRoot); | ||||||||||||||||
| const dirs = []; | ||||||||||||||||
| for (const componentDir of Object.values(discovered)) { | ||||||||||||||||
| const featureDir = path.join(componentDir, name); | ||||||||||||||||
| if (isDir(featureDir)) { | ||||||||||||||||
| dirs.push(path.resolve(featureDir)); | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| return dirs; | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| /** | ||||||||||||||||
| * Lists available feature folder names — the union of immediate subdirectory | ||||||||||||||||
| * names across all discovered component-type roots. Used for error messages | ||||||||||||||||
| * when a requested folder name does not match anything. | ||||||||||||||||
| * | ||||||||||||||||
| * @param {string} projectRoot - Project root folder | ||||||||||||||||
| * @returns {Promise<string[]>} Sorted unique feature folder names | ||||||||||||||||
| */ | ||||||||||||||||
| async function listFeatureFolders(projectRoot) { | ||||||||||||||||
| const discovered = await discoverComponents(projectRoot); | ||||||||||||||||
| const names = new Set(); | ||||||||||||||||
|
|
||||||||||||||||
| for (const componentDir of Object.values(discovered)) { | ||||||||||||||||
| let entries = []; | ||||||||||||||||
| try { | ||||||||||||||||
| entries = fs.readdirSync(componentDir, { withFileTypes: true }); | ||||||||||||||||
| } catch (error) { | ||||||||||||||||
| continue; | ||||||||||||||||
| } | ||||||||||||||||
| for (const entry of entries) { | ||||||||||||||||
| if (entry.isDirectory() && !entry.name.startsWith('.')) { | ||||||||||||||||
| names.add(entry.name); | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| return Array.from(names).sort(); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| /** | ||||||||||||||||
| * Detects component type from file path | ||||||||||||||||
| * @param {string} filePath - File path | ||||||||||||||||
|
|
@@ -177,5 +257,7 @@ module.exports = { | |||||||||||||||
| findAllCsxInComponents, | ||||||||||||||||
| getComponentDir, | ||||||||||||||||
| listDiscovered, | ||||||||||||||||
| resolveFeatureFolders, | ||||||||||||||||
| listFeatureFolders, | ||||||||||||||||
| detectComponentTypeFromPath | ||||||||||||||||
| }; | ||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The globbing operation for finding JSON files in the feature folders is not wrapped in a
try-catchblock. Ifglobthrows an error (e.g., due to permission issues), the CLI will crash with an unhandled promise rejection, leaving the spinner hanging. Wrapping this in atry-catchblock and callingspinner.fail()ensures graceful error handling.