@hahnpro/flow-cli 2.17.20 → 2025.10.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.
package/main.js ADDED
@@ -0,0 +1,1020 @@
1
+ #!/usr/bin/env node
2
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
+ }) : x)(function(x) {
5
+ if (typeof require !== "undefined") return require.apply(this, arguments);
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
+ });
8
+
9
+ // lib/flow-cli/src/main.ts
10
+ import "reflect-metadata";
11
+ import "dotenv/config";
12
+ import fs2 from "node:fs";
13
+ import path3 from "node:path";
14
+ import archiver from "archiver";
15
+ import Axios from "axios";
16
+ import { Command } from "commander";
17
+ import copyfiles from "copyfiles";
18
+ import { execa } from "execa";
19
+ import FormData from "form-data";
20
+ import { glob } from "glob";
21
+ import { HttpsProxyAgent as HttpsProxyAgent2 } from "https-proxy-agent";
22
+ import ora from "ora";
23
+
24
+ // lib/flow-cli/src/auth.ts
25
+ import path2 from "node:path";
26
+ import express from "express";
27
+ import getPort from "get-port";
28
+ import { HttpsProxyAgent } from "https-proxy-agent";
29
+ import nconf from "nconf";
30
+ import open from "open";
31
+ import openidClient, { custom } from "openid-client";
32
+
33
+ // lib/flow-cli/src/utils.ts
34
+ import fs from "node:fs/promises";
35
+ import path from "node:path";
36
+ import chalk from "chalk";
37
+ var defaultLogger = {
38
+ /* eslint-disable no-console */
39
+ log: console.log,
40
+ error: console.error,
41
+ ok: console.info
42
+ /* eslint-enable no-console */
43
+ };
44
+ function checkTypes(definedTypes, propertiesSchema, jsonPath, logger1 = defaultLogger) {
45
+ const knownTypes = /* @__PURE__ */ new Set([
46
+ ...definedTypes,
47
+ "string",
48
+ "undefined",
49
+ "number",
50
+ "boolean",
51
+ "any",
52
+ "object",
53
+ "array",
54
+ "integer",
55
+ "Asset",
56
+ "AssetType",
57
+ "Flow",
58
+ "Secret",
59
+ "TimeSeries"
60
+ ]);
61
+ const properties = propertiesSchema.properties || {};
62
+ for (const property of Object.keys(properties)) {
63
+ if (properties[property].type && !knownTypes.has(properties[property].type)) {
64
+ logger1.error(
65
+ `ERROR: unknown type ${properties[property].type}.
66
+ Please add a schema for this type in ${jsonPath}
67
+ for more info check the documentation`
68
+ );
69
+ return false;
70
+ }
71
+ }
72
+ return true;
73
+ }
74
+ async function getTypes(filePath) {
75
+ try {
76
+ const json = JSON.parse(await fs.readFile(path.join(process.cwd(), filePath), "utf-8"));
77
+ return json.definitions ? Object.keys(json.definitions) : [];
78
+ } catch {
79
+ return [];
80
+ }
81
+ }
82
+ async function handleConvertedOutput(result, jsonPath, json, logger1 = defaultLogger) {
83
+ let schema;
84
+ try {
85
+ schema = JSON.parse(result);
86
+ } catch {
87
+ logger1.error(result);
88
+ return json;
89
+ }
90
+ const values = [
91
+ ["propertiesSchema", "Properties"],
92
+ ["inputStreams", "InputProperties"],
93
+ ["outputStreams", "OutputProperties"]
94
+ ];
95
+ for (const value of values) {
96
+ const propertiesSchema = schema[value[1]] || {};
97
+ for (const requestProperty of propertiesSchema.required || []) {
98
+ propertiesSchema.properties[requestProperty] = { ...propertiesSchema.properties[requestProperty], required: true };
99
+ }
100
+ delete propertiesSchema.required;
101
+ const types = await getTypes(jsonPath);
102
+ checkTypes(types, propertiesSchema, jsonPath);
103
+ const completeSchema = {
104
+ schema: {
105
+ type: "object",
106
+ properties: {
107
+ ...propertiesSchema.properties
108
+ }
109
+ }
110
+ };
111
+ if (value[0] === "propertiesSchema") {
112
+ if (!json["propertiesSchema"]) {
113
+ json["propertiesSchema"] = completeSchema;
114
+ }
115
+ } else {
116
+ if (!json[value[0]].some((v) => v.name === "default") && propertiesSchema) {
117
+ json[value[0]].push({
118
+ name: "default",
119
+ ...completeSchema
120
+ });
121
+ }
122
+ }
123
+ }
124
+ if (Object.keys(schema).some((key) => !["Properties", "InputProperties", "OutputProperties"].includes(key))) {
125
+ const typeDefinitions = Object.keys(schema).filter((key) => !["Properties", "InputProperties", "OutputProperties"].includes(key));
126
+ json.definitions = typeDefinitions.reduce((previousValue, currentValue) => {
127
+ const additionalSchema = schema[currentValue];
128
+ for (const requestProperty of additionalSchema.required || []) {
129
+ additionalSchema.properties[requestProperty] = { ...additionalSchema.properties[requestProperty], required: true };
130
+ }
131
+ delete additionalSchema.required;
132
+ previousValue[currentValue] = additionalSchema;
133
+ return previousValue;
134
+ }, {});
135
+ }
136
+ return json;
137
+ }
138
+ function prepareTsFile(file) {
139
+ const codeBlocks = getCodeBlocks(file);
140
+ const emptyExtendsBlock = codeBlocks.find((block) => blockDefinitionIncludes(block, "extends") && isBlockEmpty(block));
141
+ if (emptyExtendsBlock) {
142
+ let replBlock = `${emptyExtendsBlock}`;
143
+ if (replBlock.replace(/\s\s+/g, " ").trim().startsWith("class OutputProperties")) {
144
+ replBlock = replBlock.replace("extends InputProperties", "");
145
+ const inputPropertiesBlock = codeBlocks.find(
146
+ (v) => blockDefinitionIncludes(v, "InputProperties") && !blockDefinitionIncludes(v, "OutputProperties")
147
+ );
148
+ replBlock = replBlock.replace(getBlockContent(replBlock), getBlockContent(inputPropertiesBlock));
149
+ file = file.replace(emptyExtendsBlock, replBlock);
150
+ }
151
+ }
152
+ return `import { validationMetadatasToSchemas as v } from 'class-validator-jsonschema';
153
+ import { defaultMetadataStorage as classTransformerDefaultMetadataStorage } from 'class-transformer/cjs/storage';
154
+ ${file}
155
+ const s = v({
156
+
157
+ additionalConverters: {
158
+
159
+ UnitArgsValidator: (meta) => {
160
+
161
+ return {
162
+
163
+ measure: meta.constraints[0],
164
+
165
+ unit: meta.constraints[1],
166
+
167
+ type: 'number',
168
+
169
+ };
170
+
171
+ },
172
+
173
+ },
174
+
175
+ classTransformerMetadataStorage
176
+
177
+ });
178
+ console.log(JSON.stringify(s));`;
179
+ }
180
+ function getCodeBlocks(string_) {
181
+ const blocks = [];
182
+ let counter = 0;
183
+ let start = 0;
184
+ let lastNewline = 0;
185
+ for (const [index, char] of [...string_].entries()) {
186
+ if (char === "\n") {
187
+ lastNewline = index;
188
+ }
189
+ if (char === "{") {
190
+ if (counter === 0) {
191
+ start = lastNewline;
192
+ }
193
+ counter++;
194
+ } else if (char === "}") {
195
+ counter--;
196
+ if (counter === 0) {
197
+ blocks.push(string_.slice(start, index + 1));
198
+ }
199
+ }
200
+ }
201
+ return blocks;
202
+ }
203
+ var logger = {
204
+ /* eslint-disable no-console */
205
+ log: console.log,
206
+ error: (message) => console.log(chalk.bold.red(message)),
207
+ ok: (message) => console.log(chalk.bold.green(message))
208
+ /* eslint-enable no-console */
209
+ };
210
+ function handleApiError(error) {
211
+ if (error.isAxiosError && error.response) {
212
+ logger.error(`${error.response.status} ${error.response.statusText}`);
213
+ if (error.response.data) {
214
+ logger.error(JSON.stringify(error.response.data));
215
+ }
216
+ } else {
217
+ logger.error(error);
218
+ }
219
+ }
220
+ function blockDefinitionIncludes(block, value) {
221
+ return block.trim().split("\n", 1)[0].includes(value);
222
+ }
223
+ function getBlockContent(block) {
224
+ return block.slice(block.indexOf("{"), block.lastIndexOf("}") + 1);
225
+ }
226
+ function isBlockEmpty(block) {
227
+ const blockContent = block.slice(block.indexOf("{") + 1, block.lastIndexOf("}"));
228
+ return !blockContent.trim();
229
+ }
230
+
231
+ // lib/flow-cli/src/auth.ts
232
+ var BASE_URL = process.env.BASE_URL || process.env.PLATFORM_URL;
233
+ var CLIENT_ID = process.env.CLIENT_ID || process.env.API_USER || "flow-cli";
234
+ var CLIENT_SECRET = process.env.CLIENT_SECRET || process.env.API_KEY;
235
+ var REALM = process.env.REALM;
236
+ var BUFFER = 120;
237
+ var viewsPath = path2.join(__dirname, "views");
238
+ var server = null;
239
+ nconf.file({ file: path2.join(__dirname, "config") });
240
+ if (process.env.https_proxy || process.env.http_proxy) {
241
+ custom.setHttpOptionsDefaults({
242
+ agent: new HttpsProxyAgent(process.env.https_proxy || process.env.http_proxy)
243
+ });
244
+ }
245
+ async function getAccessToken(baseUrl = BASE_URL, realm = REALM) {
246
+ checkEnvironment([
247
+ ["BASE_URL", baseUrl],
248
+ ["REALM", realm]
249
+ ]);
250
+ nconf.load();
251
+ let tokenSet = nconf.get(baseUrl.replace(/:/g, "")) || {};
252
+ if (tokenSet.access_token && tokenSet.expires_at > Date.now() / 1e3 + BUFFER) {
253
+ return tokenSet.access_token;
254
+ } else {
255
+ return CLIENT_ID && CLIENT_SECRET ? new Promise(async (resolve, reject) => {
256
+ try {
257
+ const kcIssuer = await openidClient.Issuer.discover(`${baseUrl}/auth/realms/${realm}/`);
258
+ const client = new kcIssuer.Client({
259
+ client_id: CLIENT_ID,
260
+ client_secret: CLIENT_SECRET,
261
+ token_endpoint_auth_method: "client_secret_jwt"
262
+ });
263
+ tokenSet = await client.grant({ grant_type: "client_credentials" });
264
+ nconf.set(baseUrl.replace(/:/g, ""), tokenSet);
265
+ nconf.save((error) => {
266
+ if (error) {
267
+ logger.error(error);
268
+ }
269
+ return resolve(tokenSet.access_token);
270
+ });
271
+ } catch (error) {
272
+ return reject(error);
273
+ }
274
+ }) : login(baseUrl, realm);
275
+ }
276
+ }
277
+ function login(baseUrl = BASE_URL, realm = REALM) {
278
+ return new Promise(async (resolve, reject) => {
279
+ checkEnvironment([
280
+ ["BASE_URL", baseUrl],
281
+ ["REALM", realm]
282
+ ]);
283
+ server = null;
284
+ const port = process.env.PORT || await getPort({ port: 3e3 });
285
+ const redirectUri = `http://localhost:${port}/callback`;
286
+ const kcIssuer = await openidClient.Issuer.discover(`${baseUrl}/auth/realms/${realm}/`);
287
+ const client = new kcIssuer.Client({
288
+ client_id: "flow-cli",
289
+ redirect_uris: [redirectUri],
290
+ response_types: ["code"],
291
+ token_endpoint_auth_method: "none"
292
+ });
293
+ const code_verifier = openidClient.generators.codeVerifier();
294
+ const code_challenge = openidClient.generators.codeChallenge(code_verifier);
295
+ const auhtUrl = client.authorizationUrl({ code_challenge, code_challenge_method: "S256" });
296
+ const app = express();
297
+ app.disable("x-powered-by");
298
+ app.use(express.static(viewsPath));
299
+ app.set("views", viewsPath);
300
+ app.get("/callback", async (request, response) => {
301
+ const parameters = client.callbackParams(request.url);
302
+ if (parameters.code) {
303
+ response.render("index.ejs", { message: "Authentication successful", hint: "You can close this window" });
304
+ } else {
305
+ response.render("index.ejs", { message: "Authentication failed", hint: "Please try again" });
306
+ }
307
+ response.end();
308
+ try {
309
+ const tokenSet = await client.callback(redirectUri, parameters, { code_verifier });
310
+ nconf.set(baseUrl.replace(/:/g, ""), tokenSet);
311
+ server.close();
312
+ nconf.save((error) => {
313
+ if (error) {
314
+ logger.error(error);
315
+ }
316
+ return resolve(tokenSet.access_token);
317
+ });
318
+ } catch (error) {
319
+ return reject(error);
320
+ }
321
+ });
322
+ server = await app.listen(port);
323
+ await open(auhtUrl);
324
+ });
325
+ }
326
+ function logout(baseUrl = BASE_URL) {
327
+ return new Promise((resolve, reject) => {
328
+ if (baseUrl) {
329
+ nconf.clear(baseUrl.replace(/:/g, ""));
330
+ } else {
331
+ nconf.reset();
332
+ }
333
+ server = null;
334
+ nconf.save((error) => {
335
+ return error ? reject(error) : resolve();
336
+ });
337
+ });
338
+ }
339
+ function checkEnvironment(values) {
340
+ let missing = false;
341
+ for (const [name, value] of values) {
342
+ if (!value && !process.env[name]) {
343
+ logger.error(`"${name}" env var is required`);
344
+ missing = true;
345
+ }
346
+ }
347
+ if (missing) {
348
+ throw new Error("Missing environment variables");
349
+ }
350
+ }
351
+
352
+ // lib/flow-cli/src/main.ts
353
+ var BASE_URL2 = process.env.BASE_URL || process.env.PLATFORM_URL;
354
+ var BUILD_DIR = process.env.BUILD_DIR || "dist";
355
+ var axios = Axios.create();
356
+ if (process.env.https_proxy || process.env.http_proxy) {
357
+ const httpsAgent = new HttpsProxyAgent2(process.env.https_proxy || process.env.http_proxy);
358
+ axios = Axios.create({ httpsAgent, proxy: false });
359
+ }
360
+ var apiToken;
361
+ var projectsRoot = "modules";
362
+ var packageJson = JSON.parse(fs2.readFileSync(path3.join(__dirname, "..", "package.json")).toString());
363
+ var CMD = {
364
+ AUDIT: "audit",
365
+ BUILD: "build",
366
+ FORMAT: "format",
367
+ INSTALL: "install",
368
+ LINT: "lint",
369
+ RUN: "run",
370
+ TEST: "test",
371
+ WATCH: "watch"
372
+ };
373
+ var program = new Command();
374
+ program.version(packageJson.version, "-v, --version").usage("[command] [options]").description("Flow Module Management Tool").on("--help", () => {
375
+ });
376
+ program.command("build [moduleNames]").description("Build specified Module(s)").action(async (moduleNames) => {
377
+ try {
378
+ const projects = await selectProjects(moduleNames);
379
+ for (const project of projects) {
380
+ await exec(CMD.INSTALL, project);
381
+ await exec(CMD.BUILD, project);
382
+ await copyProjectFiles(project);
383
+ }
384
+ } catch (error) {
385
+ if (error) {
386
+ logger.log(error);
387
+ }
388
+ process.exit(1);
389
+ }
390
+ });
391
+ program.command("install [moduleNames]").description("Install the dependencies of the specified Module(s)").action(async (moduleNames) => {
392
+ try {
393
+ const projects = await selectProjects(moduleNames);
394
+ for (const project of projects) {
395
+ await exec(CMD.INSTALL, project);
396
+ }
397
+ } catch (error) {
398
+ if (error) {
399
+ logger.log(error);
400
+ }
401
+ process.exit(1);
402
+ }
403
+ });
404
+ program.command("audit [moduleNames]").description("Audit dependencies for the specified Module(s)").action(async (moduleNames) => {
405
+ try {
406
+ const projects = await selectProjects(moduleNames);
407
+ for (const project of projects) {
408
+ await exec(CMD.AUDIT, project);
409
+ }
410
+ } catch (error) {
411
+ if (error) {
412
+ logger.log(error);
413
+ }
414
+ process.exit(1);
415
+ }
416
+ });
417
+ program.command("lint [moduleNames]").description("Lint source files for the specified Module(s)").action(async (moduleNames) => {
418
+ try {
419
+ const projects = await selectProjects(moduleNames);
420
+ for (const project of projects) {
421
+ await exec(CMD.LINT, project);
422
+ }
423
+ } catch (error) {
424
+ if (error) {
425
+ logger.log(error);
426
+ }
427
+ process.exit(1);
428
+ }
429
+ });
430
+ program.command("login").requiredOption("--url <url>", "URL of target platform", process.env.BASE_URL).requiredOption("-r, --realm <realm>", "Auth realm of target platform", process.env.REALM).description("Authenticate against platform").action(async (options) => {
431
+ try {
432
+ await login(options.url, options.realm);
433
+ process.exit(0);
434
+ } catch (error) {
435
+ if (error) {
436
+ logger.log(error);
437
+ }
438
+ process.exit(1);
439
+ }
440
+ });
441
+ program.command("logout").requiredOption("--url <url>", "URL of target platform", process.env.BASE_URL).description("Remove authentication data").action(async (options) => {
442
+ try {
443
+ await logout(options.url);
444
+ process.exit(0);
445
+ } catch (error) {
446
+ if (error) {
447
+ logger.log(error);
448
+ }
449
+ process.exit(1);
450
+ }
451
+ });
452
+ program.command("format").description("Format all typescript files according to prettier configuration").action(async () => {
453
+ try {
454
+ await exec(CMD.FORMAT, { name: "all" });
455
+ } catch (error) {
456
+ if (error) {
457
+ logger.log(error);
458
+ }
459
+ process.exit(1);
460
+ }
461
+ });
462
+ program.command("package [moduleNames]").description("Build specified Module(s) and package as .zip file for manual upload to the platform").action(async (moduleNames) => {
463
+ try {
464
+ const projects = await selectProjects(moduleNames);
465
+ await clean(BUILD_DIR);
466
+ for (const project of projects) {
467
+ await exec(CMD.INSTALL, project);
468
+ await exec(CMD.BUILD, project);
469
+ await copyProjectFiles(project);
470
+ await validateModule(project);
471
+ await packageModule(project);
472
+ }
473
+ } catch (error) {
474
+ if (error) {
475
+ logger.log(error);
476
+ }
477
+ process.exit(1);
478
+ }
479
+ });
480
+ program.command("publish-module [moduleNames]").requiredOption("--url <url>", "URL of target platform", process.env.BASE_URL).requiredOption("-r, --realm <realm>", "Auth realm of target platform", process.env.REALM).option("-f, --functions", "publish flow functions").option("-u, --update", "update existing flow functions").option("-s, --skip", "skip modules that already exists with the current version").description("Publish specified Module(s) to Cloud Platform").action(async (moduleNames, options) => {
481
+ try {
482
+ const projects = await selectProjects(moduleNames);
483
+ apiToken = await getAccessToken(options.url, options.realm);
484
+ logger.ok("Got Access Token");
485
+ await clean(BUILD_DIR);
486
+ for (const project of projects) {
487
+ await exec(CMD.INSTALL, project);
488
+ await exec(CMD.BUILD, project);
489
+ await copyProjectFiles(project);
490
+ await validateModule(project);
491
+ try {
492
+ await publishModule(project, options.url);
493
+ } catch (error) {
494
+ if (options.skip && error && error.response && error.response.data && error.response.data.message === "New module version must greater than latest version") {
495
+ logger.ok(`Module "${project.name}" is up to date. Skipping.`);
496
+ } else {
497
+ logger.error(`Publishing Module "${project.name}" failed.`);
498
+ handleApiError(error);
499
+ process.exit(1);
500
+ }
501
+ }
502
+ if (options.functions) {
503
+ await publishFunctions(project, options.update, options.url);
504
+ }
505
+ }
506
+ } catch (error) {
507
+ if (error) {
508
+ logger.log(error);
509
+ }
510
+ process.exit(1);
511
+ }
512
+ });
513
+ program.command("publish-functions [moduleNames]").requiredOption("--url <url>", "URL of target platform", process.env.BASE_URL).requiredOption("-r, --realm <realm>", "Auth realm of target platform", process.env.REALM).option("-u, --update", "update existing flow functions").description("Publish all Flow Functions inside specified Module(s) to Cloud Platform").action(async (moduleNames, options) => {
514
+ try {
515
+ const projects = await selectProjects(moduleNames);
516
+ apiToken = await getAccessToken(options.url, options.realm);
517
+ logger.ok("Got Access Token");
518
+ for (const project of projects) {
519
+ await publishFunctions(project, options.update, options.url);
520
+ }
521
+ } catch (error) {
522
+ if (error) {
523
+ logger.log(error);
524
+ }
525
+ process.exit(1);
526
+ }
527
+ });
528
+ program.command("serve [projectName]").description("Builds and serves your project. Rebuilding on file changes").action(async (projectName) => {
529
+ try {
530
+ if (checkIfAll(projectName)) {
531
+ process.exit(1);
532
+ }
533
+ const project = await findProject(projectName);
534
+ await exec(CMD.INSTALL, project);
535
+ await exec(CMD.BUILD, project);
536
+ await copyProjectFiles(project);
537
+ await exec(CMD.WATCH, project);
538
+ } catch (error) {
539
+ if (error) {
540
+ logger.log(error);
541
+ }
542
+ process.exit(1);
543
+ }
544
+ });
545
+ program.command("start [projectName]").description("Runs your project").action(async (projectName) => {
546
+ try {
547
+ if (checkIfAll(projectName)) {
548
+ process.exit(1);
549
+ }
550
+ const project = await findProject(projectName);
551
+ await exec(CMD.RUN, project);
552
+ } catch (error) {
553
+ if (error) {
554
+ logger.log(error);
555
+ }
556
+ process.exit(1);
557
+ }
558
+ });
559
+ program.command("test [moduleNames]").description("Runs tests for specified Module(s)").action(async (moduleNames) => {
560
+ try {
561
+ let projects = await selectProjects(moduleNames);
562
+ if (process.env.CI) {
563
+ projects = projects.filter((project) => !project["excludeTestsInCI"]);
564
+ }
565
+ for (const project of projects) {
566
+ await exec(CMD.TEST, project);
567
+ }
568
+ } catch (error) {
569
+ if (error) {
570
+ logger.log(error);
571
+ }
572
+ process.exit(1);
573
+ }
574
+ });
575
+ program.command("generate-schemas [projectName]").description("Generates Input, Output and Properties-Schemas for the project").option("-h, --hide", "hide warnings").option("-v, --verbose", "get more output info").action(async (projectName) => {
576
+ try {
577
+ const project = await findProject(projectName);
578
+ const globOptions = {
579
+ cwd: project.location,
580
+ ignore: ["node_modules/**/*", "**/package*.json", "**/tsconfig*.json"]
581
+ };
582
+ const files = await glob("**/*.*", globOptions);
583
+ const filtered = files.filter((file) => !file.endsWith(".spec.ts"));
584
+ const tsJsonMap = filtered.reduce((accumulator, current, index, array) => {
585
+ if (current.endsWith(".ts")) {
586
+ const json = array.find((v) => v === `${current.split(".")[0]}.json`);
587
+ if (json) {
588
+ accumulator.push({
589
+ ts: path3.join(globOptions.cwd, current),
590
+ json: path3.join(globOptions.cwd, json)
591
+ });
592
+ }
593
+ }
594
+ return accumulator;
595
+ }, []);
596
+ for (const entry of tsJsonMap) {
597
+ await generateSchemasForFile(entry.ts, entry.json);
598
+ }
599
+ } catch (error) {
600
+ if (error) {
601
+ logger.log(error);
602
+ }
603
+ process.exit(1);
604
+ }
605
+ });
606
+ if (process.env.NODE_ENV !== "test") {
607
+ program.parse(process.argv);
608
+ }
609
+ async function generateSchemasForFile(tsPath, jsonPath) {
610
+ const fileContent = await fs2.promises.readFile(path3.join(process.cwd(), jsonPath));
611
+ let json = JSON.parse(fileContent.toString());
612
+ const filePath = path3.join(process.cwd(), tsPath);
613
+ const tsFile = await fs2.promises.readFile(filePath);
614
+ const directory = path3.dirname(filePath);
615
+ const result = await execa("ts-node", ["-T", "--dir", directory], { input: prepareTsFile(tsFile.toString()), preferLocal: true });
616
+ json = await handleConvertedOutput(result.stdout, jsonPath, json);
617
+ await fs2.promises.writeFile(path3.join(process.cwd(), jsonPath), JSON.stringify(json, null, 2) + "\n");
618
+ }
619
+ async function clean(buildFolder) {
620
+ return new Promise((resolve, reject) => {
621
+ const spinner = getSpinner("Cleaning").start();
622
+ fs2.rm(buildFolder, { recursive: true, force: true }, (error) => {
623
+ if (error) {
624
+ spinner.stop();
625
+ logger.error("Cleaning failed");
626
+ logger.error(error);
627
+ return reject(error);
628
+ } else {
629
+ spinner.stop();
630
+ logger.ok("Cleaning successful");
631
+ return resolve();
632
+ }
633
+ });
634
+ });
635
+ }
636
+ function exec(cmd, project) {
637
+ return new Promise((resolve, reject) => {
638
+ if (!project) {
639
+ return reject();
640
+ }
641
+ const options = { ...getProcessOptions(cmd, project), env: process.env };
642
+ if (cmd === CMD.RUN || cmd === CMD.WATCH) {
643
+ logger.ok(`
644
+ ${getLabel(cmd)} ${project.name}:
645
+ `);
646
+ execa(getProcess(cmd), getProcessArguments(cmd, project), options).stdout.pipe(process.stdout);
647
+ } else {
648
+ const spinner = getSpinner(`${getLabel(cmd)} ${project.name}`);
649
+ spinner.start();
650
+ execa(getProcess(cmd), getProcessArguments(cmd, project), options).then((result) => {
651
+ spinner.stop();
652
+ logger.log(result.stdout);
653
+ logger.ok(`${getLabel(cmd)} Succeeded`);
654
+ return resolve();
655
+ }).catch((error) => {
656
+ spinner.stop();
657
+ if (error.stderr) {
658
+ logger.error(error.stderr);
659
+ } else {
660
+ logger.error(error);
661
+ }
662
+ if (error.stdout) {
663
+ logger.log(error.stdout);
664
+ }
665
+ logger.error(`${getLabel(cmd)} Failed`);
666
+ return reject();
667
+ });
668
+ }
669
+ });
670
+ }
671
+ function isDirectory(p) {
672
+ return new Promise((resolve) => {
673
+ fs2.lstat(p, (error, stats) => {
674
+ if (!error && stats) {
675
+ resolve(stats.isDirectory());
676
+ } else {
677
+ resolve(false);
678
+ }
679
+ });
680
+ });
681
+ }
682
+ async function findProjects() {
683
+ const isProject = (directory) => new Promise((resolve) => {
684
+ fs2.access(path3.join(directory, "package.json"), (error) => {
685
+ if (error) {
686
+ resolve(false);
687
+ } else {
688
+ resolve(true);
689
+ }
690
+ });
691
+ });
692
+ const rootPackage = await readJson(path3.join(process.cwd(), "package.json"));
693
+ const projects = [];
694
+ const files = await fs2.promises.readdir(projectsRoot);
695
+ if (files) {
696
+ for (const file of files) {
697
+ if (file && await isDirectory(path3.join(projectsRoot, file))) {
698
+ const projectPath = path3.join(projectsRoot, file, "package.json");
699
+ if (await isProject(path3.join(projectsRoot, file))) {
700
+ try {
701
+ const package_ = await readJson(path3.join(path3.dirname(projectPath), "package.json"));
702
+ package_.location = path3.posix.join(projectsRoot, file);
703
+ package_.dist = path3.posix.join(process.cwd(), BUILD_DIR, file);
704
+ if (rootPackage) {
705
+ package_.dependencies = { ...package_.dependencies, ...rootPackage.dependencies };
706
+ package_.repository = rootPackage.repository;
707
+ }
708
+ projects.push(package_);
709
+ } catch (error) {
710
+ if (error) {
711
+ logger.log(error);
712
+ }
713
+ }
714
+ }
715
+ }
716
+ }
717
+ }
718
+ return projects;
719
+ }
720
+ function findProject(projectName) {
721
+ return new Promise(async (resolve, reject) => {
722
+ if (!projectName) {
723
+ logger.error("No project specified");
724
+ return reject();
725
+ }
726
+ if (projectName === "all") {
727
+ const project = {
728
+ name: "all",
729
+ version: "0.0.0",
730
+ location: "."
731
+ };
732
+ return resolve(project);
733
+ }
734
+ const projects = await findProjects();
735
+ for (const project of projects) {
736
+ const location = path3.parse(project.location);
737
+ const directoryName = location.name + location.ext;
738
+ if (project.name === projectName || directoryName === projectName) {
739
+ return resolve(project);
740
+ }
741
+ }
742
+ logger.error(`Cloud not find ${projectName} Module.`);
743
+ reject();
744
+ });
745
+ }
746
+ function selectProjects(value) {
747
+ return new Promise(async (resolve, reject) => {
748
+ if (!value) {
749
+ logger.error("No Module specified");
750
+ return reject();
751
+ }
752
+ const projectNames = value.split(",").map((v) => v.trim());
753
+ const allProjects = await findProjects();
754
+ if (value === "all") {
755
+ return resolve(allProjects);
756
+ }
757
+ const projects = [];
758
+ for (const project of allProjects) {
759
+ const location = path3.parse(project.location);
760
+ const directoryName = location.name + location.ext;
761
+ if (projectNames.includes(project.name) || projectNames.includes(directoryName)) {
762
+ projects.push(project);
763
+ }
764
+ }
765
+ if (projects.length === 0) {
766
+ logger.error(`Cloud not find any Modules for ${JSON.stringify(projectNames)}.`);
767
+ reject();
768
+ }
769
+ return resolve(projects);
770
+ });
771
+ }
772
+ async function packageModule(project) {
773
+ const { dist, ...package_ } = project;
774
+ const file = path3.posix.join(dist, "..", `${project.name}.zip`);
775
+ await writeJson(path3.join(dist, "package.json"), package_);
776
+ await zipDirectory(dist, file);
777
+ return file;
778
+ }
779
+ async function publishModule(project, baseUrl = BASE_URL2) {
780
+ return new Promise(async (resolve, reject) => {
781
+ const file = await packageModule(project);
782
+ const form = new FormData();
783
+ form.append("file", fs2.createReadStream(file));
784
+ form.append("name", project.name);
785
+ form.append("description", project.description || "");
786
+ form.append("version", project.version || "");
787
+ try {
788
+ await axios.post(`${baseUrl}/api/flow/modules`, form, {
789
+ headers: {
790
+ ...form.getHeaders(),
791
+ Authorization: `Bearer ${apiToken}`
792
+ },
793
+ maxBodyLength: Number.POSITIVE_INFINITY,
794
+ maxContentLength: Number.POSITIVE_INFINITY
795
+ });
796
+ logger.ok(`Module "${project.name}" published!`);
797
+ return resolve();
798
+ } catch (error) {
799
+ return reject(error);
800
+ } finally {
801
+ deleteFile(file);
802
+ }
803
+ });
804
+ }
805
+ async function validateModule(project) {
806
+ const module = __require(project.dist);
807
+ const moduleName = Reflect.getMetadata("module:name", module.default);
808
+ const moduleDeclarations = Reflect.getMetadata("module:declarations", module.default);
809
+ const functionFqns = [];
810
+ for (const declaration of moduleDeclarations) {
811
+ const fqn = Reflect.getMetadata("element:functionFqn", declaration);
812
+ if (!fqn) {
813
+ throw new Error(`FlowFunction (${declaration.name}) metadata is missing or invalid.`);
814
+ }
815
+ functionFqns.push(fqn);
816
+ }
817
+ if (moduleName) {
818
+ project.name = moduleName;
819
+ project.functions = functionFqns;
820
+ } else {
821
+ throw new Error("Could not validate module name");
822
+ }
823
+ }
824
+ async function publishFunctions(project, update, baseUrl = BASE_URL2) {
825
+ return new Promise(async (resolve, reject) => {
826
+ const globOptions = {
827
+ cwd: project.location,
828
+ ignore: ["node_modules/**/*", "**/package*.json", "**/tsconfig*.json"]
829
+ };
830
+ const files = await glob("**/*.json", globOptions).catch((error) => reject(error));
831
+ const headers = { Authorization: `Bearer ${apiToken}` };
832
+ for (const file of files || []) {
833
+ try {
834
+ const data = await fs2.promises.readFile(path3.join(globOptions.cwd, file));
835
+ const json = JSON.parse(data.toString());
836
+ if (json.fqn && json.category) {
837
+ if (update) {
838
+ try {
839
+ await axios.put(`${baseUrl}/api/flow/functions/${json.fqn}`, json, { headers });
840
+ logger.ok(`Flow Function "${json.fqn}" has been updated`);
841
+ } catch (error) {
842
+ logger.error(`Flow Function "${json.fqn}" could not be updated`);
843
+ handleApiError(error);
844
+ }
845
+ } else {
846
+ try {
847
+ await axios.post(`${baseUrl}/api/flow/functions`, json, { headers });
848
+ logger.ok(`Flow Function "${json.fqn}" has been created`);
849
+ } catch (error) {
850
+ logger.error(`Flow Function "${json.fqn}" could not be created`);
851
+ handleApiError(error);
852
+ }
853
+ }
854
+ }
855
+ } catch (error) {
856
+ logger.error(error);
857
+ }
858
+ }
859
+ return resolve();
860
+ });
861
+ }
862
+ function zipDirectory(source, out) {
863
+ const archive = archiver("zip", { zlib: { level: 8 } });
864
+ const stream = fs2.createWriteStream(out);
865
+ return new Promise((resolve, reject) => {
866
+ archive.directory(source, false).on("error", (error) => reject(error)).pipe(stream);
867
+ stream.on("close", () => resolve());
868
+ archive.finalize();
869
+ });
870
+ }
871
+ function deleteFile(filePath) {
872
+ return new Promise((resolve, reject) => {
873
+ fs2.unlink(filePath, (error) => {
874
+ if (error) {
875
+ return reject(error);
876
+ }
877
+ return resolve();
878
+ });
879
+ });
880
+ }
881
+ function getProcess(cmd) {
882
+ switch (cmd) {
883
+ case CMD.BUILD: {
884
+ return "tsc";
885
+ }
886
+ case CMD.FORMAT: {
887
+ return "prettier";
888
+ }
889
+ case CMD.INSTALL:
890
+ case CMD.AUDIT: {
891
+ return "npm";
892
+ }
893
+ case CMD.LINT: {
894
+ return "eslint";
895
+ }
896
+ case CMD.RUN: {
897
+ return "node";
898
+ }
899
+ case CMD.TEST: {
900
+ return "jest";
901
+ }
902
+ case CMD.WATCH: {
903
+ return "nodemon";
904
+ }
905
+ default: {
906
+ return "";
907
+ }
908
+ }
909
+ }
910
+ function copyProjectFiles(project) {
911
+ return new Promise((resolve, reject) => {
912
+ copyfiles(
913
+ [`${project.location}/**`, `${BUILD_DIR}/`],
914
+ {
915
+ exclude: [`${project.location}/*.json`, `${project.location}/**/*.ts`, `${project.location}/**/test/**`],
916
+ up: 1
917
+ },
918
+ (error) => {
919
+ if (error) {
920
+ return reject(error);
921
+ }
922
+ return resolve();
923
+ }
924
+ );
925
+ });
926
+ }
927
+ function getProcessArguments(cmd, project) {
928
+ switch (cmd) {
929
+ case CMD.AUDIT: {
930
+ return ["audit", "--audit-level=moderate"];
931
+ }
932
+ case CMD.BUILD: {
933
+ const filename = path3.join(project.location, "tsconfig.module.json");
934
+ const configFile = fs2.existsSync(filename) ? filename : project.location;
935
+ return ["-p", configFile];
936
+ }
937
+ case CMD.FORMAT: {
938
+ return ["--write", "**/*.ts"];
939
+ }
940
+ case CMD.INSTALL: {
941
+ return ["install", "--no-package-lock"];
942
+ }
943
+ case CMD.LINT: {
944
+ return [project.location + "/**/*.{js,ts}", "--fix"];
945
+ }
946
+ case CMD.RUN: {
947
+ return [project.location];
948
+ }
949
+ case CMD.TEST: {
950
+ return project.name === "all" ? ["--runInBand", "--coverage", "--forceExit", "--verbose", "--passWithNoTests"] : ["roots", project.location, "--forceExit", "--verbose", "--passWithNoTests"];
951
+ }
952
+ case CMD.WATCH: {
953
+ return ["--inspect", project.location];
954
+ }
955
+ default: {
956
+ return [];
957
+ }
958
+ }
959
+ }
960
+ function getProcessOptions(cmd, project) {
961
+ switch (cmd) {
962
+ case CMD.INSTALL: {
963
+ return { cwd: project.location };
964
+ }
965
+ }
966
+ }
967
+ function getLabel(cmd) {
968
+ switch (cmd) {
969
+ case CMD.RUN: {
970
+ return "Running";
971
+ }
972
+ default: {
973
+ return `${cmd.charAt(0).toUpperCase()}${cmd.slice(1)}ing`;
974
+ }
975
+ }
976
+ }
977
+ function getSpinner(message) {
978
+ return ora({
979
+ color: "magenta",
980
+ spinner: "dots",
981
+ text: message
982
+ });
983
+ }
984
+ function checkIfAll(projectName) {
985
+ if (projectName === "all") {
986
+ logger.error(`Please specify a project. Command can't be run for all.`);
987
+ return true;
988
+ }
989
+ return false;
990
+ }
991
+ function readJson(filePath) {
992
+ return new Promise((resolve, reject) => {
993
+ fs2.readFile(filePath, { encoding: "utf8" }, (error, data) => {
994
+ if (error) {
995
+ return reject(error);
996
+ }
997
+ try {
998
+ return resolve(JSON.parse(data));
999
+ } catch (error_) {
1000
+ return reject(error_);
1001
+ }
1002
+ });
1003
+ });
1004
+ }
1005
+ function writeJson(filePath, data) {
1006
+ return new Promise((resolve, reject) => {
1007
+ let dataString;
1008
+ try {
1009
+ dataString = JSON.stringify(data, null, 2) + "\n";
1010
+ } catch (error) {
1011
+ return reject(error);
1012
+ }
1013
+ fs2.writeFile(filePath, dataString, (error) => {
1014
+ if (error) {
1015
+ return reject(error);
1016
+ }
1017
+ return resolve();
1018
+ });
1019
+ });
1020
+ }