@zuplo/cli 6.62.7 → 6.62.9

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 (134) hide show
  1. package/README.md +36 -4
  2. package/dist/__tests__/import-openapi-utils.test.js +1 -2
  3. package/dist/__tests__/import-openapi-utils.test.js.map +1 -1
  4. package/dist/__tests__/import-openapi.test.js +1 -5
  5. package/dist/__tests__/import-openapi.test.js.map +1 -1
  6. package/dist/cli.js +2 -0
  7. package/dist/cli.js.map +1 -1
  8. package/dist/cmds/open-api/convert.d.ts +9 -0
  9. package/dist/cmds/open-api/convert.d.ts.map +1 -0
  10. package/dist/cmds/open-api/convert.js +66 -0
  11. package/dist/cmds/open-api/convert.js.map +1 -0
  12. package/dist/cmds/open-api/index.d.ts +4 -0
  13. package/dist/cmds/open-api/index.d.ts.map +1 -0
  14. package/dist/cmds/open-api/index.js +15 -0
  15. package/dist/cmds/open-api/index.js.map +1 -0
  16. package/dist/cmds/open-api/merge.d.ts +9 -0
  17. package/dist/cmds/open-api/merge.d.ts.map +1 -0
  18. package/dist/cmds/open-api/merge.js +57 -0
  19. package/dist/cmds/open-api/merge.js.map +1 -0
  20. package/dist/cmds/open-api/overlay.d.ts +9 -0
  21. package/dist/cmds/open-api/overlay.d.ts.map +1 -0
  22. package/dist/cmds/open-api/overlay.js +85 -0
  23. package/dist/cmds/open-api/overlay.js.map +1 -0
  24. package/dist/cmds/source/import-openapi.d.ts +1 -0
  25. package/dist/cmds/source/import-openapi.d.ts.map +1 -1
  26. package/dist/cmds/source/import-openapi.js +6 -9
  27. package/dist/cmds/source/import-openapi.js.map +1 -1
  28. package/dist/cmds/source/migrate.js +1 -1
  29. package/dist/cmds/source/migrate.js.map +1 -1
  30. package/dist/common/file-format.d.ts +25 -0
  31. package/dist/common/file-format.d.ts.map +1 -0
  32. package/dist/common/file-format.js +72 -0
  33. package/dist/common/file-format.js.map +1 -0
  34. package/dist/common/runner.d.ts.map +1 -0
  35. package/dist/common/runner.js.map +1 -0
  36. package/dist/common/utils/stringify-config.d.ts.map +1 -0
  37. package/dist/common/utils/stringify-config.js.map +1 -0
  38. package/dist/common/utils/stringify-config.test.d.ts.map +1 -0
  39. package/dist/common/utils/stringify-config.test.js.map +1 -0
  40. package/dist/open-api/convert/convert-engine.d.ts +26 -0
  41. package/dist/open-api/convert/convert-engine.d.ts.map +1 -0
  42. package/dist/open-api/convert/convert-engine.js +20 -0
  43. package/dist/open-api/convert/convert-engine.js.map +1 -0
  44. package/dist/open-api/convert/convert-engine.spec.d.ts +2 -0
  45. package/dist/open-api/convert/convert-engine.spec.d.ts.map +1 -0
  46. package/dist/open-api/convert/convert-engine.spec.js +268 -0
  47. package/dist/open-api/convert/convert-engine.spec.js.map +1 -0
  48. package/dist/open-api/convert/handler.d.ts +9 -0
  49. package/dist/open-api/convert/handler.d.ts.map +1 -0
  50. package/dist/open-api/convert/handler.js +54 -0
  51. package/dist/open-api/convert/handler.js.map +1 -0
  52. package/dist/open-api/convert/handler.spec.d.ts +2 -0
  53. package/dist/open-api/convert/handler.spec.d.ts.map +1 -0
  54. package/dist/open-api/convert/handler.spec.js +291 -0
  55. package/dist/open-api/convert/handler.spec.js.map +1 -0
  56. package/dist/open-api/merge/ajv.d.ts.map +1 -0
  57. package/dist/open-api/merge/ajv.js.map +1 -0
  58. package/dist/open-api/merge/handler.d.ts +9 -0
  59. package/dist/open-api/merge/handler.d.ts.map +1 -0
  60. package/dist/{source/import-openapi → open-api/merge}/handler.js +16 -42
  61. package/dist/open-api/merge/handler.js.map +1 -0
  62. package/dist/open-api/merge/handler.spec.d.ts +2 -0
  63. package/dist/open-api/merge/handler.spec.d.ts.map +1 -0
  64. package/dist/open-api/merge/handler.spec.js +335 -0
  65. package/dist/open-api/merge/handler.spec.js.map +1 -0
  66. package/dist/open-api/merge/interfaces.d.ts.map +1 -0
  67. package/dist/open-api/merge/interfaces.js.map +1 -0
  68. package/dist/open-api/merge/merge-engine.d.ts +23 -0
  69. package/dist/open-api/merge/merge-engine.d.ts.map +1 -0
  70. package/dist/open-api/merge/merge-engine.js +33 -0
  71. package/dist/open-api/merge/merge-engine.js.map +1 -0
  72. package/dist/open-api/merge/merge-engine.spec.d.ts +2 -0
  73. package/dist/open-api/merge/merge-engine.spec.d.ts.map +1 -0
  74. package/dist/open-api/merge/merge-engine.spec.js +117 -0
  75. package/dist/open-api/merge/merge-engine.spec.js.map +1 -0
  76. package/dist/open-api/merge/utils.d.ts.map +1 -0
  77. package/dist/open-api/merge/utils.js.map +1 -0
  78. package/dist/open-api/overlay/handler.d.ts +10 -0
  79. package/dist/open-api/overlay/handler.d.ts.map +1 -0
  80. package/dist/open-api/overlay/handler.js +92 -0
  81. package/dist/open-api/overlay/handler.js.map +1 -0
  82. package/dist/open-api/overlay/handler.spec.d.ts +2 -0
  83. package/dist/open-api/overlay/handler.spec.d.ts.map +1 -0
  84. package/dist/open-api/overlay/handler.spec.js +304 -0
  85. package/dist/open-api/overlay/handler.spec.js.map +1 -0
  86. package/dist/open-api/overlay/overlay-engine.d.ts +55 -0
  87. package/dist/open-api/overlay/overlay-engine.d.ts.map +1 -0
  88. package/dist/open-api/overlay/overlay-engine.js +280 -0
  89. package/dist/open-api/overlay/overlay-engine.js.map +1 -0
  90. package/dist/open-api/overlay/overlay-engine.spec.d.ts +2 -0
  91. package/dist/open-api/overlay/overlay-engine.spec.d.ts.map +1 -0
  92. package/dist/open-api/overlay/overlay-engine.spec.js +609 -0
  93. package/dist/open-api/overlay/overlay-engine.spec.js.map +1 -0
  94. package/dist/source/migrate/dev-portal/handler.d.ts.map +1 -0
  95. package/dist/{cmds/source/migrate → source/migrate/dev-portal}/handler.js +53 -4
  96. package/dist/source/migrate/dev-portal/handler.js.map +1 -0
  97. package/dist/{cmds/source/migrate → source/migrate/dev-portal}/types.d.ts +12 -1
  98. package/dist/source/migrate/dev-portal/types.d.ts.map +1 -0
  99. package/dist/source/migrate/dev-portal/types.js.map +1 -0
  100. package/dist/tsconfig.tsbuildinfo +1 -1
  101. package/package.json +5 -4
  102. package/dist/cmds/source/migrate/handler.d.ts.map +0 -1
  103. package/dist/cmds/source/migrate/handler.js.map +0 -1
  104. package/dist/cmds/source/migrate/runner.d.ts.map +0 -1
  105. package/dist/cmds/source/migrate/runner.js.map +0 -1
  106. package/dist/cmds/source/migrate/stringify-config.d.ts.map +0 -1
  107. package/dist/cmds/source/migrate/stringify-config.js.map +0 -1
  108. package/dist/cmds/source/migrate/stringify-config.test.d.ts.map +0 -1
  109. package/dist/cmds/source/migrate/stringify-config.test.js.map +0 -1
  110. package/dist/cmds/source/migrate/types.d.ts.map +0 -1
  111. package/dist/cmds/source/migrate/types.js.map +0 -1
  112. package/dist/source/import-openapi/ajv.d.ts.map +0 -1
  113. package/dist/source/import-openapi/ajv.js.map +0 -1
  114. package/dist/source/import-openapi/handler.d.ts +0 -13
  115. package/dist/source/import-openapi/handler.d.ts.map +0 -1
  116. package/dist/source/import-openapi/handler.js.map +0 -1
  117. package/dist/source/import-openapi/interfaces.d.ts.map +0 -1
  118. package/dist/source/import-openapi/interfaces.js.map +0 -1
  119. package/dist/source/import-openapi/utils.d.ts.map +0 -1
  120. package/dist/source/import-openapi/utils.js.map +0 -1
  121. /package/dist/{cmds/source/migrate → common}/runner.d.ts +0 -0
  122. /package/dist/{cmds/source/migrate → common}/runner.js +0 -0
  123. /package/dist/{cmds/source/migrate → common/utils}/stringify-config.d.ts +0 -0
  124. /package/dist/{cmds/source/migrate → common/utils}/stringify-config.js +0 -0
  125. /package/dist/{cmds/source/migrate → common/utils}/stringify-config.test.d.ts +0 -0
  126. /package/dist/{cmds/source/migrate → common/utils}/stringify-config.test.js +0 -0
  127. /package/dist/{source/import-openapi → open-api/merge}/ajv.d.ts +0 -0
  128. /package/dist/{source/import-openapi → open-api/merge}/ajv.js +0 -0
  129. /package/dist/{source/import-openapi → open-api/merge}/interfaces.d.ts +0 -0
  130. /package/dist/{source/import-openapi → open-api/merge}/interfaces.js +0 -0
  131. /package/dist/{source/import-openapi → open-api/merge}/utils.d.ts +0 -0
  132. /package/dist/{source/import-openapi → open-api/merge}/utils.js +0 -0
  133. /package/dist/{cmds/source/migrate → source/migrate/dev-portal}/handler.d.ts +0 -0
  134. /package/dist/{cmds/source/migrate → source/migrate/dev-portal}/types.js +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../src/open-api/merge/handler.ts"],"names":[],"mappings":"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","sourcesContent":["/** biome-ignore-all lint/suspicious/noConsole: CLI output file */\nimport { existsSync, writeFileSync } from \"node:fs\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport { confirm } from \"@inquirer/prompts\";\nimport { mergeOpenApiDocuments } from \"@zuplo/openapi-tools\";\nimport { format } from \"prettier\";\nimport { logger } from \"../../common/logger.js\";\nimport {\n printCriticalFailureToConsoleAndExit,\n printDiagnosticsToConsole,\n printResultToConsoleAndExitGracefully,\n} from \"../../common/output.js\";\nimport {\n BASE_TEMPLATE,\n addOperationIdsAsNecessary,\n detectFormatFromContent,\n guessFileNameFromUrl,\n isUrl,\n parseOpenApiFile,\n type MergeMode,\n type ZuploOpenApiDocument,\n} from \"./merge-engine.js\";\n\nexport interface Arguments {\n prompt?: boolean;\n source: string;\n destination: string;\n \"merge-mode\"?: MergeMode;\n}\n\nexport async function importOpenApi(argv: Arguments): Promise<void> {\n let normalizedFilePath: string;\n\n // Check if source is a URL\n if (isUrl(argv.source)) {\n const parsedUrl = new URL(argv.source);\n const tempDir = tmpdir();\n const guessedFileName = guessFileNameFromUrl(argv.source);\n normalizedFilePath = path.join(tempDir, guessedFileName);\n await mkdir(path.dirname(normalizedFilePath), { recursive: true });\n\n try {\n const response = await fetch(parsedUrl);\n if (!response.ok) {\n await printCriticalFailureToConsoleAndExit(\n `Failed to download the remote OpenAPI file. Server responded with ${response.status} ${response.statusText}`\n );\n }\n\n const text = await response.text();\n\n try {\n const format = detectFormatFromContent(text);\n normalizedFilePath = `${normalizedFilePath}.${format === \"json\" ? \"json\" : \"yaml\"}`;\n await writeFile(normalizedFilePath, text, { flag: \"w+\" });\n } catch (err) {\n logger.error(err);\n await printCriticalFailureToConsoleAndExit(\n \"Failed to parse the remote OpenAPI file as either JSON or YAML.\"\n );\n }\n } catch (err) {\n logger.error(err);\n await printCriticalFailureToConsoleAndExit(\n \"Failed to download the remote OpenAPI file.\"\n );\n }\n } else {\n // This is a normal file\n const filePath = argv.source;\n normalizedFilePath = path.join(path.relative(process.cwd(), filePath));\n\n if (!existsSync(normalizedFilePath)) {\n await printCriticalFailureToConsoleAndExit(\n `The file ${normalizedFilePath} to import does not exist.`\n );\n }\n }\n\n const rawOpenApiSpec = await readFile(normalizedFilePath);\n const extName = path.extname(normalizedFilePath);\n const fileContent = rawOpenApiSpec.toString();\n const parsedOpenApiSpec = await parseOpenApiFile(extName, fileContent);\n\n const destination = argv.destination;\n\n if (!destination.endsWith(\".oas.json\")) {\n await printCriticalFailureToConsoleAndExit(\n \"Destination file name must end with '.oas.json'\"\n );\n }\n\n const destinationFilePath = path.join(\n path.relative(process.cwd(), destination)\n );\n\n let originalDocument: ZuploOpenApiDocument;\n if (!existsSync(destinationFilePath)) {\n // This is an initial import\n originalDocument = (await parseOpenApiFile(\n \".json\",\n BASE_TEMPLATE\n )) as ZuploOpenApiDocument;\n } else {\n const existingOpenApiSpec = await readFile(destinationFilePath);\n const existingFileContent = existingOpenApiSpec.toString();\n originalDocument = (await parseOpenApiFile(\n extName,\n existingFileContent\n )) as ZuploOpenApiDocument;\n }\n\n // Default to path-method merging\n const mergeMode = argv[\"merge-mode\"] || \"path-method\";\n\n const { created, merged, retained, mergedDocument } = mergeOpenApiDocuments(\n originalDocument,\n parsedOpenApiSpec,\n mergeMode\n );\n\n // Present the changes\n printDiagnosticsToConsole(\"This import will...\");\n printDiagnosticsToConsole(\"\");\n\n if (created.size > 0) {\n printDiagnosticsToConsole(\n `Create ${created.size} new ${created.size > 1 ? \"operations\" : \"operation\"}`\n );\n printDiagnosticsToConsole(\"\");\n created.forEach((operation) => {\n printDiagnosticsToConsole(operation);\n });\n printDiagnosticsToConsole(\"\");\n }\n\n if (merged.size > 0) {\n printDiagnosticsToConsole(\n `Merge ${merged.size} ${merged.size > 1 ? \"operations\" : \"operation\"}`\n );\n printDiagnosticsToConsole(\"\");\n merged.forEach((operation) => {\n printDiagnosticsToConsole(operation);\n });\n printDiagnosticsToConsole(\"\");\n }\n\n if (retained.size > 0) {\n printDiagnosticsToConsole(\n `Retain ${retained.size} ${retained.size > 1 ? \"operations\" : \"operation\"}`\n );\n printDiagnosticsToConsole(\"\");\n retained.forEach((operation) => {\n printDiagnosticsToConsole(operation);\n });\n printDiagnosticsToConsole(\"\");\n }\n\n if (argv.prompt) {\n printDiagnosticsToConsole(\"\");\n const answer = await confirm({ message: \"Proceed?\", default: true });\n if (!answer) {\n await printResultToConsoleAndExitGracefully(\"Import cancelled.\");\n }\n }\n\n addOperationIdsAsNecessary(mergedDocument);\n\n const formattedOpenApi = await format(JSON.stringify(mergedDocument), {\n parser: \"json-stringify\",\n });\n\n // Create folder structure if needed\n if (!existsSync(destinationFilePath)) {\n // Get the directory path of the file\n const dir = path.dirname(destinationFilePath);\n\n // Ensure that the directory exists (creates parent directories if necessary)\n await mkdir(dir, { recursive: true });\n }\n\n writeFileSync(destinationFilePath, formattedOpenApi, {\n flag: \"w\",\n });\n\n await printResultToConsoleAndExitGracefully(\n `Import successful. File written to ${destinationFilePath}`\n );\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=handler.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.spec.d.ts","sourceRoot":"","sources":["../../../src/open-api/merge/handler.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,335 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { afterEach, beforeEach, describe, it } from "node:test";
4
+ import { fileURLToPath } from "node:url";
5
+ import assert from "node:assert";
6
+ import { importOpenApi } from "./handler.js";
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ const testTmpPath = path.join(__dirname, "..", "..", "__tests__", "test-tmp", "merge-handler");
10
+ describe("Merge Handler", () => {
11
+ beforeEach(async () => {
12
+ await fs.mkdir(testTmpPath, { recursive: true });
13
+ });
14
+ afterEach(async () => {
15
+ try {
16
+ await fs.rm(testTmpPath, { recursive: true, force: true });
17
+ }
18
+ catch {
19
+ }
20
+ });
21
+ describe("importOpenApi", () => {
22
+ it("should merge source into new destination file", async () => {
23
+ const sourcePath = path.join(testTmpPath, "source.oas.json");
24
+ const destPath = path.join(testTmpPath, "dest.oas.json");
25
+ const sourceDoc = {
26
+ openapi: "3.1.0",
27
+ info: {
28
+ title: "Source API",
29
+ version: "1.0.0",
30
+ },
31
+ paths: {
32
+ "/users": {
33
+ get: {
34
+ summary: "Get users",
35
+ operationId: "getUsers",
36
+ responses: {
37
+ "200": {
38
+ description: "Success",
39
+ },
40
+ },
41
+ },
42
+ },
43
+ },
44
+ };
45
+ await fs.writeFile(sourcePath, JSON.stringify(sourceDoc, null, 2));
46
+ await importOpenApi({
47
+ source: sourcePath,
48
+ destination: destPath,
49
+ prompt: false,
50
+ });
51
+ const result = JSON.parse(await fs.readFile(destPath, "utf-8"));
52
+ assert.strictEqual(result.openapi, "3.1.0");
53
+ assert.strictEqual(result.paths["/users"].get.summary, "Get users");
54
+ assert.strictEqual(result.paths["/users"].get.operationId, "getUsers");
55
+ });
56
+ it("should merge source into existing destination file", async () => {
57
+ const sourcePath = path.join(testTmpPath, "source.oas.json");
58
+ const destPath = path.join(testTmpPath, "dest.oas.json");
59
+ const existingDoc = {
60
+ openapi: "3.1.0",
61
+ info: {
62
+ title: "Existing API",
63
+ version: "1.0.0",
64
+ },
65
+ paths: {
66
+ "/products": {
67
+ get: {
68
+ summary: "Get products",
69
+ operationId: "getProducts",
70
+ responses: {
71
+ "200": {
72
+ description: "Success",
73
+ },
74
+ },
75
+ },
76
+ },
77
+ },
78
+ };
79
+ const sourceDoc = {
80
+ openapi: "3.1.0",
81
+ info: {
82
+ title: "Source API",
83
+ version: "1.0.0",
84
+ },
85
+ paths: {
86
+ "/users": {
87
+ get: {
88
+ summary: "Get users",
89
+ operationId: "getUsers",
90
+ responses: {
91
+ "200": {
92
+ description: "Success",
93
+ },
94
+ },
95
+ },
96
+ },
97
+ },
98
+ };
99
+ await fs.writeFile(destPath, JSON.stringify(existingDoc, null, 2));
100
+ await fs.writeFile(sourcePath, JSON.stringify(sourceDoc, null, 2));
101
+ await importOpenApi({
102
+ source: sourcePath,
103
+ destination: destPath,
104
+ prompt: false,
105
+ });
106
+ const result = JSON.parse(await fs.readFile(destPath, "utf-8"));
107
+ assert.strictEqual(result.paths["/products"].get.summary, "Get products");
108
+ assert.strictEqual(result.paths["/users"].get.summary, "Get users");
109
+ });
110
+ it("should support YAML source files", async () => {
111
+ const sourcePath = path.join(testTmpPath, "source.oas.yaml");
112
+ const destPath = path.join(testTmpPath, "dest.oas.json");
113
+ const sourceDoc = `openapi: 3.1.0
114
+ info:
115
+ title: Source API
116
+ version: 1.0.0
117
+ paths:
118
+ /users:
119
+ get:
120
+ summary: Get users
121
+ operationId: getUsers
122
+ responses:
123
+ '200':
124
+ description: Success`;
125
+ await fs.writeFile(sourcePath, sourceDoc);
126
+ await importOpenApi({
127
+ source: sourcePath,
128
+ destination: destPath,
129
+ prompt: false,
130
+ });
131
+ const result = JSON.parse(await fs.readFile(destPath, "utf-8"));
132
+ assert.strictEqual(result.paths["/users"].get.summary, "Get users");
133
+ });
134
+ it("should use path-method merge mode by default", async () => {
135
+ const sourcePath = path.join(testTmpPath, "source.oas.json");
136
+ const destPath = path.join(testTmpPath, "dest.oas.json");
137
+ const existingDoc = {
138
+ openapi: "3.1.0",
139
+ info: {
140
+ title: "Existing API",
141
+ version: "1.0.0",
142
+ },
143
+ paths: {
144
+ "/users": {
145
+ get: {
146
+ summary: "Original summary",
147
+ operationId: "getUsers",
148
+ responses: {
149
+ "200": {
150
+ description: "Success",
151
+ },
152
+ },
153
+ },
154
+ },
155
+ },
156
+ };
157
+ const sourceDoc = {
158
+ openapi: "3.1.0",
159
+ info: {
160
+ title: "Source API",
161
+ version: "1.0.0",
162
+ },
163
+ paths: {
164
+ "/users": {
165
+ get: {
166
+ summary: "Updated summary",
167
+ operationId: "getUsersUpdated",
168
+ responses: {
169
+ "200": {
170
+ description: "Updated Success",
171
+ },
172
+ },
173
+ },
174
+ },
175
+ },
176
+ };
177
+ await fs.writeFile(destPath, JSON.stringify(existingDoc, null, 2));
178
+ await fs.writeFile(sourcePath, JSON.stringify(sourceDoc, null, 2));
179
+ await importOpenApi({
180
+ source: sourcePath,
181
+ destination: destPath,
182
+ prompt: false,
183
+ });
184
+ const result = JSON.parse(await fs.readFile(destPath, "utf-8"));
185
+ assert.strictEqual(result.paths["/users"].get.summary, "Updated summary");
186
+ });
187
+ it("should support operation-id merge mode", async () => {
188
+ const sourcePath = path.join(testTmpPath, "source.oas.json");
189
+ const destPath = path.join(testTmpPath, "dest.oas.json");
190
+ const existingDoc = {
191
+ openapi: "3.1.0",
192
+ info: {
193
+ title: "Existing API",
194
+ version: "1.0.0",
195
+ },
196
+ paths: {
197
+ "/users": {
198
+ get: {
199
+ summary: "Original summary",
200
+ operationId: "getUsers",
201
+ responses: {
202
+ "200": {
203
+ description: "Success",
204
+ },
205
+ },
206
+ },
207
+ },
208
+ },
209
+ };
210
+ const sourceDoc = {
211
+ openapi: "3.1.0",
212
+ info: {
213
+ title: "Source API",
214
+ version: "1.0.0",
215
+ },
216
+ paths: {
217
+ "/v2/users": {
218
+ get: {
219
+ summary: "Updated summary",
220
+ operationId: "getUsers",
221
+ responses: {
222
+ "200": {
223
+ description: "Updated Success",
224
+ },
225
+ },
226
+ },
227
+ },
228
+ },
229
+ };
230
+ await fs.writeFile(destPath, JSON.stringify(existingDoc, null, 2));
231
+ await fs.writeFile(sourcePath, JSON.stringify(sourceDoc, null, 2));
232
+ await importOpenApi({
233
+ source: sourcePath,
234
+ destination: destPath,
235
+ prompt: false,
236
+ "merge-mode": "operation-id",
237
+ });
238
+ const result = JSON.parse(await fs.readFile(destPath, "utf-8"));
239
+ assert.strictEqual(result.paths["/users"], undefined);
240
+ assert.strictEqual(result.paths["/v2/users"].get.summary, "Updated summary");
241
+ });
242
+ it("should create output directory if it doesn't exist", async () => {
243
+ const sourcePath = path.join(testTmpPath, "source.oas.json");
244
+ const destPath = path.join(testTmpPath, "nested", "dir", "dest.oas.json");
245
+ const sourceDoc = {
246
+ openapi: "3.1.0",
247
+ info: {
248
+ title: "Source API",
249
+ version: "1.0.0",
250
+ },
251
+ paths: {
252
+ "/users": {
253
+ get: {
254
+ summary: "Get users",
255
+ operationId: "getUsers",
256
+ responses: {
257
+ "200": {
258
+ description: "Success",
259
+ },
260
+ },
261
+ },
262
+ },
263
+ },
264
+ };
265
+ await fs.writeFile(sourcePath, JSON.stringify(sourceDoc, null, 2));
266
+ await importOpenApi({
267
+ source: sourcePath,
268
+ destination: destPath,
269
+ prompt: false,
270
+ });
271
+ const result = JSON.parse(await fs.readFile(destPath, "utf-8"));
272
+ assert.strictEqual(result.paths["/users"].get.summary, "Get users");
273
+ });
274
+ it("should add operation IDs if missing", async () => {
275
+ const sourcePath = path.join(testTmpPath, "source.oas.json");
276
+ const destPath = path.join(testTmpPath, "dest.oas.json");
277
+ const sourceDoc = {
278
+ openapi: "3.1.0",
279
+ info: {
280
+ title: "Source API",
281
+ version: "1.0.0",
282
+ },
283
+ paths: {
284
+ "/users": {
285
+ get: {
286
+ summary: "Get users",
287
+ responses: {
288
+ "200": {
289
+ description: "Success",
290
+ },
291
+ },
292
+ },
293
+ },
294
+ },
295
+ };
296
+ await fs.writeFile(sourcePath, JSON.stringify(sourceDoc, null, 2));
297
+ await importOpenApi({
298
+ source: sourcePath,
299
+ destination: destPath,
300
+ prompt: false,
301
+ });
302
+ const result = JSON.parse(await fs.readFile(destPath, "utf-8"));
303
+ assert.strictEqual(typeof result.paths["/users"].get.operationId, "string");
304
+ assert.strictEqual(result.paths["/users"].get.operationId.length > 0, true);
305
+ });
306
+ it("should throw error when source file does not exist", async () => {
307
+ const sourcePath = path.join(testTmpPath, "nonexistent.oas.json");
308
+ const destPath = path.join(testTmpPath, "dest.oas.json");
309
+ await assert.rejects(() => importOpenApi({
310
+ source: sourcePath,
311
+ destination: destPath,
312
+ prompt: false,
313
+ }), /does not exist/);
314
+ });
315
+ it("should throw error when destination doesn't end with .oas.json", async () => {
316
+ const sourcePath = path.join(testTmpPath, "source.oas.json");
317
+ const destPath = path.join(testTmpPath, "dest.json");
318
+ const sourceDoc = {
319
+ openapi: "3.1.0",
320
+ info: {
321
+ title: "Source API",
322
+ version: "1.0.0",
323
+ },
324
+ paths: {},
325
+ };
326
+ await fs.writeFile(sourcePath, JSON.stringify(sourceDoc, null, 2));
327
+ await assert.rejects(() => importOpenApi({
328
+ source: sourcePath,
329
+ destination: destPath,
330
+ prompt: false,
331
+ }), /must end with '.oas.json'/);
332
+ });
333
+ });
334
+ });
335
+ //# sourceMappingURL=handler.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.spec.js","sourceRoot":"","sources":["../../../src/open-api/merge/handler.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { afterEach, beforeEach, describe, it } from \"node:test\";\nimport { fileURLToPath } from \"node:url\";\nimport assert from \"node:assert\";\nimport { importOpenApi } from \"./handler.js\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nconst testTmpPath = path.join(\n __dirname,\n \"..\",\n \"..\",\n \"__tests__\",\n \"test-tmp\",\n \"merge-handler\"\n);\n\ndescribe(\"Merge Handler\", () => {\n beforeEach(async () => {\n // Ensure test-tmp directory exists\n await fs.mkdir(testTmpPath, { recursive: true });\n });\n\n afterEach(async () => {\n // Clean up test files\n try {\n await fs.rm(testTmpPath, { recursive: true, force: true });\n } catch {\n // Ignore cleanup errors\n }\n });\n\n describe(\"importOpenApi\", () => {\n it(\"should merge source into new destination file\", async () => {\n const sourcePath = path.join(testTmpPath, \"source.oas.json\");\n const destPath = path.join(testTmpPath, \"dest.oas.json\");\n\n const sourceDoc = {\n openapi: \"3.1.0\",\n info: {\n title: \"Source API\",\n version: \"1.0.0\",\n },\n paths: {\n \"/users\": {\n get: {\n summary: \"Get users\",\n operationId: \"getUsers\",\n responses: {\n \"200\": {\n description: \"Success\",\n },\n },\n },\n },\n },\n };\n\n await fs.writeFile(sourcePath, JSON.stringify(sourceDoc, null, 2));\n\n await importOpenApi({\n source: sourcePath,\n destination: destPath,\n prompt: false,\n });\n\n const result = JSON.parse(await fs.readFile(destPath, \"utf-8\"));\n assert.strictEqual(result.openapi, \"3.1.0\");\n assert.strictEqual(result.paths[\"/users\"].get.summary, \"Get users\");\n assert.strictEqual(result.paths[\"/users\"].get.operationId, \"getUsers\");\n });\n\n it(\"should merge source into existing destination file\", async () => {\n const sourcePath = path.join(testTmpPath, \"source.oas.json\");\n const destPath = path.join(testTmpPath, \"dest.oas.json\");\n\n const existingDoc = {\n openapi: \"3.1.0\",\n info: {\n title: \"Existing API\",\n version: \"1.0.0\",\n },\n paths: {\n \"/products\": {\n get: {\n summary: \"Get products\",\n operationId: \"getProducts\",\n responses: {\n \"200\": {\n description: \"Success\",\n },\n },\n },\n },\n },\n };\n\n const sourceDoc = {\n openapi: \"3.1.0\",\n info: {\n title: \"Source API\",\n version: \"1.0.0\",\n },\n paths: {\n \"/users\": {\n get: {\n summary: \"Get users\",\n operationId: \"getUsers\",\n responses: {\n \"200\": {\n description: \"Success\",\n },\n },\n },\n },\n },\n };\n\n await fs.writeFile(destPath, JSON.stringify(existingDoc, null, 2));\n await fs.writeFile(sourcePath, JSON.stringify(sourceDoc, null, 2));\n\n await importOpenApi({\n source: sourcePath,\n destination: destPath,\n prompt: false,\n });\n\n const result = JSON.parse(await fs.readFile(destPath, \"utf-8\"));\n assert.strictEqual(result.paths[\"/products\"].get.summary, \"Get products\");\n assert.strictEqual(result.paths[\"/users\"].get.summary, \"Get users\");\n });\n\n it(\"should support YAML source files\", async () => {\n const sourcePath = path.join(testTmpPath, \"source.oas.yaml\");\n const destPath = path.join(testTmpPath, \"dest.oas.json\");\n\n const sourceDoc = `openapi: 3.1.0\ninfo:\n title: Source API\n version: 1.0.0\npaths:\n /users:\n get:\n summary: Get users\n operationId: getUsers\n responses:\n '200':\n description: Success`;\n\n await fs.writeFile(sourcePath, sourceDoc);\n\n await importOpenApi({\n source: sourcePath,\n destination: destPath,\n prompt: false,\n });\n\n const result = JSON.parse(await fs.readFile(destPath, \"utf-8\"));\n assert.strictEqual(result.paths[\"/users\"].get.summary, \"Get users\");\n });\n\n it(\"should use path-method merge mode by default\", async () => {\n const sourcePath = path.join(testTmpPath, \"source.oas.json\");\n const destPath = path.join(testTmpPath, \"dest.oas.json\");\n\n const existingDoc = {\n openapi: \"3.1.0\",\n info: {\n title: \"Existing API\",\n version: \"1.0.0\",\n },\n paths: {\n \"/users\": {\n get: {\n summary: \"Original summary\",\n operationId: \"getUsers\",\n responses: {\n \"200\": {\n description: \"Success\",\n },\n },\n },\n },\n },\n };\n\n const sourceDoc = {\n openapi: \"3.1.0\",\n info: {\n title: \"Source API\",\n version: \"1.0.0\",\n },\n paths: {\n \"/users\": {\n get: {\n summary: \"Updated summary\",\n operationId: \"getUsersUpdated\",\n responses: {\n \"200\": {\n description: \"Updated Success\",\n },\n },\n },\n },\n },\n };\n\n await fs.writeFile(destPath, JSON.stringify(existingDoc, null, 2));\n await fs.writeFile(sourcePath, JSON.stringify(sourceDoc, null, 2));\n\n await importOpenApi({\n source: sourcePath,\n destination: destPath,\n prompt: false,\n });\n\n const result = JSON.parse(await fs.readFile(destPath, \"utf-8\"));\n // In path-method mode, matching path+method should be merged\n assert.strictEqual(result.paths[\"/users\"].get.summary, \"Updated summary\");\n });\n\n it(\"should support operation-id merge mode\", async () => {\n const sourcePath = path.join(testTmpPath, \"source.oas.json\");\n const destPath = path.join(testTmpPath, \"dest.oas.json\");\n\n const existingDoc = {\n openapi: \"3.1.0\",\n info: {\n title: \"Existing API\",\n version: \"1.0.0\",\n },\n paths: {\n \"/users\": {\n get: {\n summary: \"Original summary\",\n operationId: \"getUsers\",\n responses: {\n \"200\": {\n description: \"Success\",\n },\n },\n },\n },\n },\n };\n\n const sourceDoc = {\n openapi: \"3.1.0\",\n info: {\n title: \"Source API\",\n version: \"1.0.0\",\n },\n paths: {\n \"/v2/users\": {\n get: {\n summary: \"Updated summary\",\n operationId: \"getUsers\",\n responses: {\n \"200\": {\n description: \"Updated Success\",\n },\n },\n },\n },\n },\n };\n\n await fs.writeFile(destPath, JSON.stringify(existingDoc, null, 2));\n await fs.writeFile(sourcePath, JSON.stringify(sourceDoc, null, 2));\n\n await importOpenApi({\n source: sourcePath,\n destination: destPath,\n prompt: false,\n \"merge-mode\": \"operation-id\",\n });\n\n const result = JSON.parse(await fs.readFile(destPath, \"utf-8\"));\n // In operation-id mode, matching operationId should be merged\n // Original path should be removed, new path should exist\n assert.strictEqual(result.paths[\"/users\"], undefined);\n assert.strictEqual(\n result.paths[\"/v2/users\"].get.summary,\n \"Updated summary\"\n );\n });\n\n it(\"should create output directory if it doesn't exist\", async () => {\n const sourcePath = path.join(testTmpPath, \"source.oas.json\");\n const destPath = path.join(testTmpPath, \"nested\", \"dir\", \"dest.oas.json\");\n\n const sourceDoc = {\n openapi: \"3.1.0\",\n info: {\n title: \"Source API\",\n version: \"1.0.0\",\n },\n paths: {\n \"/users\": {\n get: {\n summary: \"Get users\",\n operationId: \"getUsers\",\n responses: {\n \"200\": {\n description: \"Success\",\n },\n },\n },\n },\n },\n };\n\n await fs.writeFile(sourcePath, JSON.stringify(sourceDoc, null, 2));\n\n await importOpenApi({\n source: sourcePath,\n destination: destPath,\n prompt: false,\n });\n\n const result = JSON.parse(await fs.readFile(destPath, \"utf-8\"));\n assert.strictEqual(result.paths[\"/users\"].get.summary, \"Get users\");\n });\n\n it(\"should add operation IDs if missing\", async () => {\n const sourcePath = path.join(testTmpPath, \"source.oas.json\");\n const destPath = path.join(testTmpPath, \"dest.oas.json\");\n\n const sourceDoc = {\n openapi: \"3.1.0\",\n info: {\n title: \"Source API\",\n version: \"1.0.0\",\n },\n paths: {\n \"/users\": {\n get: {\n summary: \"Get users\",\n // No operationId\n responses: {\n \"200\": {\n description: \"Success\",\n },\n },\n },\n },\n },\n };\n\n await fs.writeFile(sourcePath, JSON.stringify(sourceDoc, null, 2));\n\n await importOpenApi({\n source: sourcePath,\n destination: destPath,\n prompt: false,\n });\n\n const result = JSON.parse(await fs.readFile(destPath, \"utf-8\"));\n // Operation ID should be auto-generated\n assert.strictEqual(\n typeof result.paths[\"/users\"].get.operationId,\n \"string\"\n );\n assert.strictEqual(\n result.paths[\"/users\"].get.operationId.length > 0,\n true\n );\n });\n\n it(\"should throw error when source file does not exist\", async () => {\n const sourcePath = path.join(testTmpPath, \"nonexistent.oas.json\");\n const destPath = path.join(testTmpPath, \"dest.oas.json\");\n\n await assert.rejects(\n () =>\n importOpenApi({\n source: sourcePath,\n destination: destPath,\n prompt: false,\n }),\n /does not exist/\n );\n });\n\n it(\"should throw error when destination doesn't end with .oas.json\", async () => {\n const sourcePath = path.join(testTmpPath, \"source.oas.json\");\n const destPath = path.join(testTmpPath, \"dest.json\");\n\n const sourceDoc = {\n openapi: \"3.1.0\",\n info: {\n title: \"Source API\",\n version: \"1.0.0\",\n },\n paths: {},\n };\n\n await fs.writeFile(sourcePath, JSON.stringify(sourceDoc, null, 2));\n\n await assert.rejects(\n () =>\n importOpenApi({\n source: sourcePath,\n destination: destPath,\n prompt: false,\n }),\n /must end with '.oas.json'/\n );\n });\n });\n});\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../../src/open-api/merge/interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB,CAAC;AAEF,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC;AACzC,MAAM,MAAM,gBAAgB,GAAG,WAAW,CAAC,WAAW,GAAG,MAAM,CAAC;AAGhE,eAAO,MAAM,4BAA4B,YAAY,CAAC;AACtD,eAAO,MAAM,qCAAqC,eAAe,CAAC;AAClE,eAAO,MAAM,uBAAuB,gBACa,CAAC;AAClD,eAAO,MAAM,wBAAwB,iBACa,CAAC;AAEnD,MAAM,MAAM,oBAAoB,GAAG,MAAM,CACvC,WAAW,CAAC,QAAQ,CAAC,8BAA8B,CAAC,EACpD;IACE,KAAK,EAAE,uBAAuB,GAAG,SAAS,CAAC;CAC5C,CACF,CAAC;AACF,MAAM,MAAM,uBAAuB,GAAG,MAAM,CAC1C,MAAM,EACN,0BAA0B,GAAG,SAAS,CACvC,CAAC;AACF,MAAM,MAAM,0BAA0B,GAAG,MAAM,CAC7C,WAAW,CAAC,cAAc,CAAC,8BAA8B,CAAC,GACxD,6BAA6B,EAC/B;IACE,GAAG,CAAC,EAAE,2BAA2B,CAAC;IAClC,GAAG,CAAC,EAAE,2BAA2B,CAAC;IAClC,IAAI,CAAC,EAAE,2BAA2B,CAAC;IACnC,MAAM,CAAC,EAAE,2BAA2B,CAAC;IACrC,OAAO,CAAC,EAAE,2BAA2B,CAAC;IACtC,IAAI,CAAC,EAAE,2BAA2B,CAAC;IACnC,KAAK,CAAC,EAAE,2BAA2B,CAAC;IACpC,KAAK,CAAC,EAAE,2BAA2B,CAAC;CACrC,CACF,CAAC;AACF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,CAAC,uBAAuB,CAAC,CAAC,EAAE,8BAA8B,CAAC;CAC5D,CAAC;AACF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,QAAQ,EAAE,QAAQ,CAAC;CACpB,CAAC;AACF,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,aAAa,CAAC;AAClD,MAAM,MAAM,2BAA2B,GAAG,MAAM,CAC9C,WAAW,CAAC,eAAe,CAAC,8BAA8B,CAAC,EAC3D;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,CACxB,CAAC;AACF,MAAM,MAAM,+BAA+B,GAAG,IAAI,CAChD,kBAAkB,EAChB,aAAa,GACb,MAAM,GACN,SAAS,GACT,OAAO,GACP,KAAK,GACL,SAAS,GACT,aAAa,GACb,YAAY,GACZ,WAAW,GACX,MAAM,GACN,SAAS,GACT,KAAK,CACR,CAAC;AACF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,CAAC,wBAAwB,CAAC,CAAC,EAAE,+BAA+B,CAAC;IAC7D,CAAC,qCAAqC,CAAC,CAAC,EAAE,OAAO,CAAC;CACnD,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../../../src/open-api/merge/interfaces.ts"],"names":[],"mappings":"AAcA,MAAM,CAAC,MAAM,4BAA4B,GAAG,SAAS,CAAC;AACtD,MAAM,CAAC,MAAM,qCAAqC,GAAG,YAAY,CAAC;AAClE,MAAM,CAAC,MAAM,uBAAuB,GAClC,GAAG,4BAA4B,OAAgB,CAAC;AAClD,MAAM,CAAC,MAAM,wBAAwB,GACnC,GAAG,4BAA4B,QAAiB,CAAC","sourcesContent":["import { RouteConfiguration } from \"@zuplo/runtime\";\nimport { OpenAPIV3_1 } from \"openapi-types\";\nimport type { ErrorObject } from \"./ajv.js\";\n\nexport type ValidationResult = {\n isValid: boolean;\n errors: ErrorObject[];\n warnings: ErrorObject[];\n};\n\ntype Modify<T, R> = Omit<T, keyof R> & R;\nexport type ZuploHttpMethods = OpenAPIV3_1.HttpMethods | string;\n\n// TODO: This should all be moved to the backend eventually\nexport const ZUPLO_OPEN_API_VENDOR_PREFIX = \"x-zuplo\";\nexport const OPEN_API_INTERNAL_PROPERTY_VENDOR_TAG = \"x-internal\";\nexport const ZUPLO_OPEN_API_PATH_KEY =\n `${ZUPLO_OPEN_API_VENDOR_PREFIX}-path` as const;\nexport const ZUPLO_OPEN_API_ROUTE_KEY =\n `${ZUPLO_OPEN_API_VENDOR_PREFIX}-route` as const;\n\nexport type ZuploOpenApiDocument = Modify<\n OpenAPIV3_1.Document<ZuploOpenApiOperationExtension>,\n {\n paths: ZuploOpenApiPathsObject | undefined;\n }\n>;\nexport type ZuploOpenApiPathsObject = Record<\n string,\n ZuploOpenApiPathItemObject | undefined\n>;\nexport type ZuploOpenApiPathItemObject = Modify<\n OpenAPIV3_1.PathItemObject<ZuploOpenApiOperationExtension> &\n ZuploOpenApiPathItemExtension,\n {\n get?: ZuploOpenApiOperationObject;\n put?: ZuploOpenApiOperationObject;\n post?: ZuploOpenApiOperationObject;\n delete?: ZuploOpenApiOperationObject;\n options?: ZuploOpenApiOperationObject;\n head?: ZuploOpenApiOperationObject;\n patch?: ZuploOpenApiOperationObject;\n trace?: ZuploOpenApiOperationObject;\n }\n>;\nexport type ZuploOpenApiPathItemExtension = {\n [ZUPLO_OPEN_API_PATH_KEY]?: ZuploOpenApiPathItemProperties;\n};\nexport type ZuploOpenApiPathItemProperties = {\n pathMode: PathMode;\n};\nexport type PathMode = \"open-api\" | \"url-pattern\";\nexport type ZuploOpenApiOperationObject = Modify<\n OpenAPIV3_1.OperationObject<ZuploOpenApiOperationExtension>,\n { operationId: string }\n>;\nexport type ZuploOpenApiOperationProperties = Omit<\n RouteConfiguration,\n | \"operationId\"\n | \"path\"\n | \"methods\"\n | \"label\"\n | \"key\"\n | \"summary\"\n | \"description\"\n | \"parameters\"\n | \"responses\"\n | \"tags\"\n | \"version\"\n | \"raw\"\n>;\nexport type ZuploOpenApiOperationExtension = {\n [ZUPLO_OPEN_API_ROUTE_KEY]?: ZuploOpenApiOperationProperties;\n [OPEN_API_INTERNAL_PROPERTY_VENDOR_TAG]?: boolean;\n};\n"]}
@@ -0,0 +1,23 @@
1
+ import {
2
+ detectFormatFromContent as detectFormatFromContentCommon,
3
+ detectFormatFromExtension,
4
+ type FileFormat,
5
+ } from "../../common/file-format.js";
6
+ import type { ZuploOpenApiDocument } from "./interfaces.js";
7
+ import {
8
+ OPEN_API_FILE_TYPES,
9
+ addOperationIdsAsNecessary,
10
+ parseOpenApiFile,
11
+ } from "./utils.js";
12
+ export type MergeMode = "path-method" | "operation-id";
13
+ export type { ZuploOpenApiDocument, FileFormat };
14
+ export { OPEN_API_FILE_TYPES, addOperationIdsAsNecessary, parseOpenApiFile };
15
+ export {
16
+ detectFormatFromExtension as detectFormat,
17
+ detectFormatFromContentCommon as detectFormatFromContent,
18
+ };
19
+ export declare const BASE_TEMPLATE =
20
+ '\n{\n "openapi": "3.1.0",\n "info": {\n "version": "1.0.0",\n "title": "My Zuplo API"\n },\n "paths": {}\n}\n\n';
21
+ export declare function isUrl(source: string): boolean;
22
+ export declare function guessFileNameFromUrl(url: string): string;
23
+ //# sourceMappingURL=merge-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-engine.d.ts","sourceRoot":"","sources":["../../../src/open-api/merge/merge-engine.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,IAAI,6BAA6B,EACxD,yBAAyB,EACzB,KAAK,UAAU,EAChB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,cAAc,CAAC;AACvD,YAAY,EAAE,oBAAoB,EAAE,UAAU,EAAE,CAAC;AAGjD,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,CAAC;AAG7E,OAAO,EACL,yBAAyB,IAAI,YAAY,EACzC,6BAA6B,IAAI,uBAAuB,GACzD,CAAC;AAEF,eAAO,MAAM,aAAa,8IAUzB,CAAC;AAKF,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAO7C;AAKD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAOxD"}
@@ -0,0 +1,33 @@
1
+ import { detectFormatFromContent as detectFormatFromContentCommon, detectFormatFromExtension, } from "../../common/file-format.js";
2
+ import { OPEN_API_FILE_TYPES, addOperationIdsAsNecessary, parseOpenApiFile, } from "./utils.js";
3
+ export { OPEN_API_FILE_TYPES, addOperationIdsAsNecessary, parseOpenApiFile };
4
+ export { detectFormatFromExtension as detectFormat, detectFormatFromContentCommon as detectFormatFromContent, };
5
+ export const BASE_TEMPLATE = `
6
+ {
7
+ "openapi": "3.1.0",
8
+ "info": {
9
+ "version": "1.0.0",
10
+ "title": "My Zuplo API"
11
+ },
12
+ "paths": {}
13
+ }
14
+
15
+ `;
16
+ export function isUrl(source) {
17
+ try {
18
+ new URL(source);
19
+ return true;
20
+ }
21
+ catch {
22
+ return false;
23
+ }
24
+ }
25
+ export function guessFileNameFromUrl(url) {
26
+ const parsedUrl = new URL(url);
27
+ const basename = parsedUrl.pathname
28
+ .split("/")
29
+ .pop()
30
+ ?.replace(/\.[^/.]+$/, "");
31
+ return basename || `imported-${Buffer.from(url).toString("base64")}`;
32
+ }
33
+ //# sourceMappingURL=merge-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-engine.js","sourceRoot":"","sources":["../../../src/open-api/merge/merge-engine.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,IAAI,6BAA6B,EACxD,yBAAyB,GAE1B,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAMpB,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,CAAC;AAG7E,OAAO,EACL,yBAAyB,IAAI,YAAY,EACzC,6BAA6B,IAAI,uBAAuB,GACzD,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;CAU5B,CAAC;AAKF,MAAM,UAAU,KAAK,CAAC,MAAc;IAClC,IAAI,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAKD,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ;SAChC,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,EAAE;QACN,EAAE,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC7B,OAAO,QAAQ,IAAI,YAAY,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;AACvE,CAAC","sourcesContent":["import {\n detectFormatFromContent as detectFormatFromContentCommon,\n detectFormatFromExtension,\n type FileFormat,\n} from \"../../common/file-format.js\";\nimport type { ZuploOpenApiDocument } from \"./interfaces.js\";\nimport {\n OPEN_API_FILE_TYPES,\n addOperationIdsAsNecessary,\n parseOpenApiFile,\n} from \"./utils.js\";\n\nexport type MergeMode = \"path-method\" | \"operation-id\";\nexport type { ZuploOpenApiDocument, FileFormat };\n\n// Re-export utilities\nexport { OPEN_API_FILE_TYPES, addOperationIdsAsNecessary, parseOpenApiFile };\n\n// Re-export common file format functions for backward compatibility\nexport {\n detectFormatFromExtension as detectFormat,\n detectFormatFromContentCommon as detectFormatFromContent,\n};\n\nexport const BASE_TEMPLATE = `\n{\n \"openapi\": \"3.1.0\",\n \"info\": {\n \"version\": \"1.0.0\",\n \"title\": \"My Zuplo API\"\n },\n \"paths\": {}\n}\n\n`;\n\n/**\n * Detect if source is a URL\n */\nexport function isUrl(source: string): boolean {\n try {\n new URL(source);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Guess file name from URL\n */\nexport function guessFileNameFromUrl(url: string): string {\n const parsedUrl = new URL(url);\n const basename = parsedUrl.pathname\n .split(\"/\")\n .pop()\n ?.replace(/\\.[^/.]+$/, \"\");\n return basename || `imported-${Buffer.from(url).toString(\"base64\")}`;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=merge-engine.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-engine.spec.d.ts","sourceRoot":"","sources":["../../../src/open-api/merge/merge-engine.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,117 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert";
3
+ import { isUrl, guessFileNameFromUrl, detectFormat, detectFormatFromContent, } from "./merge-engine.js";
4
+ describe("Merge Engine", () => {
5
+ describe("isUrl", () => {
6
+ it("should return true for valid HTTP URLs", () => {
7
+ assert.strictEqual(isUrl("http://example.com"), true);
8
+ assert.strictEqual(isUrl("http://example.com/api.json"), true);
9
+ assert.strictEqual(isUrl("http://api.example.com/v1/openapi.yaml"), true);
10
+ });
11
+ it("should return true for valid HTTPS URLs", () => {
12
+ assert.strictEqual(isUrl("https://example.com"), true);
13
+ assert.strictEqual(isUrl("https://example.com/api.json"), true);
14
+ assert.strictEqual(isUrl("https://api.example.com/v1/openapi.yaml"), true);
15
+ });
16
+ it("should return true for URLs with query parameters", () => {
17
+ assert.strictEqual(isUrl("https://example.com/api?version=1.0"), true);
18
+ assert.strictEqual(isUrl("https://example.com/api?format=json&version=2"), true);
19
+ });
20
+ it("should return true for URLs with fragments", () => {
21
+ assert.strictEqual(isUrl("https://example.com/api#section"), true);
22
+ assert.strictEqual(isUrl("https://example.com/api.json#definitions"), true);
23
+ });
24
+ it("should return true for URLs with ports", () => {
25
+ assert.strictEqual(isUrl("https://example.com:8080/api"), true);
26
+ assert.strictEqual(isUrl("http://localhost:3000/openapi.json"), true);
27
+ });
28
+ it("should return false for relative file paths", () => {
29
+ assert.strictEqual(isUrl("./api.json"), false);
30
+ assert.strictEqual(isUrl("../specs/openapi.yaml"), false);
31
+ assert.strictEqual(isUrl("docs/api.json"), false);
32
+ });
33
+ it("should return false for absolute file paths", () => {
34
+ assert.strictEqual(isUrl("/path/to/api.json"), false);
35
+ assert.strictEqual(isUrl("/Users/name/docs/openapi.yaml"), false);
36
+ });
37
+ it("should return false for filenames without paths", () => {
38
+ assert.strictEqual(isUrl("api.json"), false);
39
+ assert.strictEqual(isUrl("openapi.yaml"), false);
40
+ });
41
+ it("should return false for empty strings", () => {
42
+ assert.strictEqual(isUrl(""), false);
43
+ });
44
+ });
45
+ describe("guessFileNameFromUrl", () => {
46
+ it("should extract filename from URL path", () => {
47
+ assert.strictEqual(guessFileNameFromUrl("https://example.com/api.json"), "api");
48
+ assert.strictEqual(guessFileNameFromUrl("https://example.com/openapi.yaml"), "openapi");
49
+ });
50
+ it("should extract filename from URL with multiple path segments", () => {
51
+ assert.strictEqual(guessFileNameFromUrl("https://example.com/v1/specs/api.json"), "api");
52
+ assert.strictEqual(guessFileNameFromUrl("https://api.example.com/docs/v2/openapi.yaml"), "openapi");
53
+ });
54
+ it("should extract filename from URL with query parameters", () => {
55
+ assert.strictEqual(guessFileNameFromUrl("https://example.com/api.json?version=1"), "api");
56
+ assert.strictEqual(guessFileNameFromUrl("https://example.com/openapi.yaml?format=json&v=2"), "openapi");
57
+ });
58
+ it("should extract filename from URL with fragments", () => {
59
+ assert.strictEqual(guessFileNameFromUrl("https://example.com/api.json#section"), "api");
60
+ assert.strictEqual(guessFileNameFromUrl("https://example.com/spec.yaml#definitions"), "spec");
61
+ });
62
+ it("should handle URLs with no file extension", () => {
63
+ assert.strictEqual(guessFileNameFromUrl("https://example.com/api"), "api");
64
+ assert.strictEqual(guessFileNameFromUrl("https://example.com/v1/openapi"), "openapi");
65
+ });
66
+ it("should handle URLs ending with slash", () => {
67
+ const result = guessFileNameFromUrl("https://example.com/api/");
68
+ assert.strictEqual(result.startsWith("imported-"), true);
69
+ });
70
+ it("should generate base64 name for root URLs", () => {
71
+ const result = guessFileNameFromUrl("https://example.com/");
72
+ assert.strictEqual(result.startsWith("imported-"), true);
73
+ assert.strictEqual(result.length > 9, true);
74
+ });
75
+ it("should handle filenames with multiple dots", () => {
76
+ assert.strictEqual(guessFileNameFromUrl("https://example.com/api.v1.0.json"), "api.v1.0");
77
+ assert.strictEqual(guessFileNameFromUrl("https://example.com/my.api.spec.yaml"), "my.api.spec");
78
+ });
79
+ it("should handle complex URLs", () => {
80
+ assert.strictEqual(guessFileNameFromUrl("https://api.example.com:8080/v2/specs/openapi.json?auth=token#info"), "openapi");
81
+ });
82
+ it("should generate consistent base64 names for same URL", () => {
83
+ const url = "https://example.com/";
84
+ const name1 = guessFileNameFromUrl(url);
85
+ const name2 = guessFileNameFromUrl(url);
86
+ assert.strictEqual(name1, name2);
87
+ });
88
+ });
89
+ describe("detectFormat (re-exported from common)", () => {
90
+ it("should detect JSON format", () => {
91
+ assert.strictEqual(detectFormat("api.json"), "json");
92
+ assert.strictEqual(detectFormat("/path/to/api.json"), "json");
93
+ });
94
+ it("should detect YAML format", () => {
95
+ assert.strictEqual(detectFormat("api.yaml"), "yaml");
96
+ assert.strictEqual(detectFormat("api.yml"), "yaml");
97
+ });
98
+ it("should return null for unknown extensions", () => {
99
+ assert.strictEqual(detectFormat("api.txt"), null);
100
+ });
101
+ });
102
+ describe("detectFormatFromContent (re-exported from common)", () => {
103
+ it("should detect JSON content", () => {
104
+ const content = '{"openapi": "3.1.0"}';
105
+ assert.strictEqual(detectFormatFromContent(content), "json");
106
+ });
107
+ it("should detect YAML content", () => {
108
+ const content = "openapi: 3.1.0\ninfo:\n title: Test";
109
+ assert.strictEqual(detectFormatFromContent(content), "yaml");
110
+ });
111
+ it("should throw error for invalid content", () => {
112
+ const content = "not valid json or yaml: : :";
113
+ assert.throws(() => detectFormatFromContent(content), /Failed to parse content/);
114
+ });
115
+ });
116
+ });
117
+ //# sourceMappingURL=merge-engine.spec.js.map