jitsu-cli 0.0.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 (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -0
  3. package/bin/jitsu-cli +3 -0
  4. package/lib/cli/extension/build.d.ts +2 -0
  5. package/lib/cli/extension/build.js +137 -0
  6. package/lib/cli/extension/create.d.ts +2 -0
  7. package/lib/cli/extension/create.js +92 -0
  8. package/lib/cli/extension/exec.d.ts +3 -0
  9. package/lib/cli/extension/exec.js +296 -0
  10. package/lib/cli/extension/index.d.ts +9 -0
  11. package/lib/cli/extension/index.js +147 -0
  12. package/lib/cli/extension/template.d.ts +30 -0
  13. package/lib/cli/extension/template.js +237 -0
  14. package/lib/cli/extension/test.d.ts +2 -0
  15. package/lib/cli/extension/test.js +34 -0
  16. package/lib/cli/extension/validate-config.d.ts +2 -0
  17. package/lib/cli/extension/validate-config.js +62 -0
  18. package/lib/index.d.ts +1 -0
  19. package/lib/index.js +7 -0
  20. package/lib/lib/chalk-code-highlight.d.ts +14 -0
  21. package/lib/lib/chalk-code-highlight.js +41 -0
  22. package/lib/lib/command/index.d.ts +3 -0
  23. package/lib/lib/command/index.js +118 -0
  24. package/lib/lib/errors.d.ts +1 -0
  25. package/lib/lib/errors.js +18 -0
  26. package/lib/lib/indent.d.ts +9 -0
  27. package/lib/lib/indent.js +41 -0
  28. package/lib/lib/log.d.ts +8 -0
  29. package/lib/lib/log.js +35 -0
  30. package/lib/lib/template.d.ts +5 -0
  31. package/lib/lib/template.js +36 -0
  32. package/lib/lib/validation.d.ts +2 -0
  33. package/lib/lib/validation.js +23 -0
  34. package/lib/lib/version.d.ts +5 -0
  35. package/lib/lib/version.js +35 -0
  36. package/lib/package.json +73 -0
  37. package/lib/run.d.ts +1 -0
  38. package/lib/run.js +37 -0
  39. package/lib/tests.d.ts +2 -0
  40. package/lib/tests.js +10 -0
  41. package/package.json +73 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Jitsu Labs, Inc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # Jitsu CLI
2
+
3
+ #### Quick start
4
+
5
+ ```
6
+ npx jitsu-cli --help
7
+ ```
8
+
9
+ Submodule of [Jitsu SDK](https://github.com/jitsucom/jitsu-sdk).
10
+
11
+ [Read more →](https://github.com/jitsucom/jitsu-sdk).
package/bin/jitsu-cli ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ require("../lib/index.js");
@@ -0,0 +1,2 @@
1
+ import { CommandResult } from "../../lib/command/types";
2
+ export declare function build(args: string[]): Promise<CommandResult>;
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.build = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const path_1 = tslib_1.__importDefault(require("path"));
6
+ const log_1 = tslib_1.__importDefault(require("../../lib/log"));
7
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
8
+ const fs_1 = tslib_1.__importDefault(require("fs"));
9
+ const errors_1 = require("../../lib/errors");
10
+ const rollup_1 = require("rollup");
11
+ const rollup_plugin_typescript2_1 = tslib_1.__importDefault(require("rollup-plugin-typescript2"));
12
+ const plugin_multi_entry_1 = tslib_1.__importDefault(require("@rollup/plugin-multi-entry"));
13
+ const plugin_json_1 = tslib_1.__importDefault(require("@rollup/plugin-json"));
14
+ const plugin_node_resolve_1 = tslib_1.__importDefault(require("@rollup/plugin-node-resolve"));
15
+ const plugin_commonjs_1 = tslib_1.__importDefault(require("@rollup/plugin-commonjs"));
16
+ const version_1 = require("../../lib/version");
17
+ const _1 = require("./");
18
+ const JSON5 = tslib_1.__importStar(require("json5"));
19
+ function fixNodeFetch(code) {
20
+ return code.replace("throw new TypeError('Expected signal to be an instanceof AbortSignal');", "");
21
+ }
22
+ function insertStreamReaderFacade(targetFile) {
23
+ return {
24
+ name: "insert-stream-reader-facade",
25
+ transform: async (code, id) => {
26
+ const [path] = id.split("?");
27
+ if (path === targetFile) {
28
+ let newCode = [
29
+ `import * as srcLib from "@jitsu/jlib/lib/sources-lib";`,
30
+ code,
31
+ ";",
32
+ `export const __$srcLib = srcLib;`,
33
+ ].join("\n");
34
+ return { code: newCode };
35
+ }
36
+ return null;
37
+ },
38
+ };
39
+ }
40
+ async function build(args) {
41
+ const directory = args?.[0] || "";
42
+ let projectBase = path_1.default.isAbsolute(directory) ? directory : path_1.default.resolve(process.cwd() + "/" + directory);
43
+ (0, log_1.default)().info("🔨 Building project in " + chalk_1.default.bold(projectBase) + ` with ${version_1.jitsuPackageName}@${version_1.jitsuCliVersion} `);
44
+ if (!fs_1.default.existsSync(projectBase)) {
45
+ throw new Error(`Path ${projectBase} does not exist`);
46
+ }
47
+ let packageFile = path_1.default.resolve(projectBase, "package.json");
48
+ if (!fs_1.default.existsSync(packageFile)) {
49
+ return { success: false, message: "Can't find package.json in " + projectBase };
50
+ }
51
+ let packageJson;
52
+ try {
53
+ packageJson = JSON5.parse(fs_1.default.readFileSync(packageFile, "utf8"));
54
+ }
55
+ catch (e) {
56
+ throw new Error((0, errors_1.appendError)(`Failed to parse package.json at ${projectBase}`, e));
57
+ }
58
+ let distFile = (0, _1.getDistFile)(packageJson);
59
+ let fullOutputPath = path_1.default.resolve(projectBase, distFile);
60
+ (0, log_1.default)().info("📦 Bundle will be written to " + chalk_1.default.bold(fullOutputPath));
61
+ let tsConfigPath = path_1.default.resolve(projectBase, "tsconfig.json");
62
+ const typescriptEnabled = fs_1.default.existsSync(tsConfigPath);
63
+ if (typescriptEnabled) {
64
+ (0, log_1.default)().info(`Found ${chalk_1.default.bold("tsconfig.json")}, typescript will be enabled`);
65
+ }
66
+ else {
67
+ return {
68
+ success: false,
69
+ message: `${chalk_1.default.bold("tsconfig.json")} is not found in the root of the project. Pure JS extensions are not supported yet`,
70
+ };
71
+ }
72
+ (0, _1.validateTsConfig)(tsConfigPath);
73
+ (0, log_1.default)().info("Building project...");
74
+ try {
75
+ let indexFile = path_1.default.resolve(projectBase, "src/index.ts");
76
+ if (!fs_1.default.existsSync(indexFile)) {
77
+ return { success: false, message: `Project should have src/index.ts file. Can't find it at ${indexFile}` };
78
+ }
79
+ const bundle = await (0, rollup_1.rollup)({
80
+ input: [indexFile],
81
+ plugins: [
82
+ typescriptEnabled && (0, rollup_plugin_typescript2_1.default)({ cwd: projectBase }),
83
+ insertStreamReaderFacade(indexFile),
84
+ (0, plugin_multi_entry_1.default)(),
85
+ (0, plugin_node_resolve_1.default)({ preferBuiltins: false }),
86
+ (0, plugin_commonjs_1.default)(),
87
+ (0, plugin_json_1.default)(),
88
+ ],
89
+ });
90
+ let format = "cjs";
91
+ let output = await bundle.generate({
92
+ generatedCode: "es5",
93
+ format: format,
94
+ exports: "named",
95
+ banner: `//format=${format}`,
96
+ outro: [
97
+ `exports.buildInfo = {sdkVersion: "${version_1.jitsuCliVersion}", sdkPackage: "${version_1.jitsuPackageName}", buildTimestamp: "${new Date().toISOString()}"};`,
98
+ `exports.streamReader$StdoutFacade = exports.streamReader && __$srcLib.stdoutStreamReader(exports.streamReader);`,
99
+ ].join("\n"),
100
+ });
101
+ let code = fixNodeFetch(output.output[0].code);
102
+ fs_1.default.mkdirSync(path_1.default.dirname(fullOutputPath), { recursive: true });
103
+ fs_1.default.writeFileSync(fullOutputPath, code);
104
+ (0, log_1.default)().info("Validating build");
105
+ const exports = await (0, _1.loadBuild)(fullOutputPath);
106
+ if (!exports.destination && !exports.transform && !(exports.streamReader && exports.sourceCatalog)) {
107
+ return {
108
+ success: false,
109
+ message: `${chalk_1.default.bold(indexFile)} should export ${chalk_1.default.italic("destination")}, ${chalk_1.default.italic("transform")} or both ${chalk_1.default.italic("streamReader")} and ${chalk_1.default.italic("sourceCatalog")} symbols. It exports: ` +
110
+ Object.keys(exports).join(", "),
111
+ };
112
+ }
113
+ else if (exports.destination && exports.transform) {
114
+ return {
115
+ success: false,
116
+ message: `${chalk_1.default.bold(indexFile)} exports both ${chalk_1.default.italic("destination")} and ${chalk_1.default.italic("transform")} symbol. It should export either of them` + Object.keys(exports).join(", "),
117
+ };
118
+ }
119
+ }
120
+ catch (e) {
121
+ if (e.id && e.loc && e.frame) {
122
+ return {
123
+ success: false,
124
+ message: "Build failed: " +
125
+ e.message +
126
+ `\n\n See: ${e.loc.file}:${e.loc.line}\n\n` +
127
+ `${e.frame
128
+ .split("\n")
129
+ .map(ln => " " + chalk_1.default.bgRed(" ") + " " + ln)
130
+ .join("\n")}`,
131
+ };
132
+ }
133
+ return { success: false, message: (0, errors_1.appendError)("Build failed", e), details: e?.stack };
134
+ }
135
+ return { success: true };
136
+ }
137
+ exports.build = build;
@@ -0,0 +1,2 @@
1
+ import { CommandResult } from "../../lib/command/types";
2
+ export declare function create(args: string[]): Promise<CommandResult>;
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.create = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const commander_1 = tslib_1.__importDefault(require("commander"));
6
+ const validate_npm_package_name_1 = tslib_1.__importDefault(require("validate-npm-package-name"));
7
+ const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
8
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
9
+ const path_1 = tslib_1.__importDefault(require("path"));
10
+ const fs_1 = tslib_1.__importDefault(require("fs"));
11
+ const log_1 = tslib_1.__importDefault(require("../../lib/log"));
12
+ const template_1 = require("../../lib/template");
13
+ const template_2 = require("./template");
14
+ async function create(args) {
15
+ const program = new commander_1.default.Command();
16
+ program.option("-t, --type <project type>", "project type (destination or source)");
17
+ program.option("-d, --dir <project_dir>", "project dir");
18
+ program.option("-n, --name <project_name>", "project name");
19
+ program.option("-j, --jitsu-version <jitsu_version>", "Jitsu version");
20
+ program.parse(["dummy", "dummy", ...args]);
21
+ let cliOpts = program.opts();
22
+ if (cliOpts.name) {
23
+ let isValid = (0, validate_npm_package_name_1.default)(cliOpts.name);
24
+ if (!isValid.validForNewPackages) {
25
+ return { success: false, message: `Can't use ${cliOpts.name} as package name: ${isValid.errors}` };
26
+ }
27
+ }
28
+ let packageType = cliOpts.type ||
29
+ (await inquirer_1.default.prompt([
30
+ {
31
+ type: "list",
32
+ name: "package",
33
+ message: [
34
+ `What is the type of extension?`,
35
+ ` ${chalk_1.default.bold("destination")} adds a new type of HTTP-based destination to Jitsu`,
36
+ ` ${chalk_1.default.bold("source")} adds a new source extension to Jitsu`,
37
+ ].join("\n"),
38
+ choices: ["destination", "source"],
39
+ },
40
+ ])).package;
41
+ let packageName = cliOpts.name ||
42
+ (await inquirer_1.default.prompt([
43
+ {
44
+ type: "input",
45
+ name: "package",
46
+ message: "Please, provide project name:",
47
+ validate: input => {
48
+ let isValid = (0, validate_npm_package_name_1.default)(input);
49
+ if (!isValid.validForNewPackages) {
50
+ return `Can't use ${input} as package name: ${isValid.errors}`;
51
+ }
52
+ return true;
53
+ },
54
+ },
55
+ ])).package;
56
+ let projectDir = cliOpts.dir ||
57
+ (await inquirer_1.default.prompt([
58
+ {
59
+ type: "input",
60
+ name: "directory",
61
+ message: "Project directory:",
62
+ default: path_1.default.resolve(".", packageName),
63
+ validate: input => {
64
+ let projectBase = path_1.default.resolve(input);
65
+ if (fs_1.default.existsSync(projectBase)) {
66
+ if (!fs_1.default.lstatSync(projectBase).isDirectory() || fs_1.default.readdirSync(projectBase).length > 0) {
67
+ return `${input} should be an non-existent or empty directory`;
68
+ }
69
+ }
70
+ return true;
71
+ },
72
+ },
73
+ ])).directory;
74
+ projectDir = path_1.default.resolve(projectDir);
75
+ (0, log_1.default)().info("Creating new jitsu project in " + chalk_1.default.bold(projectDir));
76
+ if (fs_1.default.existsSync(projectDir)) {
77
+ if (!fs_1.default.lstatSync(projectDir).isDirectory() || fs_1.default.readdirSync(projectDir).length > 0) {
78
+ return { success: false, message: `${projectDir} should be an empty directory` };
79
+ }
80
+ }
81
+ else {
82
+ (0, log_1.default)().info("Project directory doesn't exist, creating it!");
83
+ fs_1.default.mkdirSync(projectDir, { recursive: true });
84
+ }
85
+ (0, template_1.write)(projectDir, template_2.extensionProjectTemplate, {
86
+ packageName: packageName,
87
+ jitsuVersion: cliOpts.jitsuVersion,
88
+ type: packageType,
89
+ });
90
+ return { success: true };
91
+ }
92
+ exports.create = create;
@@ -0,0 +1,3 @@
1
+ import { CommandResult } from "../../lib/command/types";
2
+ export declare function execSourceExtension(args: string[]): Promise<CommandResult>;
3
+ export declare function execDestinationExtension(args: string[]): Promise<CommandResult>;
@@ -0,0 +1,296 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.execDestinationExtension = exports.execSourceExtension = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const commander_1 = tslib_1.__importDefault(require("commander"));
6
+ const log_1 = tslib_1.__importDefault(require("../../lib/log"));
7
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
8
+ const path_1 = tslib_1.__importDefault(require("path"));
9
+ const JSON5 = tslib_1.__importStar(require("json5"));
10
+ const fs_1 = tslib_1.__importDefault(require("fs"));
11
+ const index_1 = require("./index");
12
+ const indent_1 = require("../../lib/indent");
13
+ const chalk_code_highlight_1 = require("../../lib/chalk-code-highlight");
14
+ const errors_1 = require("../../lib/errors");
15
+ const validation_1 = require("../../lib/validation");
16
+ const build_1 = require("./build");
17
+ const sources_lib_1 = require("@jitsu/jlib/lib/sources-lib");
18
+ function getJson(json, file) {
19
+ if (json) {
20
+ return JSON5.parse(json);
21
+ }
22
+ else {
23
+ if (!fs_1.default.existsSync(file)) {
24
+ throw new Error(`File ${file} does not exist!`);
25
+ }
26
+ return JSON5.parse(fs_1.default.readFileSync(file, "utf-8"));
27
+ }
28
+ }
29
+ function ellipsis(str, opts = { maxLen: 80 }) {
30
+ const maxLen = opts?.maxLen || 80;
31
+ return str.length < maxLen ? str : str.substring(0, maxLen - 3) + "...";
32
+ }
33
+ function toArray(obj) {
34
+ if (!(typeof obj === "object" && Array.isArray(obj))) {
35
+ return [obj];
36
+ }
37
+ else {
38
+ return obj;
39
+ }
40
+ }
41
+ async function loadExtension(directory) {
42
+ let projectBase = path_1.default.isAbsolute(directory) ? directory : path_1.default.resolve(process.cwd() + "/" + directory);
43
+ let packageFile = path_1.default.resolve(projectBase, "package.json");
44
+ if (!fs_1.default.existsSync(packageFile)) {
45
+ throw new Error("Can't find package.json in " + projectBase);
46
+ }
47
+ let distFile = path_1.default.resolve(projectBase, (0, index_1.getDistFile)(JSON5.parse(fs_1.default.readFileSync(packageFile, "utf-8"))));
48
+ (0, log_1.default)().info("⌛️ Loading extension (don't forget to build it before running exec!). Source: " + chalk_1.default.bold(distFile));
49
+ if (!fs_1.default.existsSync(path_1.default.resolve(projectBase, distFile))) {
50
+ throw new Error(`Can't find dist file ${chalk_1.default.bold(distFile)}. Does this dir contains jitsu extension? Have you run yarn build? `);
51
+ }
52
+ let extension = await (0, index_1.loadBuild)(distFile);
53
+ return { extension, distFile, projectBase };
54
+ }
55
+ async function execSourceExtension(args) {
56
+ const program = new commander_1.default.Command();
57
+ program.option("-c, --config <config_file_or_json>", "Configuration file path or inline extension configuration JSON");
58
+ program.option("-d, --dir <project_dir>", "project dir");
59
+ program.option("-s, --stream-config <json>", "Stream configuration as an object.");
60
+ program.option("-t, --state <file>", "Saved state object to file");
61
+ program.parse(["dummy", "dummy", ...args]);
62
+ let cliOpts = program.opts();
63
+ let directory = cliOpts.dir || ".";
64
+ await (0, build_1.build)([directory]);
65
+ const { extension } = await loadExtension(directory);
66
+ if (!extension.streamReader) {
67
+ return { success: false, message: `Extension doesn't export ${chalk_1.default.bold("streamReader")} symbol` };
68
+ }
69
+ if (!extension.sourceCatalog) {
70
+ return { success: false, message: `Extension doesn't export ${chalk_1.default.bold("sourceCatalog")} symbol` };
71
+ }
72
+ let configObject;
73
+ try {
74
+ configObject = (0, index_1.getConfigJson)(cliOpts.config);
75
+ }
76
+ catch (e) {
77
+ return {
78
+ success: false,
79
+ message: `Can't parse config JSON: '${cliOpts.config}' ${e.message})`,
80
+ };
81
+ }
82
+ const stateFile = path_1.default.resolve(cliOpts.state || `./src-${extension?.descriptor?.id || ""}-state.json`);
83
+ const stateFilePresent = fs_1.default.existsSync(stateFile);
84
+ if (stateFilePresent) {
85
+ (0, log_1.default)().info(`Loading state from ${chalk_1.default.bold(path_1.default.isAbsolute(stateFile))}`);
86
+ }
87
+ else {
88
+ (0, log_1.default)().info(`State file is missing, starting with empty state ${chalk_1.default.bold(stateFile)}`);
89
+ }
90
+ const stateObject = stateFilePresent ? JSON5.parse(fs_1.default.readFileSync(stateFile, "utf-8")) : {};
91
+ if (!extension.validator) {
92
+ (0, log_1.default)().info("⚠️ Extension doesn't support connection validation");
93
+ }
94
+ else {
95
+ (0, log_1.default)().info("⌛️ Validating connection parameters...");
96
+ let validationError = await (0, validation_1.validateConfiguration)(configObject, extension.validator);
97
+ if (validationError) {
98
+ return {
99
+ success: false,
100
+ message: `Configuration validation failed: ${validationError}`,
101
+ };
102
+ }
103
+ (0, log_1.default)().info("🙌️ Configuration is valid!");
104
+ }
105
+ (0, log_1.default)().info("🏃 Getting available streams...");
106
+ let streams = await extension.sourceCatalog(configObject);
107
+ streams.forEach(stream => {
108
+ let paramsDocs = (stream.params ?? []).map(param => `${param.id} - ${param.displayName}`).join(", ");
109
+ (0, log_1.default)().info(`🌊 Stream: ${chalk_1.default.bold(stream.type)}. Parameters: ${paramsDocs.length > 0 ? paramsDocs : "none"}`);
110
+ });
111
+ let stream;
112
+ let streamConfigObject = cliOpts.streamConfig && JSON5.parse(cliOpts.streamConfig);
113
+ let mode = streamConfigObject?.mode;
114
+ if (streams.length > 1) {
115
+ if (!streamConfigObject) {
116
+ return {
117
+ success: false,
118
+ message: `The connector exports more than one (${streams.length}) streams. Please specify stream name and config as -s {stream: 'name', ...}`,
119
+ };
120
+ }
121
+ if (!streamConfigObject?.name) {
122
+ return {
123
+ success: false,
124
+ message: `Connector defines multiple streams (${streams.map(s => s.type)}). Specify stream name as as: -s {name: 'name', ...}`,
125
+ };
126
+ }
127
+ stream = streams.find(stream => stream.type === streamConfigObject?.name);
128
+ if (!stream) {
129
+ return { success: false, message: `Stream with ${streamConfigObject?.name} is not found` };
130
+ }
131
+ }
132
+ else {
133
+ stream = streams[0];
134
+ }
135
+ const modes = stream.supportedModes;
136
+ if (!mode && modes.length > 1) {
137
+ return {
138
+ success: false,
139
+ message: `Stream ${stream.type} supports multiple modes (${modes.join(", ")}). Please specify mode as -s {mode: '${modes[0]}', ...}`,
140
+ };
141
+ }
142
+ if (mode && !modes.includes(mode)) {
143
+ return {
144
+ success: false,
145
+ message: `Stream ${stream.type} doesn't support mode ${mode}. Supported modes: ${modes.join(", ")}`,
146
+ };
147
+ }
148
+ const effectiveMode = mode || modes[0];
149
+ const resultTable = newTable();
150
+ const sink = (0, sources_lib_1.makeStreamSink)({
151
+ msg(msg) {
152
+ if (msg.type === "record") {
153
+ (0, log_1.default)().info("[" + msg.type + "] " + (msg.message ? JSON.stringify(msg.message) : ""));
154
+ add(resultTable, msg.message);
155
+ }
156
+ else if (msg.type === "log") {
157
+ (0, log_1.default)()[msg.message?.["level"].toLowerCase()]?.("[" + msg.type + "] " + msg.message?.["message"]);
158
+ }
159
+ else if (msg.type === "state") {
160
+ (0, log_1.default)().info("[" + msg.type + "] " + (msg.message ? JSON.stringify(msg.message) : ""));
161
+ (0, log_1.default)().info(`💾 State was modified. Saving to: ${chalk_1.default.bold(path_1.default.isAbsolute(stateFile))}`);
162
+ fs_1.default.writeFileSync(stateFile, JSON.stringify(msg.message, null, 2));
163
+ }
164
+ else {
165
+ (0, log_1.default)().info("[" + msg.type + "] " + (msg.message ? JSON.stringify(msg.message) : ""));
166
+ }
167
+ },
168
+ }, effectiveMode);
169
+ let streamConfiguration = { parameters: streamConfigObject };
170
+ if (mode) {
171
+ streamConfiguration.mode = mode;
172
+ }
173
+ await extension.streamReader(configObject, stream.type, streamConfiguration, sink, {
174
+ state: (0, sources_lib_1.stateService)(stateObject, sink),
175
+ });
176
+ (0, log_1.default)().info("🏁 Result data:");
177
+ console.log(JSON.stringify(resultTable.rows, null, 2));
178
+ console.log("Special column types: \n" +
179
+ Object.entries(resultTable.columns)
180
+ .filter(([colName, colValue]) => colValue.types.length > 0)
181
+ .map(([colName, colValue]) => `\t${colName}: ${colValue.types}`)
182
+ .join("\n"));
183
+ return { success: true };
184
+ }
185
+ exports.execSourceExtension = execSourceExtension;
186
+ function newTable() {
187
+ return { rows: [], columns: {} };
188
+ }
189
+ function add(t, rec) {
190
+ const newRow = {};
191
+ for (const [key, val] of Object.entries(rec)) {
192
+ if (key.indexOf("__sql_type") !== 0) {
193
+ if (t.columns[key] === undefined) {
194
+ t.columns[key] = { types: [] };
195
+ t.rows.forEach(row => (row[key] = undefined));
196
+ }
197
+ newRow[key] = Array.isArray(val) ? JSON.stringify(val) : val;
198
+ }
199
+ else {
200
+ const columnName = key.substring("__sql_type_".length);
201
+ if (t.columns[columnName] === undefined) {
202
+ t.columns[columnName] = { types: [] };
203
+ }
204
+ let uniqueTypes = new Set(t.columns[columnName].types);
205
+ uniqueTypes.add(val);
206
+ t.columns[columnName].types = [...uniqueTypes];
207
+ }
208
+ }
209
+ Object.keys(t.columns).forEach(col => {
210
+ if (newRow[col] === undefined) {
211
+ newRow[col] = undefined;
212
+ }
213
+ });
214
+ t.rows.push(newRow);
215
+ }
216
+ async function execDestinationExtension(args) {
217
+ const program = new commander_1.default.Command();
218
+ program.option("-f, --file <file path>", "Path to file with jitsu events. Could be either on object, or array of objects");
219
+ program.option("-c, --config <config_file_or_json>", "Configuration file path or inline extension configuration JSON");
220
+ program.option("-j, --json <event json>", "Events JSON for processing (alternative to -f). Could be one object, or array of objects");
221
+ program.option("-d, --dir <project_dir>", "project dir");
222
+ program.option("-v, --skip-validation", "To skip config validation");
223
+ program.parse(["dummy", "dummy", ...args]);
224
+ let cliOpts = program.opts();
225
+ let directory = cliOpts.dir || ".";
226
+ if (!cliOpts.json && !cliOpts.file) {
227
+ return { success: false, message: "Please specify -j or -f" };
228
+ }
229
+ if (cliOpts.json && cliOpts.file) {
230
+ return { success: false, message: "Both options -f and -j are provided. You should use either, not both" };
231
+ }
232
+ if (!cliOpts.config) {
233
+ return { success: false, message: "Please define config object -c json_file_path or -c '{json_object:}'" };
234
+ }
235
+ let config;
236
+ try {
237
+ config = (0, index_1.getConfigJson)(cliOpts.config);
238
+ }
239
+ catch (e) {
240
+ return {
241
+ success: false,
242
+ message: `Can't parse config JSON: '${cliOpts.config}' ${e.message})`,
243
+ };
244
+ }
245
+ const { extension, projectBase } = await loadExtension(directory);
246
+ (0, log_1.default)().info("🛂 Executing tests destination on " + chalk_1.default.bold(projectBase));
247
+ let events = toArray(getJson(cliOpts.json, cliOpts.file));
248
+ if (!extension.destination) {
249
+ return { success: false, message: "Extension doesn't export destination function" };
250
+ }
251
+ if (!cliOpts.skipValidation && extension.validator) {
252
+ (0, log_1.default)().info(`Validating configuration:${chalk_code_highlight_1.chalkCode.json((0, indent_1.align)(JSON.stringify(config, null, 2), { indent: 4, lnBefore: 1, lnAfter: 1 }))}`);
253
+ let configError = await (0, validation_1.validateConfiguration)(config, extension.validator);
254
+ if (configError) {
255
+ return { success: false, message: "Config is not valid: " + configError };
256
+ }
257
+ (0, log_1.default)().info("✅ Configuration is valid!");
258
+ }
259
+ else {
260
+ (0, log_1.default)().info("💡 Config validation will be skipped " +
261
+ (cliOpts.skipValidation ? " as per requested by -v flag" : ": extension does not have an exported validator"));
262
+ }
263
+ (0, log_1.default)().info("🏃 Running destination plugin on " + events.length + " events");
264
+ for (const ev of events) {
265
+ try {
266
+ let messages = await extension.destination(ev, {
267
+ destinationId: "test",
268
+ destinationType: "test",
269
+ config: config,
270
+ });
271
+ if (!messages) {
272
+ (0, log_1.default)().info("⚽ Event is skipped: " + ellipsis(JSON.stringify(ev)));
273
+ continue;
274
+ }
275
+ const messagesArray = Array.isArray(messages) ? messages : [messages];
276
+ (0, log_1.default)().info(`✅ Event emitted ${messagesArray.length} messages. Event JSON: ` + chalk_1.default.italic(ellipsis(JSON.stringify(ev))));
277
+ messagesArray.forEach(msg => {
278
+ (0, log_1.default)().info(` ${chalk_1.default.bold(msg.method)} ${msg.url}`);
279
+ if (msg.headers && Object.entries(msg).length > 0) {
280
+ Object.entries(msg.headers).forEach(([h, v]) => {
281
+ (0, log_1.default)().info(` ${chalk_1.default.bold(h)}: ${v}`);
282
+ });
283
+ }
284
+ if (msg.body) {
285
+ (0, log_1.default)().info(" Body:\n" + chalk_code_highlight_1.chalkCode.json((0, indent_1.align)(JSON.stringify((0, indent_1.jsonify)(msg.body), null, 2), { indent: 8 })));
286
+ }
287
+ });
288
+ }
289
+ catch (e) {
290
+ (0, log_1.default)().info(chalk_1.default.red((0, errors_1.appendError)("❌ Failed to process event", e) +
291
+ (0, indent_1.align)(JSON.stringify(ev, null, 2), { lnBefore: 1, lnAfter: 1, indent: 0 })));
292
+ }
293
+ }
294
+ return { success: true };
295
+ }
296
+ exports.execDestinationExtension = execDestinationExtension;
@@ -0,0 +1,9 @@
1
+ import { CommandRegistry } from "../../lib/command/types";
2
+ import { JitsuExtensionExport } from "@jitsu/types/extension";
3
+ import { Partial } from "rollup-plugin-typescript2/dist/partial";
4
+ export declare const help: string;
5
+ export declare const extensionCommands: CommandRegistry<"test" | "build" | "create" | "validate-config" | "exec" | "exec-src">;
6
+ export declare function validateTsConfig(tsConfigPath: string): void;
7
+ export declare function getDistFile(packageJson: any): any;
8
+ export declare function getConfigJson(jsonOrFile: string): any;
9
+ export declare function loadBuild(file: string): Promise<Partial<JitsuExtensionExport>>;