@zuplo/cli 1.88.0 → 1.90.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/README.md CHANGED
@@ -33,6 +33,7 @@ Project commands
33
33
 
34
34
  Commands:
35
35
  zup project update Updates your project structure to the latest conventions
36
+ zup project import-openapi Import an OpenAPI file into your Zuplo project
36
37
 
37
38
  zup tunnel
38
39
 
@@ -0,0 +1,41 @@
1
+
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a7cdf682-723c-53b1-b20c-0a26e9a504db")}catch(e){}}();
3
+ import { captureEvent } from "../../common/analytics/lib.js";
4
+ import setBlocking from "../../common/output.js";
5
+ import { ZuploProjectValidator } from "../../common/validators/file-system-validator.js";
6
+ import { YargsChecker } from "../../common/validators/lib.js";
7
+ import { importOpenApi, } from "../../project/import-openapi/handler.js";
8
+ export default {
9
+ desc: "Import an OpenAPI file into your Zuplo project",
10
+ command: "import-openapi",
11
+ builder: (yargs) => {
12
+ return yargs
13
+ .option("dir", {
14
+ type: "string",
15
+ describe: "The directory containing your config/routes.json",
16
+ default: ".",
17
+ normalize: true,
18
+ hidden: true,
19
+ })
20
+ .option("prompt", {
21
+ type: "boolean",
22
+ describe: "Prompt for confirmation before importing",
23
+ default: true,
24
+ })
25
+ .option("source", {
26
+ type: "string",
27
+ describe: "The OpenAPI file to import",
28
+ })
29
+ .demandOption(["source"])
30
+ .middleware([setBlocking])
31
+ .check(async (argv) => {
32
+ return await new YargsChecker(new ZuploProjectValidator()).check(argv);
33
+ });
34
+ },
35
+ handler: async (argv) => {
36
+ await captureEvent({ argv, event: "zup project import-open-api" });
37
+ await importOpenApi(argv);
38
+ },
39
+ };
40
+ //# sourceMappingURL=import-openapi.js.map
41
+ //# debugId=a7cdf682-723c-53b1-b20c-0a26e9a504db
@@ -1,15 +1,16 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f9aa83d0-91be-5c97-ac89-131d1bec0878")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="e46201b0-8148-5c25-a7c1-7ae7cdc49cbb")}catch(e){}}();
3
3
  import { groupHandler } from "../../common/handler.js";
4
+ import importOpenApi from "./import-openapi.js";
4
5
  import update from "./update.js";
5
6
  const commands = {
6
7
  describe: "Project commands",
7
8
  command: "project",
8
9
  builder: (yargs) => {
9
- return yargs.command(update).help();
10
+ return yargs.command(update).command(importOpenApi).help();
10
11
  },
11
12
  handler: groupHandler,
12
13
  };
13
14
  export default commands;
14
15
  //# sourceMappingURL=index.js.map
15
- //# debugId=f9aa83d0-91be-5c97-ac89-131d1bec0878
16
+ //# debugId=e46201b0-8148-5c25-a7c1-7ae7cdc49cbb
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="648539ee-2075-5ac0-b268-fc5ae066e234")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="5d2da21c-788d-547b-b136-8738ace98ad7")}catch(e){}}();
3
3
  import fg from "fast-glob";
4
4
  import { existsSync, readdirSync } from "node:fs";
5
5
  import { join } from "node:path";
@@ -16,7 +16,7 @@ export class NotAGitRepoError extends Error {
16
16
  }
17
17
  export class NotAZuploProject extends Error {
18
18
  constructor() {
19
- super("Invalid directory: The current directory is not a Zuplo project.");
19
+ super("Invalid directory: The current directory is not the root of a Zuplo project.");
20
20
  Object.setPrototypeOf(this, NotAZuploProject.prototype);
21
21
  }
22
22
  }
@@ -163,4 +163,4 @@ export const validDeployDirectoryValidator = new CompositeValidator(new ZuploPro
163
163
  export const validTestDirectoryValidator = new CompositeValidator(new ZuploProjectValidator(), new ZuploProjectHasTestsValidator());
164
164
  export const validLinkDirectoryValidator = new CompositeValidator(new ZuploProjectValidator(), new UserIsLoggedInValidator());
165
165
  //# sourceMappingURL=file-system-validator.js.map
166
- //# debugId=648539ee-2075-5ac0-b268-fc5ae066e234
166
+ //# debugId=5d2da21c-788d-547b-b136-8738ace98ad7
@@ -0,0 +1,86 @@
1
+
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7a8f9c9d-06a4-50e2-963a-5a23166de5eb")}catch(e){}}();
3
+ import { confirm } from "@inquirer/prompts";
4
+ import { existsSync, writeFileSync } from "node:fs";
5
+ import { readFile } from "node:fs/promises";
6
+ import { basename, extname, join, relative } from "node:path";
7
+ import prettier from "prettier";
8
+ import { printCriticalFailureToConsoleAndExit, printDiagnosticsToConsole, printResultToConsoleAndExitGracefully, } from "../../common/output.js";
9
+ import { addOperationIdsAsNecessary, generateMergeChangeset, parseOpenApiFile, } from "./utils.js";
10
+ const BASE_TEMPLATE = `
11
+ {
12
+ "openapi": "3.1.0",
13
+ "info": {
14
+ "version": "1.0.0",
15
+ "title": "My Zuplo API"
16
+ },
17
+ "paths": {}
18
+ }
19
+
20
+ `;
21
+ export async function importOpenApi(argv) {
22
+ const filePath = argv.source;
23
+ const normalizedFilePath = join(relative(process.cwd(), filePath));
24
+ if (!existsSync(normalizedFilePath)) {
25
+ printCriticalFailureToConsoleAndExit(`The file ${normalizedFilePath} to import does not exist.`);
26
+ }
27
+ const rawOpenApiSpec = await readFile(normalizedFilePath);
28
+ const extName = extname(normalizedFilePath);
29
+ const basePath = basename(normalizedFilePath, extName);
30
+ const fileContent = rawOpenApiSpec.toString();
31
+ const parsedOpenApiSpec = await parseOpenApiFile(extName, fileContent);
32
+ const destinationFilePath = join(process.cwd(), "config", `${basePath.replace("oas", "")}.oas.json`);
33
+ let originalDocument;
34
+ if (!existsSync(destinationFilePath)) {
35
+ originalDocument = (await parseOpenApiFile(".json", BASE_TEMPLATE));
36
+ }
37
+ else {
38
+ const existingOpenApiSpec = await readFile(destinationFilePath);
39
+ const existingFileContent = existingOpenApiSpec.toString();
40
+ originalDocument = (await parseOpenApiFile(extName, existingFileContent));
41
+ }
42
+ const { created, merged, retained } = generateMergeChangeset(originalDocument, parsedOpenApiSpec);
43
+ printDiagnosticsToConsole("This import will...");
44
+ printDiagnosticsToConsole("");
45
+ if (created.size > 0) {
46
+ printDiagnosticsToConsole(`Create ${created.size} new ${created.size > 1 ? "operations" : "operation"}`);
47
+ printDiagnosticsToConsole("");
48
+ created.forEach((operation) => {
49
+ printDiagnosticsToConsole(operation);
50
+ });
51
+ printDiagnosticsToConsole("");
52
+ }
53
+ if (merged.size > 0) {
54
+ printDiagnosticsToConsole(`Merge ${merged.size} new ${merged.size > 1 ? "operations" : "operation"}`);
55
+ printDiagnosticsToConsole("");
56
+ merged.forEach((operation) => {
57
+ printDiagnosticsToConsole(operation);
58
+ });
59
+ printDiagnosticsToConsole("");
60
+ }
61
+ if (retained.size > 0) {
62
+ printDiagnosticsToConsole(`Retain ${retained.size} new ${retained.size > 1 ? "operations" : "operation"}`);
63
+ printDiagnosticsToConsole("");
64
+ retained.forEach((operation) => {
65
+ printDiagnosticsToConsole(operation);
66
+ });
67
+ printDiagnosticsToConsole("");
68
+ }
69
+ if (argv.prompt) {
70
+ printDiagnosticsToConsole("");
71
+ const answer = await confirm({ message: "Proceed?", default: true });
72
+ if (!answer) {
73
+ printResultToConsoleAndExitGracefully("Import cancelled.");
74
+ }
75
+ }
76
+ addOperationIdsAsNecessary(parsedOpenApiSpec);
77
+ const formattedOpenApi = prettier.format(JSON.stringify(parsedOpenApiSpec), {
78
+ parser: "json-stringify",
79
+ });
80
+ writeFileSync(destinationFilePath, formattedOpenApi, {
81
+ flag: "w",
82
+ });
83
+ printResultToConsoleAndExitGracefully(`Import successful. File written to ${destinationFilePath}`);
84
+ }
85
+ //# sourceMappingURL=handler.js.map
86
+ //# debugId=7a8f9c9d-06a4-50e2-963a-5a23166de5eb
@@ -0,0 +1,8 @@
1
+
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b14028c7-4667-5a33-8f2d-e6c8216e26eb")}catch(e){}}();
3
+ export const ZUPLO_OPEN_API_VENDOR_PREFIX = "x-zuplo";
4
+ export const OPEN_API_INTERNAL_PROPERTY_VENDOR_TAG = "x-internal";
5
+ export const ZUPLO_OPEN_API_PATH_KEY = `${ZUPLO_OPEN_API_VENDOR_PREFIX}-path`;
6
+ export const ZUPLO_OPEN_API_ROUTE_KEY = `${ZUPLO_OPEN_API_VENDOR_PREFIX}-route`;
7
+ //# sourceMappingURL=interfaces.js.map
8
+ //# debugId=b14028c7-4667-5a33-8f2d-e6c8216e26eb
@@ -0,0 +1,223 @@
1
+
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="c089ea78-2efe-51c0-aa97-d386b8b87324")}catch(e){}}();
3
+ import yaml from "js-yaml";
4
+ import { v4 } from "uuid";
5
+ import { ZUPLO_OPEN_API_PATH_KEY, ZUPLO_OPEN_API_ROUTE_KEY, } from "./interfaces.js";
6
+ export const OPERATION_PATH_MERGE_DELIMITER = ">";
7
+ export const OPEN_API_FILE_TYPES = [".json", ".yml", ".yaml"];
8
+ export const parseOpenApiFile = async (extName, fileText) => {
9
+ if (!OPEN_API_FILE_TYPES.includes(extName)) {
10
+ throw new Error(`Invalid file type. Supported file types are: ${OPEN_API_FILE_TYPES.join(", ")}`);
11
+ }
12
+ if (extName.includes("json")) {
13
+ return JSON.parse(fileText);
14
+ }
15
+ return yaml.load(fileText);
16
+ };
17
+ export const addOperationIdsAsNecessary = (openApi) => {
18
+ const paths = openApi.paths;
19
+ if (!paths) {
20
+ return;
21
+ }
22
+ for (const path of Object.keys(paths)) {
23
+ const pathItem = paths[path];
24
+ if (!pathItem) {
25
+ continue;
26
+ }
27
+ const methods = [
28
+ "get",
29
+ "put",
30
+ "post",
31
+ "delete",
32
+ "options",
33
+ "head",
34
+ "patch",
35
+ "trace",
36
+ ];
37
+ for (const method of methods) {
38
+ const operation = pathItem[method];
39
+ if (operation && !operation.operationId) {
40
+ operation.operationId = v4();
41
+ }
42
+ }
43
+ }
44
+ };
45
+ export const mergeOpenApiDocuments = (originalDocument, newDocument) => {
46
+ return mergeOpenApiDocumentOnPathMethod(originalDocument, newDocument);
47
+ };
48
+ const mergeOpenApiDocumentOnPathMethod = (originalDocument, newDocument) => {
49
+ let paths = originalDocument.paths;
50
+ if (newDocument.paths) {
51
+ paths = Object.entries(newDocument.paths).reduce((mergedPaths, [path, pathObject]) => {
52
+ const existingEntry = mergedPaths[path];
53
+ if (!existingEntry) {
54
+ mergedPaths[path] = pathObject;
55
+ }
56
+ else {
57
+ mergedPaths[path] = mergePathItems(existingEntry, pathObject);
58
+ }
59
+ return mergedPaths;
60
+ }, originalDocument.paths ?? {});
61
+ }
62
+ return {
63
+ info: newDocument.info,
64
+ openapi: mergeOpenApiVersion(originalDocument.openapi, newDocument.openapi),
65
+ components: mergeComponents(originalDocument.components, newDocument.components),
66
+ jsonSchemaDialect: newDocument.jsonSchemaDialect ?? originalDocument.jsonSchemaDialect,
67
+ servers: newDocument.servers ?? originalDocument.servers,
68
+ security: newDocument.security ?? originalDocument.security,
69
+ tags: newDocument.tags ?? originalDocument.tags,
70
+ externalDocs: newDocument.externalDocs ?? originalDocument.externalDocs,
71
+ paths,
72
+ };
73
+ };
74
+ const mergeComponents = (originalComponents, newComponents) => {
75
+ if (!originalComponents) {
76
+ return newComponents;
77
+ }
78
+ if (!newComponents) {
79
+ return originalComponents;
80
+ }
81
+ const { schemas, responses, parameters, examples, requestBodies, headers, securitySchemes, links, callbacks, pathItems, } = originalComponents;
82
+ const { schemas: newSchemas, responses: newResponses, parameters: newParameters, examples: newExamples, requestBodies: newRequestBodies, headers: newHeaders, securitySchemes: newSecuritySchemes, links: newLinks, callbacks: newCallbacks, pathItems: newPathItems, } = newComponents;
83
+ return {
84
+ schemas: schemas ? { ...schemas, ...newSchemas } : newSchemas,
85
+ responses: responses ? { ...responses, ...newResponses } : newResponses,
86
+ parameters: parameters
87
+ ? { ...parameters, ...newParameters }
88
+ : newParameters,
89
+ examples: examples ? { ...examples, ...newExamples } : newExamples,
90
+ requestBodies: requestBodies
91
+ ? { ...requestBodies, ...newRequestBodies }
92
+ : newRequestBodies,
93
+ headers: headers ? { ...headers, ...newHeaders } : newHeaders,
94
+ securitySchemes: securitySchemes
95
+ ? { ...securitySchemes, ...newSecuritySchemes }
96
+ : newSecuritySchemes,
97
+ links: links ? { ...links, ...newLinks } : newLinks,
98
+ callbacks: callbacks ? { ...callbacks, ...newCallbacks } : newCallbacks,
99
+ pathItems: pathItems ? { ...pathItems, ...newPathItems } : newPathItems,
100
+ };
101
+ };
102
+ const mergeOpenApiVersion = (originalVersion, newDocumentVersion) => {
103
+ if (originalVersion === "3.1.0") {
104
+ return originalVersion;
105
+ }
106
+ if (newDocumentVersion === "3.1.0") {
107
+ return newDocumentVersion;
108
+ }
109
+ return originalVersion;
110
+ };
111
+ const mergePathItems = (originalPathItem, newPathItem) => {
112
+ const { summary, description, get, put, post, delete: deleteOperation, options, head, patch, trace, servers, parameters, } = originalPathItem;
113
+ const zuploPathProperties = originalPathItem[ZUPLO_OPEN_API_PATH_KEY];
114
+ const newZuploPathProperty = typeof newPathItem === "object" &&
115
+ newPathItem !== null &&
116
+ ZUPLO_OPEN_API_PATH_KEY in newPathItem
117
+ ? newPathItem[ZUPLO_OPEN_API_PATH_KEY]
118
+ : undefined;
119
+ const { summary: newSummary, description: newDescription, get: newGetOperation, put: newPutOperation, post: newPostOperation, delete: newDeleteOperation, options: newOptionsOperation, head: newHeadOperation, patch: newPatchOperation, trace: newTraceOperation, servers: newServers, parameters: newParameters, } = newPathItem;
120
+ return {
121
+ summary: newSummary ?? summary,
122
+ description: newDescription ?? description,
123
+ [ZUPLO_OPEN_API_PATH_KEY]: newZuploPathProperty ?? zuploPathProperties,
124
+ servers: newServers ?? servers,
125
+ parameters: newParameters ?? parameters,
126
+ get: mergeOperationObjects(get, newGetOperation),
127
+ put: mergeOperationObjects(put, newPutOperation),
128
+ post: mergeOperationObjects(post, newPostOperation),
129
+ delete: mergeOperationObjects(deleteOperation, newDeleteOperation),
130
+ options: mergeOperationObjects(options, newOptionsOperation),
131
+ head: mergeOperationObjects(head, newHeadOperation),
132
+ patch: mergeOperationObjects(patch, newPatchOperation),
133
+ trace: mergeOperationObjects(trace, newTraceOperation),
134
+ };
135
+ };
136
+ const mergeOperationObjects = (originalOperationObject, newOperationObject) => {
137
+ if (!newOperationObject) {
138
+ return originalOperationObject;
139
+ }
140
+ const originalZuploProperties = originalOperationObject?.[ZUPLO_OPEN_API_ROUTE_KEY];
141
+ return {
142
+ operationId: v4(),
143
+ [ZUPLO_OPEN_API_ROUTE_KEY]: originalZuploProperties,
144
+ ...newOperationObject,
145
+ };
146
+ };
147
+ export const generateMergeChangeset = (originalDocument, newDocument) => {
148
+ return generatePathMethodMergeChangeset(originalDocument, newDocument);
149
+ };
150
+ const generatePathMethodMergeChangeset = (originalDocument, newDocument) => {
151
+ let created = new Set();
152
+ const merged = new Set();
153
+ let retained = new Set();
154
+ const originalPaths = originalDocument.paths;
155
+ const newPaths = newDocument.paths;
156
+ if (!originalPaths && !newPaths) {
157
+ return { created, merged, retained };
158
+ }
159
+ if (!originalPaths && newPaths) {
160
+ created = new Set(getAllOperationKeys(newPaths));
161
+ return { created, merged, retained };
162
+ }
163
+ if (!newPaths && originalPaths) {
164
+ retained = new Set(getAllOperationKeys(originalPaths));
165
+ return { created, merged, retained };
166
+ }
167
+ if (newPaths && originalPaths) {
168
+ const oldOperationKeys = getAllOperationKeys(originalPaths);
169
+ const newOperationKeys = getAllOperationKeys(newPaths);
170
+ const oldOperationKeysSet = new Set(oldOperationKeys);
171
+ const newOperationKeysSet = new Set(newOperationKeys);
172
+ newOperationKeys.forEach((operationKey) => {
173
+ if (oldOperationKeysSet.has(operationKey)) {
174
+ merged.add(operationKey);
175
+ }
176
+ else {
177
+ created.add(operationKey);
178
+ }
179
+ });
180
+ oldOperationKeys.forEach((operationKey) => {
181
+ if (!newOperationKeysSet.has(operationKey)) {
182
+ retained.add(operationKey);
183
+ }
184
+ });
185
+ }
186
+ return { created, merged, retained };
187
+ };
188
+ const getAllOperationKeys = (pathsObject) => {
189
+ return Object.entries(pathsObject)
190
+ .map(([path, pathObject]) => {
191
+ const pathItemObject = pathObject;
192
+ const { get, put, post, delete: deleteOperation, options, head, patch, trace, } = pathItemObject;
193
+ const operations = [];
194
+ if (get) {
195
+ operations.push("get");
196
+ }
197
+ if (put) {
198
+ operations.push("put");
199
+ }
200
+ if (post) {
201
+ operations.push("post");
202
+ }
203
+ if (deleteOperation) {
204
+ operations.push("delete");
205
+ }
206
+ if (options) {
207
+ operations.push("options");
208
+ }
209
+ if (head) {
210
+ operations.push("head");
211
+ }
212
+ if (patch) {
213
+ operations.push("patch");
214
+ }
215
+ if (trace) {
216
+ operations.push("trace");
217
+ }
218
+ return operations.map((operation) => `${operation}${OPERATION_PATH_MERGE_DELIMITER}${path}`);
219
+ })
220
+ .flat();
221
+ };
222
+ //# sourceMappingURL=utils.js.map
223
+ //# debugId=c089ea78-2efe-51c0-aa97-d386b8b87324
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zuplo/cli",
3
- "version": "1.88.0",
3
+ "version": "1.90.0",
4
4
  "type": "module",
5
5
  "repository": "https://github.com/zuplo/cli",
6
6
  "description": "The command-line interface for Zuplo",
@@ -30,6 +30,7 @@
30
30
  "devDependencies": {
31
31
  "@sentry/cli": "^2.20.7",
32
32
  "@types/chai": "^4.3.4",
33
+ "@types/js-yaml": "^4.0.8",
33
34
  "@types/mocha": "^10.0.1",
34
35
  "@types/node": "^18.15.11",
35
36
  "@types/prettier": "^2.7.2",
@@ -37,6 +38,7 @@
37
38
  "@types/semver": "^7.3.13",
38
39
  "@types/tar": "^6.1.4",
39
40
  "@types/temp": "^0.9.1",
41
+ "@types/uuid": "^9.0.6",
40
42
  "@types/yargs": "^17.0.24",
41
43
  "@typescript-eslint/eslint-plugin": "^5.57.1",
42
44
  "@typescript-eslint/parser": "^5.57.1",
@@ -49,6 +51,7 @@
49
51
  "husky": "^8.0.3",
50
52
  "lint-staged": "^13.2.0",
51
53
  "mocha": "^10.2.0",
54
+ "openapi-types": "^12.1.3",
52
55
  "prettier-plugin-organize-imports": "^3.2.2",
53
56
  "typescript": "^5.2.2"
54
57
  },
@@ -58,9 +61,9 @@
58
61
  "@inquirer/prompts": "^3.0.4",
59
62
  "@sentry/node": "7.69.0",
60
63
  "@swc/core": "1.3.78",
61
- "@zuplo/core": "5.1401.0",
64
+ "@zuplo/core": "5.1450.0",
62
65
  "@zuplo/pino-pretty-configurations": "^1.5.0",
63
- "@zuplo/runtime": "5.1401.0",
66
+ "@zuplo/runtime": "5.1450.0",
64
67
  "chalk": "^5.1.2",
65
68
  "chokidar": "^3.5.3",
66
69
  "deno-bin": "1.37.1",
@@ -73,6 +76,7 @@
73
76
  "fastify-sse-v2": "^3.1.2",
74
77
  "ignore": "^5.2.4",
75
78
  "jose": "^4.14.4",
79
+ "js-yaml": "^4.1.0",
76
80
  "jsonc-parser": "^3.2.0",
77
81
  "open": "^9.1.0",
78
82
  "pino": "^8.11.0",
@@ -85,6 +89,7 @@
85
89
  "simple-git": "^3.17.0",
86
90
  "tar": "^6.1.13",
87
91
  "temp": "^0.9.4",
92
+ "uuid": "^9.0.1",
88
93
  "yargs": "^17.7.1"
89
94
  }
90
95
  }