@dartfx/n8n-nodes-qsv 0.2.0 → 0.2.1
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/README.md +8 -8
- package/package.json +4 -4
- package/dist/nodes/Qsv/actions/executeClean.d.ts +0 -2
- package/dist/nodes/Qsv/actions/executeClean.js +0 -147
- package/dist/nodes/Qsv/actions/executeClipboard.d.ts +0 -2
- package/dist/nodes/Qsv/actions/executeClipboard.js +0 -114
- package/dist/nodes/Qsv/actions/executeColor.d.ts +0 -2
- package/dist/nodes/Qsv/actions/executeColor.js +0 -126
- package/dist/nodes/Qsv/actions/executeLens.d.ts +0 -2
- package/dist/nodes/Qsv/actions/executeLens.js +0 -156
- package/dist/nodes/Qsv/actions/executeLog.d.ts +0 -2
- package/dist/nodes/Qsv/actions/executeLog.js +0 -111
- package/dist/nodes/Qsv/actions/executePrompt.d.ts +0 -2
- package/dist/nodes/Qsv/actions/executePrompt.js +0 -132
- package/dist/nodes/Qsv/descriptions/CleanDescription.d.ts +0 -2
- package/dist/nodes/Qsv/descriptions/CleanDescription.js +0 -140
- package/dist/nodes/Qsv/descriptions/ClipboardDescription.d.ts +0 -2
- package/dist/nodes/Qsv/descriptions/ClipboardDescription.js +0 -63
- package/dist/nodes/Qsv/descriptions/ColorDescription.d.ts +0 -2
- package/dist/nodes/Qsv/descriptions/ColorDescription.js +0 -91
- package/dist/nodes/Qsv/descriptions/LensDescription.d.ts +0 -2
- package/dist/nodes/Qsv/descriptions/LensDescription.js +0 -161
- package/dist/nodes/Qsv/descriptions/LogDescription.d.ts +0 -2
- package/dist/nodes/Qsv/descriptions/LogDescription.js +0 -55
- package/dist/nodes/Qsv/descriptions/PromptDescription.d.ts +0 -2
- package/dist/nodes/Qsv/descriptions/PromptDescription.js +0 -105
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# n8n Community Node for datHere QSV (`@dartfx/n8n-nodes-qsv`)
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@dartfx/n8n-nodes-qsv)
|
|
4
|
-
[](https://github.com/DataArtifex/dartfx-n8n/actions/workflows/ci.yml)
|
|
5
|
-
[](https://dataartifex.github.io/dartfx-n8n/)
|
|
6
|
-
[](https://deepwiki.com/DataArtifex/dartfx-n8n)
|
|
4
|
+
[](https://github.com/DataArtifex/dartfx-n8n-nodes-qsv/actions/workflows/ci.yml)
|
|
5
|
+
[](https://dataartifex.github.io/dartfx-n8n-nodes-qsv/)
|
|
6
|
+
[](https://deepwiki.com/DataArtifex/dartfx-n8n-nodes-qsv)
|
|
7
7
|
[](https://docs.n8n.io/integrations/community-nodes/)
|
|
8
8
|
[](https://github.com/dathere/qsv)
|
|
9
9
|
[](https://opensource.org/licenses/MIT)
|
|
10
10
|
|
|
11
11
|
> [!NOTE]
|
|
12
|
-
> Node APIs, parameters, and operation interfaces in `@dartfx/n8n-nodes-qsv` are actively expanding. Please report any issues or feature requests on [GitHub Issues](https://github.com/DataArtifex/dartfx-n8n/issues).
|
|
12
|
+
> Node APIs, parameters, and operation interfaces in `@dartfx/n8n-nodes-qsv` are actively expanding. Please report any issues or feature requests on [GitHub Issues](https://github.com/DataArtifex/dartfx-n8n-nodes-qsv/issues).
|
|
13
13
|
|
|
14
14
|
A custom [n8n](https://n8n.io/) community node for ultra-fast, zero-copy tabular data wrangling, transformation, validation, and analytics powered by [datHere QSV](https://github.com/dathere/qsv).
|
|
15
15
|
|
|
@@ -189,8 +189,8 @@ Node collections that wrap host binaries declare and verify their target CLI ver
|
|
|
189
189
|
|
|
190
190
|
```bash
|
|
191
191
|
# Clone repository
|
|
192
|
-
git clone https://github.com/DataArtifex/dartfx-n8n.git
|
|
193
|
-
cd dartfx-n8n
|
|
192
|
+
git clone https://github.com/DataArtifex/dartfx-n8n-nodes-qsv.git
|
|
193
|
+
cd dartfx-n8n-nodes-qsv
|
|
194
194
|
|
|
195
195
|
# Use supported Node LTS (20/22)
|
|
196
196
|
nvm use
|
|
@@ -235,7 +235,7 @@ If running an older n8n v1 or v2 instance directly via Node / npm on your host m
|
|
|
235
235
|
```bash
|
|
236
236
|
mkdir -p ~/.n8n/custom
|
|
237
237
|
cd ~/.n8n/custom
|
|
238
|
-
pnpm link /path/to/dartfx-n8n
|
|
238
|
+
pnpm link /path/to/dartfx-n8n-nodes-qsv
|
|
239
239
|
|
|
240
240
|
# Start n8n
|
|
241
241
|
n8n start
|
|
@@ -250,7 +250,7 @@ n8n start
|
|
|
250
250
|
|
|
251
251
|
```bash
|
|
252
252
|
# Step 1: Register package globally
|
|
253
|
-
cd /path/to/dartfx-n8n
|
|
253
|
+
cd /path/to/dartfx-n8n-nodes-qsv
|
|
254
254
|
npm link
|
|
255
255
|
|
|
256
256
|
# Step 2: Link into n8n custom directory
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dartfx/n8n-nodes-qsv",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "n8n community node for datHere QSV ultra-fast tabular data wrangling, stats, and transformations (requires 'qsv' CLI on host)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package",
|
|
@@ -17,13 +17,13 @@
|
|
|
17
17
|
"name": "Data Artifex",
|
|
18
18
|
"url": "https://github.com/DataArtifex"
|
|
19
19
|
},
|
|
20
|
-
"homepage": "https://github.com/DataArtifex/dartfx-n8n",
|
|
20
|
+
"homepage": "https://github.com/DataArtifex/dartfx-n8n-nodes-qsv",
|
|
21
21
|
"repository": {
|
|
22
22
|
"type": "git",
|
|
23
|
-
"url": "https://github.com/DataArtifex/dartfx-n8n.git"
|
|
23
|
+
"url": "https://github.com/DataArtifex/dartfx-n8n-nodes-qsv.git"
|
|
24
24
|
},
|
|
25
25
|
"bugs": {
|
|
26
|
-
"url": "https://github.com/DataArtifex/dartfx-n8n/issues"
|
|
26
|
+
"url": "https://github.com/DataArtifex/dartfx-n8n-nodes-qsv/issues"
|
|
27
27
|
},
|
|
28
28
|
"main": "index.js",
|
|
29
29
|
"engines": {
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.executeClean = executeClean;
|
|
4
|
-
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
-
const child_process_1 = require("child_process");
|
|
6
|
-
const util_1 = require("util");
|
|
7
|
-
const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
|
|
8
|
-
async function executeClean(itemIndex) {
|
|
9
|
-
const inputPath = this.getNodeParameter("inputPath", itemIndex);
|
|
10
|
-
if (!inputPath || !inputPath.trim()) {
|
|
11
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), "Input CSV file path is required.", { itemIndex });
|
|
12
|
-
}
|
|
13
|
-
const outputPath = this.getNodeParameter("outputPath", itemIndex, "") || "";
|
|
14
|
-
const additionalArgs = this.getNodeParameter("additionalArgs", itemIndex, "") || "";
|
|
15
|
-
const options = this.getNodeParameter("options", itemIndex, {}) || {};
|
|
16
|
-
const args = ["clean"];
|
|
17
|
-
if (options.stale === true) {
|
|
18
|
-
args.push("--stale");
|
|
19
|
-
}
|
|
20
|
-
if (options.recursive === true) {
|
|
21
|
-
args.push("--recursive");
|
|
22
|
-
}
|
|
23
|
-
if (options.dryRun === true) {
|
|
24
|
-
args.push("--dry-run");
|
|
25
|
-
}
|
|
26
|
-
if (options.force === true) {
|
|
27
|
-
args.push("--force");
|
|
28
|
-
}
|
|
29
|
-
if (options.index === true) {
|
|
30
|
-
args.push("--index");
|
|
31
|
-
}
|
|
32
|
-
if (options.stats === true) {
|
|
33
|
-
args.push("--stats");
|
|
34
|
-
}
|
|
35
|
-
if (options.frequency === true) {
|
|
36
|
-
args.push("--frequency");
|
|
37
|
-
}
|
|
38
|
-
if (options.schema === true) {
|
|
39
|
-
args.push("--schema");
|
|
40
|
-
}
|
|
41
|
-
if (options.validate === true) {
|
|
42
|
-
args.push("--validate");
|
|
43
|
-
}
|
|
44
|
-
if (options.moarstats === true) {
|
|
45
|
-
args.push("--moarstats");
|
|
46
|
-
}
|
|
47
|
-
if (options.all === true) {
|
|
48
|
-
args.push("--all");
|
|
49
|
-
}
|
|
50
|
-
if (options.quiet === true) {
|
|
51
|
-
args.push("--quiet");
|
|
52
|
-
}
|
|
53
|
-
if (additionalArgs.trim()) {
|
|
54
|
-
const rawMatches = additionalArgs.match(/[^\s"']+|"[^"]*"|'[^']*'/g) || [];
|
|
55
|
-
const parsedArgs = rawMatches.map((arg) => {
|
|
56
|
-
if ((arg.startsWith('"') && arg.endsWith('"')) ||
|
|
57
|
-
(arg.startsWith("'") && arg.endsWith("'"))) {
|
|
58
|
-
return arg.slice(1, -1);
|
|
59
|
-
}
|
|
60
|
-
return arg;
|
|
61
|
-
});
|
|
62
|
-
args.push(...parsedArgs);
|
|
63
|
-
}
|
|
64
|
-
if (outputPath.trim()) {
|
|
65
|
-
args.push("--output", outputPath.trim());
|
|
66
|
-
}
|
|
67
|
-
args.push(inputPath);
|
|
68
|
-
const qsvBin = process.env.DARTFX_QSV_BIN_PATH ||
|
|
69
|
-
process.env.QSV_BIN_PATH ||
|
|
70
|
-
process.env.QSV_PATH ||
|
|
71
|
-
"qsv";
|
|
72
|
-
try {
|
|
73
|
-
const { stdout, stderr } = await execFileAsync(qsvBin, args, {
|
|
74
|
-
maxBuffer: 50 * 1024 * 1024,
|
|
75
|
-
encoding: "utf8",
|
|
76
|
-
});
|
|
77
|
-
let resultJson;
|
|
78
|
-
try {
|
|
79
|
-
resultJson = JSON.parse(stdout);
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
resultJson = {
|
|
83
|
-
command: "qsv clean",
|
|
84
|
-
inputPath,
|
|
85
|
-
rawOutput: stdout,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
const returnJson = {
|
|
89
|
-
success: true,
|
|
90
|
-
command: "clean",
|
|
91
|
-
inputPath,
|
|
92
|
-
result: resultJson,
|
|
93
|
-
};
|
|
94
|
-
if (outputPath.trim()) {
|
|
95
|
-
returnJson.outputPath = outputPath.trim();
|
|
96
|
-
}
|
|
97
|
-
if (stderr && stderr.trim()) {
|
|
98
|
-
returnJson.warnings = stderr.trim();
|
|
99
|
-
}
|
|
100
|
-
return [
|
|
101
|
-
{
|
|
102
|
-
json: returnJson,
|
|
103
|
-
},
|
|
104
|
-
];
|
|
105
|
-
}
|
|
106
|
-
catch (error) {
|
|
107
|
-
if (error.code === "ENOENT") {
|
|
108
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The QSV CLI binary ('${qsvBin}') was not found`, {
|
|
109
|
-
itemIndex,
|
|
110
|
-
description: `Please ensure 'qsv' is installed and available in the system PATH where n8n is running, or specify its absolute path via the DARTFX_QSV_BIN_PATH environment variable. (https://github.com/dathere/qsv)`,
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
if (error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER" ||
|
|
114
|
-
(error.message && error.message.includes("maxBuffer"))) {
|
|
115
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `QSV execution exceeded maximum stdout buffer (50 MB)`, {
|
|
116
|
-
itemIndex,
|
|
117
|
-
description: `qsv clean returned more data than could fit into memory. Specify an 'Output File Path' to stream results directly to disk instead.`,
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
const rawError = (error.stderr || error.message || "").trim();
|
|
121
|
-
if (rawError.includes("is not a qsv command") ||
|
|
122
|
-
rawError.includes("unrecognized subcommand") ||
|
|
123
|
-
rawError.includes("not available in this")) {
|
|
124
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Operation 'clean' is not available in the installed QSV binary`, {
|
|
125
|
-
itemIndex,
|
|
126
|
-
description: `The installed QSV binary at '${qsvBin}' does not include the 'clean' feature. This feature may require a full feature build of QSV (e.g. qsv with all_features or a prebuilt binary with feature flags enabled). See https://github.com/dathere/qsv#feature-flags`,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
if (rawError.includes("No such file or directory") ||
|
|
130
|
-
rawError.includes("os error 2")) {
|
|
131
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Input file not found: '${inputPath}'`, {
|
|
132
|
-
itemIndex,
|
|
133
|
-
description: `qsv clean could not find the file at '${inputPath}'. Check for typos, or if n8n is running in Docker, ensure the host directory is mounted into the container.`,
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
if (rawError.includes("Operation not permitted") ||
|
|
137
|
-
rawError.includes("os error 1") ||
|
|
138
|
-
rawError.includes("Permission denied") ||
|
|
139
|
-
rawError.includes("os error 13")) {
|
|
140
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Permission denied accessing file: '${inputPath}'`, {
|
|
141
|
-
itemIndex,
|
|
142
|
-
description: `qsv clean was denied read access to '${inputPath}'. On macOS, check Full Disk Access or Removable Volumes permissions for the application running n8n.`,
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed executing 'qsv clean': ${rawError}`, { itemIndex });
|
|
146
|
-
}
|
|
147
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.executeClipboard = executeClipboard;
|
|
4
|
-
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
-
const child_process_1 = require("child_process");
|
|
6
|
-
const util_1 = require("util");
|
|
7
|
-
const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
|
|
8
|
-
async function executeClipboard(itemIndex) {
|
|
9
|
-
const inputPath = this.getNodeParameter("inputPath", itemIndex);
|
|
10
|
-
if (!inputPath || !inputPath.trim()) {
|
|
11
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), "Input CSV file path is required.", { itemIndex });
|
|
12
|
-
}
|
|
13
|
-
const outputPath = this.getNodeParameter("outputPath", itemIndex, "") || "";
|
|
14
|
-
const additionalArgs = this.getNodeParameter("additionalArgs", itemIndex, "") || "";
|
|
15
|
-
const options = this.getNodeParameter("options", itemIndex, {}) || {};
|
|
16
|
-
const args = ["clipboard"];
|
|
17
|
-
if (options.save === true) {
|
|
18
|
-
args.push("--save");
|
|
19
|
-
}
|
|
20
|
-
if (additionalArgs.trim()) {
|
|
21
|
-
const rawMatches = additionalArgs.match(/[^\s"']+|"[^"]*"|'[^']*'/g) || [];
|
|
22
|
-
const parsedArgs = rawMatches.map((arg) => {
|
|
23
|
-
if ((arg.startsWith('"') && arg.endsWith('"')) ||
|
|
24
|
-
(arg.startsWith("'") && arg.endsWith("'"))) {
|
|
25
|
-
return arg.slice(1, -1);
|
|
26
|
-
}
|
|
27
|
-
return arg;
|
|
28
|
-
});
|
|
29
|
-
args.push(...parsedArgs);
|
|
30
|
-
}
|
|
31
|
-
if (outputPath.trim()) {
|
|
32
|
-
args.push("--output", outputPath.trim());
|
|
33
|
-
}
|
|
34
|
-
args.push(inputPath);
|
|
35
|
-
const qsvBin = process.env.DARTFX_QSV_BIN_PATH ||
|
|
36
|
-
process.env.QSV_BIN_PATH ||
|
|
37
|
-
process.env.QSV_PATH ||
|
|
38
|
-
"qsv";
|
|
39
|
-
try {
|
|
40
|
-
const { stdout, stderr } = await execFileAsync(qsvBin, args, {
|
|
41
|
-
maxBuffer: 50 * 1024 * 1024,
|
|
42
|
-
encoding: "utf8",
|
|
43
|
-
});
|
|
44
|
-
let resultJson;
|
|
45
|
-
try {
|
|
46
|
-
resultJson = JSON.parse(stdout);
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
resultJson = {
|
|
50
|
-
command: "qsv clipboard",
|
|
51
|
-
inputPath,
|
|
52
|
-
rawOutput: stdout,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
const returnJson = {
|
|
56
|
-
success: true,
|
|
57
|
-
command: "clipboard",
|
|
58
|
-
inputPath,
|
|
59
|
-
result: resultJson,
|
|
60
|
-
};
|
|
61
|
-
if (outputPath.trim()) {
|
|
62
|
-
returnJson.outputPath = outputPath.trim();
|
|
63
|
-
}
|
|
64
|
-
if (stderr && stderr.trim()) {
|
|
65
|
-
returnJson.warnings = stderr.trim();
|
|
66
|
-
}
|
|
67
|
-
return [
|
|
68
|
-
{
|
|
69
|
-
json: returnJson,
|
|
70
|
-
},
|
|
71
|
-
];
|
|
72
|
-
}
|
|
73
|
-
catch (error) {
|
|
74
|
-
if (error.code === "ENOENT") {
|
|
75
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The QSV CLI binary ('${qsvBin}') was not found`, {
|
|
76
|
-
itemIndex,
|
|
77
|
-
description: `Please ensure 'qsv' is installed and available in the system PATH where n8n is running, or specify its absolute path via the DARTFX_QSV_BIN_PATH environment variable. (https://github.com/dathere/qsv)`,
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
if (error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER" ||
|
|
81
|
-
(error.message && error.message.includes("maxBuffer"))) {
|
|
82
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `QSV execution exceeded maximum stdout buffer (50 MB)`, {
|
|
83
|
-
itemIndex,
|
|
84
|
-
description: `qsv clipboard returned more data than could fit into memory. Specify an 'Output File Path' to stream results directly to disk instead.`,
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
const rawError = (error.stderr || error.message || "").trim();
|
|
88
|
-
if (rawError.includes("is not a qsv command") ||
|
|
89
|
-
rawError.includes("unrecognized subcommand") ||
|
|
90
|
-
rawError.includes("not available in this")) {
|
|
91
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Operation 'clipboard' is not available in the installed QSV binary`, {
|
|
92
|
-
itemIndex,
|
|
93
|
-
description: `The installed QSV binary at '${qsvBin}' does not include the 'clipboard' feature. This feature may require a full feature build of QSV (e.g. qsv with all_features or a prebuilt binary with feature flags enabled). See https://github.com/dathere/qsv#feature-flags`,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
if (rawError.includes("No such file or directory") ||
|
|
97
|
-
rawError.includes("os error 2")) {
|
|
98
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Input file not found: '${inputPath}'`, {
|
|
99
|
-
itemIndex,
|
|
100
|
-
description: `qsv clipboard could not find the file at '${inputPath}'. Check for typos, or if n8n is running in Docker, ensure the host directory is mounted into the container.`,
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
if (rawError.includes("Operation not permitted") ||
|
|
104
|
-
rawError.includes("os error 1") ||
|
|
105
|
-
rawError.includes("Permission denied") ||
|
|
106
|
-
rawError.includes("os error 13")) {
|
|
107
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Permission denied accessing file: '${inputPath}'`, {
|
|
108
|
-
itemIndex,
|
|
109
|
-
description: `qsv clipboard was denied read access to '${inputPath}'. On macOS, check Full Disk Access or Removable Volumes permissions for the application running n8n.`,
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed executing 'qsv clipboard': ${rawError}`, { itemIndex });
|
|
113
|
-
}
|
|
114
|
-
}
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.executeColor = executeColor;
|
|
4
|
-
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
-
const child_process_1 = require("child_process");
|
|
6
|
-
const util_1 = require("util");
|
|
7
|
-
const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
|
|
8
|
-
async function executeColor(itemIndex) {
|
|
9
|
-
const inputPath = this.getNodeParameter("inputPath", itemIndex);
|
|
10
|
-
if (!inputPath || !inputPath.trim()) {
|
|
11
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), "Input CSV file path is required.", { itemIndex });
|
|
12
|
-
}
|
|
13
|
-
const outputPath = this.getNodeParameter("outputPath", itemIndex, "") || "";
|
|
14
|
-
const additionalArgs = this.getNodeParameter("additionalArgs", itemIndex, "") || "";
|
|
15
|
-
const options = this.getNodeParameter("options", itemIndex, {}) || {};
|
|
16
|
-
const args = ["color"];
|
|
17
|
-
if (options.color === true) {
|
|
18
|
-
args.push("--color");
|
|
19
|
-
}
|
|
20
|
-
if (options.rowNumbers === true) {
|
|
21
|
-
args.push("--row-numbers");
|
|
22
|
-
}
|
|
23
|
-
if (options.title !== undefined && options.title !== "") {
|
|
24
|
-
args.push("--title", String(options.title));
|
|
25
|
-
}
|
|
26
|
-
if (options.delimiter !== undefined && options.delimiter !== "") {
|
|
27
|
-
args.push("--delimiter", String(options.delimiter));
|
|
28
|
-
}
|
|
29
|
-
if (options.memcheck === true) {
|
|
30
|
-
args.push("--memcheck");
|
|
31
|
-
}
|
|
32
|
-
if (additionalArgs.trim()) {
|
|
33
|
-
const rawMatches = additionalArgs.match(/[^\s"']+|"[^"]*"|'[^']*'/g) || [];
|
|
34
|
-
const parsedArgs = rawMatches.map((arg) => {
|
|
35
|
-
if ((arg.startsWith('"') && arg.endsWith('"')) ||
|
|
36
|
-
(arg.startsWith("'") && arg.endsWith("'"))) {
|
|
37
|
-
return arg.slice(1, -1);
|
|
38
|
-
}
|
|
39
|
-
return arg;
|
|
40
|
-
});
|
|
41
|
-
args.push(...parsedArgs);
|
|
42
|
-
}
|
|
43
|
-
if (outputPath.trim()) {
|
|
44
|
-
args.push("--output", outputPath.trim());
|
|
45
|
-
}
|
|
46
|
-
args.push(inputPath);
|
|
47
|
-
const qsvBin = process.env.DARTFX_QSV_BIN_PATH ||
|
|
48
|
-
process.env.QSV_BIN_PATH ||
|
|
49
|
-
process.env.QSV_PATH ||
|
|
50
|
-
"qsv";
|
|
51
|
-
try {
|
|
52
|
-
const { stdout, stderr } = await execFileAsync(qsvBin, args, {
|
|
53
|
-
maxBuffer: 50 * 1024 * 1024,
|
|
54
|
-
encoding: "utf8",
|
|
55
|
-
});
|
|
56
|
-
let resultJson;
|
|
57
|
-
try {
|
|
58
|
-
resultJson = JSON.parse(stdout);
|
|
59
|
-
}
|
|
60
|
-
catch {
|
|
61
|
-
resultJson = {
|
|
62
|
-
command: "qsv color",
|
|
63
|
-
inputPath,
|
|
64
|
-
rawOutput: stdout,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
const returnJson = {
|
|
68
|
-
success: true,
|
|
69
|
-
command: "color",
|
|
70
|
-
inputPath,
|
|
71
|
-
result: resultJson,
|
|
72
|
-
};
|
|
73
|
-
if (outputPath.trim()) {
|
|
74
|
-
returnJson.outputPath = outputPath.trim();
|
|
75
|
-
}
|
|
76
|
-
if (stderr && stderr.trim()) {
|
|
77
|
-
returnJson.warnings = stderr.trim();
|
|
78
|
-
}
|
|
79
|
-
return [
|
|
80
|
-
{
|
|
81
|
-
json: returnJson,
|
|
82
|
-
},
|
|
83
|
-
];
|
|
84
|
-
}
|
|
85
|
-
catch (error) {
|
|
86
|
-
if (error.code === "ENOENT") {
|
|
87
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The QSV CLI binary ('${qsvBin}') was not found`, {
|
|
88
|
-
itemIndex,
|
|
89
|
-
description: `Please ensure 'qsv' is installed and available in the system PATH where n8n is running, or specify its absolute path via the DARTFX_QSV_BIN_PATH environment variable. (https://github.com/dathere/qsv)`,
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
if (error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER" ||
|
|
93
|
-
(error.message && error.message.includes("maxBuffer"))) {
|
|
94
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `QSV execution exceeded maximum stdout buffer (50 MB)`, {
|
|
95
|
-
itemIndex,
|
|
96
|
-
description: `qsv color returned more data than could fit into memory. Specify an 'Output File Path' to stream results directly to disk instead.`,
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
const rawError = (error.stderr || error.message || "").trim();
|
|
100
|
-
if (rawError.includes("is not a qsv command") ||
|
|
101
|
-
rawError.includes("unrecognized subcommand") ||
|
|
102
|
-
rawError.includes("not available in this")) {
|
|
103
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Operation 'color' is not available in the installed QSV binary`, {
|
|
104
|
-
itemIndex,
|
|
105
|
-
description: `The installed QSV binary at '${qsvBin}' does not include the 'color' feature. This feature may require a full feature build of QSV (e.g. qsv with all_features or a prebuilt binary with feature flags enabled). See https://github.com/dathere/qsv#feature-flags`,
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
if (rawError.includes("No such file or directory") ||
|
|
109
|
-
rawError.includes("os error 2")) {
|
|
110
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Input file not found: '${inputPath}'`, {
|
|
111
|
-
itemIndex,
|
|
112
|
-
description: `qsv color could not find the file at '${inputPath}'. Check for typos, or if n8n is running in Docker, ensure the host directory is mounted into the container.`,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
if (rawError.includes("Operation not permitted") ||
|
|
116
|
-
rawError.includes("os error 1") ||
|
|
117
|
-
rawError.includes("Permission denied") ||
|
|
118
|
-
rawError.includes("os error 13")) {
|
|
119
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Permission denied accessing file: '${inputPath}'`, {
|
|
120
|
-
itemIndex,
|
|
121
|
-
description: `qsv color was denied read access to '${inputPath}'. On macOS, check Full Disk Access or Removable Volumes permissions for the application running n8n.`,
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed executing 'qsv color': ${rawError}`, { itemIndex });
|
|
125
|
-
}
|
|
126
|
-
}
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.executeLens = executeLens;
|
|
4
|
-
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
-
const child_process_1 = require("child_process");
|
|
6
|
-
const util_1 = require("util");
|
|
7
|
-
const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
|
|
8
|
-
async function executeLens(itemIndex) {
|
|
9
|
-
const inputPath = this.getNodeParameter("inputPath", itemIndex);
|
|
10
|
-
if (!inputPath || !inputPath.trim()) {
|
|
11
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), "Input CSV file path is required.", { itemIndex });
|
|
12
|
-
}
|
|
13
|
-
const outputPath = this.getNodeParameter("outputPath", itemIndex, "") || "";
|
|
14
|
-
const additionalArgs = this.getNodeParameter("additionalArgs", itemIndex, "") || "";
|
|
15
|
-
const options = this.getNodeParameter("options", itemIndex, {}) || {};
|
|
16
|
-
const args = ["lens"];
|
|
17
|
-
if (options.delimiter !== undefined && options.delimiter !== "") {
|
|
18
|
-
args.push("--delimiter", String(options.delimiter));
|
|
19
|
-
}
|
|
20
|
-
if (options.tabSeparated === true) {
|
|
21
|
-
args.push("--tab-separated");
|
|
22
|
-
}
|
|
23
|
-
if (options.noHeaders === true) {
|
|
24
|
-
args.push("--no-headers");
|
|
25
|
-
}
|
|
26
|
-
if (options.columns !== undefined && options.columns !== "") {
|
|
27
|
-
args.push("--columns", String(options.columns));
|
|
28
|
-
}
|
|
29
|
-
if (options.filter !== undefined && options.filter !== "") {
|
|
30
|
-
args.push("--filter", String(options.filter));
|
|
31
|
-
}
|
|
32
|
-
if (options.find !== undefined && options.find !== "") {
|
|
33
|
-
args.push("--find", String(options.find));
|
|
34
|
-
}
|
|
35
|
-
if (options.ignoreCase === true) {
|
|
36
|
-
args.push("--ignore-case");
|
|
37
|
-
}
|
|
38
|
-
if (options.freezeColumns !== undefined && options.freezeColumns !== "") {
|
|
39
|
-
args.push("--freeze-columns", String(options.freezeColumns));
|
|
40
|
-
}
|
|
41
|
-
if (options.monochrome === true) {
|
|
42
|
-
args.push("--monochrome");
|
|
43
|
-
}
|
|
44
|
-
if (options.wrapMode !== undefined && options.wrapMode !== "") {
|
|
45
|
-
args.push("--wrap-mode", String(options.wrapMode));
|
|
46
|
-
}
|
|
47
|
-
if (options.autoReload === true) {
|
|
48
|
-
args.push("--auto-reload");
|
|
49
|
-
}
|
|
50
|
-
if (options.streamingStdin === true) {
|
|
51
|
-
args.push("--streaming-stdin");
|
|
52
|
-
}
|
|
53
|
-
if (options.prompt !== undefined && options.prompt !== "") {
|
|
54
|
-
args.push("--prompt", String(options.prompt));
|
|
55
|
-
}
|
|
56
|
-
if (options.echoColumn !== undefined && options.echoColumn !== "") {
|
|
57
|
-
args.push("--echo-column", String(options.echoColumn));
|
|
58
|
-
}
|
|
59
|
-
if (options.debug === true) {
|
|
60
|
-
args.push("--debug");
|
|
61
|
-
}
|
|
62
|
-
if (additionalArgs.trim()) {
|
|
63
|
-
const rawMatches = additionalArgs.match(/[^\s"']+|"[^"]*"|'[^']*'/g) || [];
|
|
64
|
-
const parsedArgs = rawMatches.map((arg) => {
|
|
65
|
-
if ((arg.startsWith('"') && arg.endsWith('"')) ||
|
|
66
|
-
(arg.startsWith("'") && arg.endsWith("'"))) {
|
|
67
|
-
return arg.slice(1, -1);
|
|
68
|
-
}
|
|
69
|
-
return arg;
|
|
70
|
-
});
|
|
71
|
-
args.push(...parsedArgs);
|
|
72
|
-
}
|
|
73
|
-
if (outputPath.trim()) {
|
|
74
|
-
args.push("--output", outputPath.trim());
|
|
75
|
-
}
|
|
76
|
-
args.push(inputPath);
|
|
77
|
-
const qsvBin = process.env.DARTFX_QSV_BIN_PATH ||
|
|
78
|
-
process.env.QSV_BIN_PATH ||
|
|
79
|
-
process.env.QSV_PATH ||
|
|
80
|
-
"qsv";
|
|
81
|
-
try {
|
|
82
|
-
const { stdout, stderr } = await execFileAsync(qsvBin, args, {
|
|
83
|
-
maxBuffer: 50 * 1024 * 1024,
|
|
84
|
-
encoding: "utf8",
|
|
85
|
-
});
|
|
86
|
-
let resultJson;
|
|
87
|
-
try {
|
|
88
|
-
resultJson = JSON.parse(stdout);
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
resultJson = {
|
|
92
|
-
command: "qsv lens",
|
|
93
|
-
inputPath,
|
|
94
|
-
rawOutput: stdout,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
const returnJson = {
|
|
98
|
-
success: true,
|
|
99
|
-
command: "lens",
|
|
100
|
-
inputPath,
|
|
101
|
-
result: resultJson,
|
|
102
|
-
};
|
|
103
|
-
if (outputPath.trim()) {
|
|
104
|
-
returnJson.outputPath = outputPath.trim();
|
|
105
|
-
}
|
|
106
|
-
if (stderr && stderr.trim()) {
|
|
107
|
-
returnJson.warnings = stderr.trim();
|
|
108
|
-
}
|
|
109
|
-
return [
|
|
110
|
-
{
|
|
111
|
-
json: returnJson,
|
|
112
|
-
},
|
|
113
|
-
];
|
|
114
|
-
}
|
|
115
|
-
catch (error) {
|
|
116
|
-
if (error.code === "ENOENT") {
|
|
117
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The QSV CLI binary ('${qsvBin}') was not found`, {
|
|
118
|
-
itemIndex,
|
|
119
|
-
description: `Please ensure 'qsv' is installed and available in the system PATH where n8n is running, or specify its absolute path via the DARTFX_QSV_BIN_PATH environment variable. (https://github.com/dathere/qsv)`,
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
if (error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER" ||
|
|
123
|
-
(error.message && error.message.includes("maxBuffer"))) {
|
|
124
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `QSV execution exceeded maximum stdout buffer (50 MB)`, {
|
|
125
|
-
itemIndex,
|
|
126
|
-
description: `qsv lens returned more data than could fit into memory. Specify an 'Output File Path' to stream results directly to disk instead.`,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
const rawError = (error.stderr || error.message || "").trim();
|
|
130
|
-
if (rawError.includes("is not a qsv command") ||
|
|
131
|
-
rawError.includes("unrecognized subcommand") ||
|
|
132
|
-
rawError.includes("not available in this")) {
|
|
133
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Operation 'lens' is not available in the installed QSV binary`, {
|
|
134
|
-
itemIndex,
|
|
135
|
-
description: `The installed QSV binary at '${qsvBin}' does not include the 'lens' feature. This feature may require a full feature build of QSV (e.g. qsv with all_features or a prebuilt binary with feature flags enabled). See https://github.com/dathere/qsv#feature-flags`,
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
if (rawError.includes("No such file or directory") ||
|
|
139
|
-
rawError.includes("os error 2")) {
|
|
140
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Input file not found: '${inputPath}'`, {
|
|
141
|
-
itemIndex,
|
|
142
|
-
description: `qsv lens could not find the file at '${inputPath}'. Check for typos, or if n8n is running in Docker, ensure the host directory is mounted into the container.`,
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
if (rawError.includes("Operation not permitted") ||
|
|
146
|
-
rawError.includes("os error 1") ||
|
|
147
|
-
rawError.includes("Permission denied") ||
|
|
148
|
-
rawError.includes("os error 13")) {
|
|
149
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Permission denied accessing file: '${inputPath}'`, {
|
|
150
|
-
itemIndex,
|
|
151
|
-
description: `qsv lens was denied read access to '${inputPath}'. On macOS, check Full Disk Access or Removable Volumes permissions for the application running n8n.`,
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed executing 'qsv lens': ${rawError}`, { itemIndex });
|
|
155
|
-
}
|
|
156
|
-
}
|