@zuplo/cli 6.63.4 → 6.63.6

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 (64) hide show
  1. package/dist/cmds/mtls-certificates/create.js +8 -8
  2. package/dist/cmds/mtls-certificates/create.js.map +1 -1
  3. package/dist/cmds/mtls-certificates/disable.d.ts +9 -0
  4. package/dist/cmds/mtls-certificates/disable.d.ts.map +1 -0
  5. package/dist/cmds/mtls-certificates/disable.js +57 -0
  6. package/dist/cmds/mtls-certificates/disable.js.map +1 -0
  7. package/dist/cmds/mtls-certificates/index.d.ts.map +1 -1
  8. package/dist/cmds/mtls-certificates/index.js +2 -0
  9. package/dist/cmds/mtls-certificates/index.js.map +1 -1
  10. package/dist/cmds/mtls-certificates/update.js +8 -8
  11. package/dist/cmds/mtls-certificates/update.js.map +1 -1
  12. package/dist/cmds/open-api/overlay.js +1 -1
  13. package/dist/cmds/open-api/overlay.js.map +1 -1
  14. package/dist/common/open-api/constants.d.ts +13 -0
  15. package/dist/common/open-api/constants.d.ts.map +1 -0
  16. package/dist/common/open-api/constants.js +16 -0
  17. package/dist/common/open-api/constants.js.map +1 -0
  18. package/dist/common/open-api/index.d.ts +3 -0
  19. package/dist/common/open-api/index.d.ts.map +1 -0
  20. package/dist/common/open-api/index.js +3 -0
  21. package/dist/common/open-api/index.js.map +1 -0
  22. package/dist/common/open-api/validation.d.ts +297 -0
  23. package/dist/common/open-api/validation.d.ts.map +1 -0
  24. package/dist/common/open-api/validation.js +88 -0
  25. package/dist/common/open-api/validation.js.map +1 -0
  26. package/dist/deploy/handler.js +3 -2
  27. package/dist/deploy/handler.js.map +1 -1
  28. package/dist/mtls-certificates/create/handler.js +1 -1
  29. package/dist/mtls-certificates/create/handler.js.map +1 -1
  30. package/dist/mtls-certificates/describe/handler.js +3 -3
  31. package/dist/mtls-certificates/describe/handler.js.map +1 -1
  32. package/dist/mtls-certificates/disable/handler.d.ts +3 -0
  33. package/dist/mtls-certificates/disable/handler.d.ts.map +1 -0
  34. package/dist/mtls-certificates/disable/handler.js +32 -0
  35. package/dist/mtls-certificates/disable/handler.js.map +1 -0
  36. package/dist/mtls-certificates/list/handler.js +3 -3
  37. package/dist/mtls-certificates/list/handler.js.map +1 -1
  38. package/dist/mtls-certificates/models.d.ts +8 -2
  39. package/dist/mtls-certificates/models.d.ts.map +1 -1
  40. package/dist/mtls-certificates/models.js.map +1 -1
  41. package/dist/mtls-certificates/update/handler.js +2 -2
  42. package/dist/mtls-certificates/update/handler.js.map +1 -1
  43. package/dist/open-api/merge/merge-engine.d.ts +2 -5
  44. package/dist/open-api/merge/merge-engine.d.ts.map +1 -1
  45. package/dist/open-api/merge/merge-engine.js +2 -1
  46. package/dist/open-api/merge/merge-engine.js.map +1 -1
  47. package/dist/open-api/merge/utils.d.ts +0 -1
  48. package/dist/open-api/merge/utils.d.ts.map +1 -1
  49. package/dist/open-api/merge/utils.js +2 -11
  50. package/dist/open-api/merge/utils.js.map +1 -1
  51. package/dist/open-api/overlay/handler.d.ts.map +1 -1
  52. package/dist/open-api/overlay/handler.js +33 -33
  53. package/dist/open-api/overlay/handler.js.map +1 -1
  54. package/dist/open-api/overlay/overlay-engine.d.ts +20 -30
  55. package/dist/open-api/overlay/overlay-engine.d.ts.map +1 -1
  56. package/dist/open-api/overlay/overlay-engine.js +75 -46
  57. package/dist/open-api/overlay/overlay-engine.js.map +1 -1
  58. package/dist/open-api/overlay/overlay-engine.spec.js +109 -31
  59. package/dist/open-api/overlay/overlay-engine.spec.js.map +1 -1
  60. package/dist/source/migrate/dev-portal/handler.d.ts.map +1 -1
  61. package/dist/source/migrate/dev-portal/handler.js +48 -1
  62. package/dist/source/migrate/dev-portal/handler.js.map +1 -1
  63. package/dist/tsconfig.tsbuildinfo +1 -1
  64. package/package.json +6 -5
@@ -3,41 +3,26 @@ import path from "node:path";
3
3
  import { detectFormatFromExtension, parseFile, serializeContent, } from "../../common/file-format.js";
4
4
  import { printDiagnosticsToConsole } from "../../common/output.js";
5
5
  import { logger } from "../../common/logger.js";
6
- import { applyOverlay as applyOverlayEngine, applyAction, deepClone, } from "./overlay-engine.js";
6
+ import { applyOverlay as applyOverlayEngine } from "./overlay-engine.js";
7
7
  function applyOverlayWithProgress(openapi, overlay) {
8
- printDiagnosticsToConsole(`\nApplying overlay: ${overlay.info?.title || "Unnamed"}`);
9
- printDiagnosticsToConsole(`Version: ${overlay.overlay || "unknown"}\n`);
10
- const result = deepClone(openapi);
11
- const stats = { applied: 0, skipped: 0, totalNodes: 0 };
12
- for (const [index, action] of overlay.actions.entries()) {
13
- const desc = action.description || `Action ${index + 1}`;
14
- process.stdout.write(` [${index + 1}/${overlay.actions.length}] ${desc}... `);
15
- try {
16
- const { applied, count } = applyAction(result, action);
17
- if (applied) {
18
- printDiagnosticsToConsole(`✓ (${count} node${count !== 1 ? "s" : ""})`);
19
- stats.applied++;
20
- stats.totalNodes += count;
21
- }
22
- else {
23
- printDiagnosticsToConsole("⊘ skipped");
24
- stats.skipped++;
25
- }
26
- }
27
- catch (error) {
28
- printDiagnosticsToConsole(`✗ failed`);
29
- logger.warn(error, `Failed to apply action: ${desc}`);
30
- stats.skipped++;
8
+ let validatedOverlay;
9
+ try {
10
+ validatedOverlay = applyOverlayEngine(openapi, overlay);
11
+ }
12
+ catch (error) {
13
+ if (error instanceof Error) {
14
+ printDiagnosticsToConsole(`\n${error.message}`);
31
15
  }
16
+ throw error;
32
17
  }
18
+ const { result, stats } = validatedOverlay;
33
19
  printDiagnosticsToConsole(`\n=== Summary ===`);
34
- printDiagnosticsToConsole(`Applied: ${stats.applied}/${overlay.actions.length} actions`);
35
- printDiagnosticsToConsole(`Modified: ${stats.totalNodes} nodes`);
20
+ printDiagnosticsToConsole(`Applied: ${stats.applied} action${stats.applied !== 1 ? "s" : ""}`);
21
+ printDiagnosticsToConsole(`Modified: ${stats.totalNodes} node${stats.totalNodes !== 1 ? "s" : ""}`);
36
22
  if (stats.skipped > 0) {
37
- printDiagnosticsToConsole(`Skipped: ${stats.skipped} actions`);
23
+ printDiagnosticsToConsole(`Skipped: ${stats.skipped} action${stats.skipped !== 1 ? "s" : ""}`);
38
24
  }
39
- const { result: finalResult } = applyOverlayEngine(openapi, overlay);
40
- return finalResult;
25
+ return result;
41
26
  }
42
27
  export async function applyOverlay(args) {
43
28
  const openapiPath = path.resolve(args.input);
@@ -57,10 +42,25 @@ export async function applyOverlay(args) {
57
42
  try {
58
43
  const openapiContent = fs.readFileSync(openapiPath, "utf-8");
59
44
  const overlayContent = fs.readFileSync(overlayPath, "utf-8");
60
- const { document: openapi } = parseFile(openapiContent, openapiPath);
61
- const { document: overlay } = parseFile(overlayContent, overlayPath);
62
- if (!overlay.overlay || !overlay.actions) {
63
- throw new Error("Invalid overlay format. Must include 'overlay' version and 'actions' array.");
45
+ let openapi;
46
+ let overlay;
47
+ try {
48
+ const parsed = parseFile(openapiContent, openapiPath);
49
+ openapi = parsed.document;
50
+ }
51
+ catch (error) {
52
+ throw new Error(`Failed to parse OpenAPI file '${openapiPath}'`, {
53
+ cause: error,
54
+ });
55
+ }
56
+ try {
57
+ const parsed = parseFile(overlayContent, overlayPath);
58
+ overlay = parsed.document;
59
+ }
60
+ catch (error) {
61
+ throw new Error(`Failed to parse overlay file '${overlayPath}'`, {
62
+ cause: error,
63
+ });
64
64
  }
65
65
  const result = applyOverlayWithProgress(openapi, overlay);
66
66
  let outputFormat;
@@ -1 +1 @@
1
- {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../src/open-api/overlay/handler.ts"],"names":[],"mappings":"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","sourcesContent":["/** biome-ignore-all lint/suspicious/noConsole: CLI output file */\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport {\n detectFormatFromExtension,\n parseFile,\n serializeContent,\n type FileFormat,\n} from \"../../common/file-format.js\";\nimport { printDiagnosticsToConsole } from \"../../common/output.js\";\nimport { logger } from \"../../common/logger.js\";\nimport {\n applyOverlay as applyOverlayEngine,\n applyAction,\n deepClone,\n type OverlayDocument,\n} from \"./overlay-engine.js\";\n\nexport interface Arguments {\n overlay: string;\n input: string;\n output: string;\n format?: \"json\" | \"yaml\";\n json?: boolean;\n yaml?: boolean;\n}\n\n/**\n * Apply overlay to OpenAPI document with console output\n */\n// biome-ignore lint/suspicious/noExplicitAny: Working with dynamic JSON structures\nfunction applyOverlayWithProgress(openapi: any, overlay: OverlayDocument): any {\n printDiagnosticsToConsole(\n `\\nApplying overlay: ${overlay.info?.title || \"Unnamed\"}`\n );\n printDiagnosticsToConsole(`Version: ${overlay.overlay || \"unknown\"}\\n`);\n\n // Clone the document for modification\n const result = deepClone(openapi);\n const stats = { applied: 0, skipped: 0, totalNodes: 0 };\n\n // Apply actions with progress reporting\n for (const [index, action] of overlay.actions.entries()) {\n const desc = action.description || `Action ${index + 1}`;\n process.stdout.write(\n ` [${index + 1}/${overlay.actions.length}] ${desc}... `\n );\n\n try {\n const { applied, count } = applyAction(result, action);\n\n if (applied) {\n printDiagnosticsToConsole(`✓ (${count} node${count !== 1 ? \"s\" : \"\"})`);\n stats.applied++;\n stats.totalNodes += count;\n } else {\n printDiagnosticsToConsole(\"⊘ skipped\");\n stats.skipped++;\n }\n } catch (error) {\n printDiagnosticsToConsole(`✗ failed`);\n logger.warn(error, `Failed to apply action: ${desc}`);\n stats.skipped++;\n }\n }\n\n printDiagnosticsToConsole(`\\n=== Summary ===`);\n printDiagnosticsToConsole(\n `Applied: ${stats.applied}/${overlay.actions.length} actions`\n );\n printDiagnosticsToConsole(`Modified: ${stats.totalNodes} nodes`);\n if (stats.skipped > 0) {\n printDiagnosticsToConsole(`Skipped: ${stats.skipped} actions`);\n }\n\n // Use the engine's reordering functions\n const { result: finalResult } = applyOverlayEngine(openapi, overlay);\n\n return finalResult;\n}\n\n/**\n * Main handler function\n */\nexport async function applyOverlay(args: Arguments): Promise<void> {\n const openapiPath = path.resolve(args.input);\n const overlayPath = path.resolve(args.overlay);\n const outputPath = path.resolve(args.output);\n\n // Validate input files exist\n if (!fs.existsSync(openapiPath)) {\n throw new Error(`OpenAPI file not found: ${openapiPath}`);\n }\n\n if (!fs.existsSync(overlayPath)) {\n throw new Error(`Overlay file not found: ${overlayPath}`);\n }\n\n printDiagnosticsToConsole(\"OpenAPI Overlay Applicator\");\n printDiagnosticsToConsole(\"===========================\");\n printDiagnosticsToConsole(`OpenAPI file: ${openapiPath}`);\n printDiagnosticsToConsole(`Overlay file: ${overlayPath}`);\n printDiagnosticsToConsole(`Output file: ${outputPath}`);\n\n try {\n // Load files with flexible parsing\n const openapiContent = fs.readFileSync(openapiPath, \"utf-8\");\n const overlayContent = fs.readFileSync(overlayPath, \"utf-8\");\n\n // Parse with automatic format detection\n const { document: openapi } = parseFile(openapiContent, openapiPath);\n const { document: overlay } = parseFile(overlayContent, overlayPath);\n\n // Validate overlay format\n if (!overlay.overlay || !overlay.actions) {\n throw new Error(\n \"Invalid overlay format. Must include 'overlay' version and 'actions' array.\"\n );\n }\n\n // Apply overlay with progress output\n const result = applyOverlayWithProgress(\n openapi,\n overlay as OverlayDocument\n );\n\n // Determine output format\n let outputFormat: FileFormat;\n if (args.format) {\n outputFormat = args.format;\n } else if (args.json) {\n outputFormat = \"json\";\n } else if (args.yaml) {\n outputFormat = \"yaml\";\n } else {\n // Try to detect from output file extension\n const formatFromExt = detectFormatFromExtension(outputPath);\n outputFormat = formatFromExt || \"json\"; // Default to JSON if no extension\n }\n\n // Serialize output\n const outputContent = serializeContent(result, outputFormat);\n\n // Create output directory if it doesn't exist\n const outputDir = path.dirname(outputPath);\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true });\n }\n\n fs.writeFileSync(outputPath, outputContent, \"utf-8\");\n\n printDiagnosticsToConsole(\n `\\n✓ Output written to: ${outputPath} (${outputFormat.toUpperCase()})`\n );\n } catch (error) {\n logger.error(error, \"Error applying overlay\");\n printDiagnosticsToConsole(`Error: ${(error as Error).message}`);\n throw error;\n }\n}\n"]}
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../src/open-api/overlay/handler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EACL,yBAAyB,EACzB,SAAS,EACT,gBAAgB,GAEjB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,YAAY,IAAI,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAezE,SAAS,wBAAwB,CAC/B,OAAgB,EAChB,OAAgB;IAGhB,IAAI,gBAAuD,CAAC;IAE5D,IAAI,CAAC;QACH,gBAAgB,GAAG,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC1D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAEf,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,yBAAyB,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAClD,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,gBAAgB,CAAC;IAG3C,yBAAyB,CAAC,mBAAmB,CAAC,CAAC;IAC/C,yBAAyB,CACvB,YAAY,KAAK,CAAC,OAAO,UAAU,KAAK,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACpE,CAAC;IACF,yBAAyB,CACvB,aAAa,KAAK,CAAC,UAAU,QAAQ,KAAK,CAAC,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACzE,CAAC;IACF,IAAI,KAAK,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;QACtB,yBAAyB,CACvB,YAAY,KAAK,CAAC,OAAO,UAAU,KAAK,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACpE,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAKD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAe;IAChD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAG7C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,2BAA2B,WAAW,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,2BAA2B,WAAW,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,yBAAyB,CAAC,4BAA4B,CAAC,CAAC;IACxD,yBAAyB,CAAC,6BAA6B,CAAC,CAAC;IACzD,yBAAyB,CAAC,iBAAiB,WAAW,EAAE,CAAC,CAAC;IAC1D,yBAAyB,CAAC,iBAAiB,WAAW,EAAE,CAAC,CAAC;IAC1D,yBAAyB,CAAC,iBAAiB,UAAU,EAAE,CAAC,CAAC;IAEzD,IAAI,CAAC;QAEH,MAAM,cAAc,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAC7D,MAAM,cAAc,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAG7D,IAAI,OAAgB,CAAC;QACrB,IAAI,OAAgB,CAAC;QAErB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,SAAS,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;YACtD,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC5B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,iCAAiC,WAAW,GAAG,EAAE;gBAC/D,KAAK,EAAE,KAAK;aACb,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,SAAS,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;YACtD,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC5B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,iCAAiC,WAAW,GAAG,EAAE;gBAC/D,KAAK,EAAE,KAAK;aACb,CAAC,CAAC;QACL,CAAC;QAID,MAAM,MAAM,GAAG,wBAAwB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAG1D,IAAI,YAAwB,CAAC;QAC7B,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC;QAC7B,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACrB,YAAY,GAAG,MAAM,CAAC;QACxB,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACrB,YAAY,GAAG,MAAM,CAAC;QACxB,CAAC;aAAM,CAAC;YAEN,MAAM,aAAa,GAAG,yBAAyB,CAAC,UAAU,CAAC,CAAC;YAC5D,YAAY,GAAG,aAAa,IAAI,MAAM,CAAC;QACzC,CAAC;QAGD,MAAM,aAAa,GAAG,gBAAgB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAG7D,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC3C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,CAAC;QAED,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;QAErD,yBAAyB,CACvB,0BAA0B,UAAU,KAAK,YAAY,CAAC,WAAW,EAAE,GAAG,CACvE,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,wBAAwB,CAAC,CAAC;QAC9C,yBAAyB,CAAC,UAAW,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;QAChE,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC","sourcesContent":["/** biome-ignore-all lint/suspicious/noConsole: CLI output file */\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport {\n detectFormatFromExtension,\n parseFile,\n serializeContent,\n type FileFormat,\n} from \"../../common/file-format.js\";\nimport { printDiagnosticsToConsole } from \"../../common/output.js\";\nimport { logger } from \"../../common/logger.js\";\nimport { applyOverlay as applyOverlayEngine } from \"./overlay-engine.js\";\n\nexport interface Arguments {\n overlay: string;\n input: string;\n output: string;\n format?: \"json\" | \"yaml\";\n json?: boolean;\n yaml?: boolean;\n}\n\n/**\n * Apply overlay to OpenAPI document with console output\n * Validates inputs and provides progress feedback\n */\nfunction applyOverlayWithProgress(\n openapi: unknown,\n overlay: unknown\n): Record<string, unknown> {\n // Apply overlay (includes validation)\n let validatedOverlay: ReturnType<typeof applyOverlayEngine>;\n\n try {\n validatedOverlay = applyOverlayEngine(openapi, overlay);\n } catch (error) {\n // Re-throw with better context for CLI users\n if (error instanceof Error) {\n printDiagnosticsToConsole(`\\n${error.message}`);\n }\n throw error;\n }\n\n const { result, stats } = validatedOverlay;\n\n // Show summary\n printDiagnosticsToConsole(`\\n=== Summary ===`);\n printDiagnosticsToConsole(\n `Applied: ${stats.applied} action${stats.applied !== 1 ? \"s\" : \"\"}`\n );\n printDiagnosticsToConsole(\n `Modified: ${stats.totalNodes} node${stats.totalNodes !== 1 ? \"s\" : \"\"}`\n );\n if (stats.skipped > 0) {\n printDiagnosticsToConsole(\n `Skipped: ${stats.skipped} action${stats.skipped !== 1 ? \"s\" : \"\"}`\n );\n }\n\n return result;\n}\n\n/**\n * Main handler function\n */\nexport async function applyOverlay(args: Arguments): Promise<void> {\n const openapiPath = path.resolve(args.input);\n const overlayPath = path.resolve(args.overlay);\n const outputPath = path.resolve(args.output);\n\n // Validate input files exist\n if (!fs.existsSync(openapiPath)) {\n throw new Error(`OpenAPI file not found: ${openapiPath}`);\n }\n\n if (!fs.existsSync(overlayPath)) {\n throw new Error(`Overlay file not found: ${overlayPath}`);\n }\n\n printDiagnosticsToConsole(\"OpenAPI Overlay Applicator\");\n printDiagnosticsToConsole(\"===========================\");\n printDiagnosticsToConsole(`OpenAPI file: ${openapiPath}`);\n printDiagnosticsToConsole(`Overlay file: ${overlayPath}`);\n printDiagnosticsToConsole(`Output file: ${outputPath}`);\n\n try {\n // Load files with flexible parsing\n const openapiContent = fs.readFileSync(openapiPath, \"utf-8\");\n const overlayContent = fs.readFileSync(overlayPath, \"utf-8\");\n\n // Parse with automatic format detection\n let openapi: unknown;\n let overlay: unknown;\n\n try {\n const parsed = parseFile(openapiContent, openapiPath);\n openapi = parsed.document;\n } catch (error) {\n throw new Error(`Failed to parse OpenAPI file '${openapiPath}'`, {\n cause: error,\n });\n }\n\n try {\n const parsed = parseFile(overlayContent, overlayPath);\n overlay = parsed.document;\n } catch (error) {\n throw new Error(`Failed to parse overlay file '${overlayPath}'`, {\n cause: error,\n });\n }\n\n // Apply overlay with progress output\n // Validation happens inside applyOverlayWithProgress\n const result = applyOverlayWithProgress(openapi, overlay);\n\n // Determine output format\n let outputFormat: FileFormat;\n if (args.format) {\n outputFormat = args.format;\n } else if (args.json) {\n outputFormat = \"json\";\n } else if (args.yaml) {\n outputFormat = \"yaml\";\n } else {\n // Try to detect from output file extension\n const formatFromExt = detectFormatFromExtension(outputPath);\n outputFormat = formatFromExt || \"json\"; // Default to JSON if no extension\n }\n\n // Serialize output\n const outputContent = serializeContent(result, outputFormat);\n\n // Create output directory if it doesn't exist\n const outputDir = path.dirname(outputPath);\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true });\n }\n\n fs.writeFileSync(outputPath, outputContent, \"utf-8\");\n\n printDiagnosticsToConsole(\n `\\n✓ Output written to: ${outputPath} (${outputFormat.toUpperCase()})`\n );\n } catch (error) {\n logger.error(error, \"Error applying overlay\");\n printDiagnosticsToConsole(`Error: ${(error as Error).message}`);\n throw error;\n }\n}\n"]}
@@ -1,22 +1,8 @@
1
- export interface OverlayDocument {
2
- overlay: string;
3
- info: {
4
- title: string;
5
- version: string;
6
- };
7
- actions: OverlayAction[];
8
- extends?: string;
9
- }
10
- export interface OverlayAction {
11
- target: string;
12
- description?: string;
13
- update?: any;
14
- remove?: boolean | RemoveCondition;
15
- }
16
- export interface RemoveCondition {
17
- empty?: boolean;
18
- missing?: string;
19
- }
1
+ import {
2
+ type OverlayAction,
3
+ type OverlayDocument,
4
+ type RemoveCondition,
5
+ } from "../../common/open-api/index.js";
20
6
  export interface ApplyResult {
21
7
  applied: boolean;
22
8
  count: number;
@@ -27,29 +13,33 @@ export interface OverlayStats {
27
13
  totalNodes: number;
28
14
  }
29
15
  export declare function deepClone<T>(obj: T): T;
30
- export declare function deepMerge(target: any, source: any): any;
16
+ export declare function deepMerge(target: unknown, source: unknown): unknown;
31
17
  export declare function checkCondition(
32
- value: any,
18
+ value: unknown,
33
19
  condition: RemoveCondition
34
20
  ): boolean;
35
21
  export declare function setValueAtPath(
36
- doc: any,
22
+ doc: unknown,
37
23
  target: string,
38
- value: any
24
+ value: unknown
39
25
  ): void;
40
26
  export declare function applyAction(
41
- doc: any,
27
+ doc: unknown,
42
28
  action: OverlayAction
43
29
  ): ApplyResult;
44
30
  export declare function extractPathOrder(overlay: OverlayDocument): string[];
45
- export declare function reorderOperationProperties(operation: any): any;
46
- export declare function reorderPaths(doc: any, pathOrder: string[]): any;
47
- export declare function reorderDocumentProperties(doc: any): any;
31
+ export declare function reorderOperationProperties(
32
+ operation: unknown
33
+ ): Record<string, unknown>;
34
+ export declare function reorderPaths<T>(doc: T, pathOrder: string[]): T;
35
+ export declare function reorderDocumentProperties(
36
+ doc: unknown
37
+ ): Record<string, unknown>;
48
38
  export declare function applyOverlay(
49
- openapi: any,
50
- overlay: OverlayDocument
39
+ openapi: unknown,
40
+ overlay: unknown
51
41
  ): {
52
- result: any;
42
+ result: Record<string, unknown>;
53
43
  stats: OverlayStats;
54
44
  };
55
45
  //# sourceMappingURL=overlay-engine.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"overlay-engine.d.ts","sourceRoot":"","sources":["../../../src/open-api/overlay/overlay-engine.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,GAAG,eAAe,CAAC;CACpC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAMD,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAKtC;AAMD,wBAAgB,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,GAAG,CAgDvD;AAKD,wBAAgB,cAAc,CAC5B,KAAK,EAAE,GAAG,EACV,SAAS,EAAE,eAAe,GACzB,OAAO,CA2BT;AAyCD,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI,CAuCzE;AAKD,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,aAAa,GAAG,WAAW,CA+ExE;AAMD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,EAAE,CAiBnE;AAKD,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,GAAG,GAAG,GAAG,CAmB9D;AAKD,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,GAAG,CAiD/D;AAMD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,CAmBvD;AAQD,wBAAgB,YAAY,CAC1B,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE,eAAe,GACvB;IAAE,MAAM,EAAE,GAAG,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,CA8BtC"}
1
+ {"version":3,"file":"overlay-engine.d.ts","sourceRoot":"","sources":["../../../src/open-api/overlay/overlay-engine.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,eAAe,EACrB,MAAM,gCAAgC,CAAC;AAExC,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAmBD,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAKtC;AAQD,wBAAgB,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CA+DnE;AAKD,wBAAgB,cAAc,CAC5B,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,eAAe,GACzB,OAAO,CAgCT;AAyCD,wBAAgB,cAAc,CAC5B,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,GACb,IAAI,CAyCN;AAKD,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,GAAG,WAAW,CA2F5E;AAMD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,EAAE,CAiBnE;AAMD,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,OAAO,GACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoBzB;AAMD,wBAAgB,YAAY,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,CAkD9D;AAOD,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,OAAO,GACX,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoBzB;AAmBD,wBAAgB,YAAY,CAC1B,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,OAAO,GACf;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,CA6B1D"}
@@ -1,4 +1,11 @@
1
1
  import { JSONPath } from "jsonpath-plus";
2
+ import { HTTP_METHODS, validateOpenApiDocument, validateOverlayDocument, } from "../../common/open-api/index.js";
3
+ function toRecord(value, context) {
4
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
5
+ throw new Error(`Expected object at ${context}, but got ${Array.isArray(value) ? "array" : typeof value}`);
6
+ }
7
+ return value;
8
+ }
2
9
  export function deepClone(obj) {
3
10
  if (typeof structuredClone === "function") {
4
11
  return structuredClone(obj);
@@ -17,11 +24,22 @@ export function deepMerge(target, source) {
17
24
  return deepClone(source);
18
25
  }
19
26
  if (source.length > 0 &&
20
- source[0].name !== undefined &&
21
- source[0].in !== undefined) {
27
+ typeof source[0] === "object" &&
28
+ source[0] !== null &&
29
+ "name" in source[0] &&
30
+ "in" in source[0]) {
22
31
  const merged = [...target];
23
32
  for (const sourceParam of source) {
24
- const existingIndex = merged.findIndex((p) => p.name === sourceParam.name && p.in === sourceParam.in);
33
+ if (typeof sourceParam !== "object" || sourceParam === null)
34
+ continue;
35
+ const existingIndex = merged.findIndex((p) => typeof p === "object" &&
36
+ p !== null &&
37
+ "name" in p &&
38
+ "in" in p &&
39
+ "name" in sourceParam &&
40
+ "in" in sourceParam &&
41
+ p.name === sourceParam.name &&
42
+ p.in === sourceParam.in);
25
43
  if (existingIndex >= 0) {
26
44
  merged[existingIndex] = deepMerge(merged[existingIndex], sourceParam);
27
45
  }
@@ -33,8 +51,10 @@ export function deepMerge(target, source) {
33
51
  }
34
52
  return [...target, ...deepClone(source)];
35
53
  }
36
- const result = { ...target };
37
- for (const [key, value] of Object.entries(source)) {
54
+ const targetObj = target;
55
+ const sourceObj = source;
56
+ const result = { ...targetObj };
57
+ for (const [key, value] of Object.entries(sourceObj)) {
38
58
  if (key in result) {
39
59
  result[key] = deepMerge(result[key], value);
40
60
  }
@@ -58,7 +78,10 @@ export function checkCondition(value, condition) {
58
78
  const parts = condition.missing.split(".");
59
79
  let current = value;
60
80
  for (const part of parts) {
61
- if (current && typeof current === "object" && part in current) {
81
+ if (current &&
82
+ typeof current === "object" &&
83
+ !Array.isArray(current) &&
84
+ part in current) {
62
85
  current = current[part];
63
86
  }
64
87
  else {
@@ -83,7 +106,7 @@ function extractBracketKey(bracketNotation) {
83
106
  export function setValueAtPath(doc, target, value) {
84
107
  const targetPath = target.replace(/^\$\./, "");
85
108
  const parts = targetPath.match(JSONPATH_SEGMENT_PATTERN) || [];
86
- let current = doc;
109
+ let current = toRecord(doc, "setValueAtPath document");
87
110
  for (let i = 0; i < parts.length - 1; i++) {
88
111
  const part = parts[i];
89
112
  if (part.startsWith("[")) {
@@ -91,13 +114,15 @@ export function setValueAtPath(doc, target, value) {
91
114
  if (!(key in current)) {
92
115
  current[key] = {};
93
116
  }
94
- current = current[key];
117
+ const next = current[key];
118
+ current = toRecord(next, `setValueAtPath at ${target}[${i}]`);
95
119
  }
96
120
  else {
97
121
  if (!(part in current)) {
98
122
  current[part] = {};
99
123
  }
100
- current = current[part];
124
+ const next = current[part];
125
+ current = toRecord(next, `setValueAtPath at ${target}.${part}`);
101
126
  }
102
127
  }
103
128
  const lastPart = parts[parts.length - 1];
@@ -110,9 +135,10 @@ export function setValueAtPath(doc, target, value) {
110
135
  }
111
136
  }
112
137
  export function applyAction(doc, action) {
138
+ const docRecord = toRecord(doc, "applyAction document");
113
139
  if (action.update) {
114
140
  if (action.target === "$") {
115
- Object.assign(doc, deepMerge(doc, action.update));
141
+ Object.assign(docRecord, deepMerge(doc, action.update));
116
142
  return { applied: true, count: 1 };
117
143
  }
118
144
  try {
@@ -125,8 +151,12 @@ export function applyAction(doc, action) {
125
151
  let count = 0;
126
152
  for (const result of results) {
127
153
  const { parent, parentProperty } = result;
128
- if (parent && parentProperty !== undefined) {
129
- parent[parentProperty] = deepMerge(parent[parentProperty], action.update);
154
+ if (parent &&
155
+ typeof parent === "object" &&
156
+ !Array.isArray(parent) &&
157
+ parentProperty !== undefined) {
158
+ const parentRecord = parent;
159
+ parentRecord[parentProperty] = deepMerge(parentRecord[parentProperty], action.update);
130
160
  count++;
131
161
  }
132
162
  }
@@ -135,8 +165,8 @@ export function applyAction(doc, action) {
135
165
  setValueAtPath(doc, action.target, action.update);
136
166
  return { applied: true, count: 1 };
137
167
  }
138
- catch (error) {
139
- throw new Error(`Failed to apply update: ${error.message}`);
168
+ catch (cause) {
169
+ throw new Error("Failed to apply update", { cause });
140
170
  }
141
171
  }
142
172
  else if (action.remove) {
@@ -160,7 +190,7 @@ export function applyAction(doc, action) {
160
190
  if (Array.isArray(parent)) {
161
191
  parent.splice(parentProperty, 1);
162
192
  }
163
- else {
193
+ else if (typeof parent === "object" && parent !== null) {
164
194
  delete parent[parentProperty];
165
195
  }
166
196
  count++;
@@ -168,8 +198,8 @@ export function applyAction(doc, action) {
168
198
  }
169
199
  return { applied: true, count };
170
200
  }
171
- catch (error) {
172
- throw new Error(`Failed to remove: ${error.message}`);
201
+ catch (cause) {
202
+ throw new Error("Failed to remove", { cause });
173
203
  }
174
204
  }
175
205
  return { applied: false, count: 0 };
@@ -190,14 +220,15 @@ export function extractPathOrder(overlay) {
190
220
  return pathOrder;
191
221
  }
192
222
  export function reorderOperationProperties(operation) {
223
+ const operationObj = toRecord(operation, "reorderOperationProperties");
193
224
  const priorityKeys = ["summary", "description", "operationId"];
194
225
  const reordered = {};
195
226
  for (const key of priorityKeys) {
196
- if (key in operation) {
197
- reordered[key] = operation[key];
227
+ if (key in operationObj) {
228
+ reordered[key] = operationObj[key];
198
229
  }
199
230
  }
200
- for (const [key, value] of Object.entries(operation)) {
231
+ for (const [key, value] of Object.entries(operationObj)) {
201
232
  if (!priorityKeys.includes(key)) {
202
233
  reordered[key] = value;
203
234
  }
@@ -205,10 +236,14 @@ export function reorderOperationProperties(operation) {
205
236
  return reordered;
206
237
  }
207
238
  export function reorderPaths(doc, pathOrder) {
208
- if (!doc.paths || pathOrder.length === 0) {
239
+ if (typeof doc !== "object" ||
240
+ doc === null ||
241
+ !("paths" in doc) ||
242
+ pathOrder.length === 0) {
209
243
  return doc;
210
244
  }
211
- const originalPaths = doc.paths;
245
+ const docRecord = doc;
246
+ const originalPaths = toRecord(docRecord.paths, "paths");
212
247
  const reorderedPaths = {};
213
248
  for (const targetPath of pathOrder) {
214
249
  if (targetPath in originalPaths) {
@@ -221,35 +256,30 @@ export function reorderPaths(doc, pathOrder) {
221
256
  }
222
257
  }
223
258
  for (const pathItem of Object.values(reorderedPaths)) {
224
- const httpMethods = [
225
- "get",
226
- "post",
227
- "put",
228
- "patch",
229
- "delete",
230
- "options",
231
- "head",
232
- "trace",
233
- ];
234
- for (const method of httpMethods) {
235
- if (pathItem[method] &&
236
- typeof pathItem[method] === "object") {
237
- pathItem[method] = reorderOperationProperties(pathItem[method]);
259
+ if (typeof pathItem !== "object" || pathItem === null)
260
+ continue;
261
+ const pathItemRecord = pathItem;
262
+ for (const method of HTTP_METHODS) {
263
+ if (method in pathItemRecord &&
264
+ typeof pathItemRecord[method] === "object" &&
265
+ pathItemRecord[method] !== null) {
266
+ pathItemRecord[method] = reorderOperationProperties(pathItemRecord[method]);
238
267
  }
239
268
  }
240
269
  }
241
- doc.paths = reorderedPaths;
270
+ docRecord.paths = reorderedPaths;
242
271
  return doc;
243
272
  }
244
273
  export function reorderDocumentProperties(doc) {
274
+ const docObj = toRecord(doc, "reorderDocumentProperties");
245
275
  const priorityKeys = ["openapi", "info", "tags"];
246
276
  const reordered = {};
247
277
  for (const key of priorityKeys) {
248
- if (key in doc) {
249
- reordered[key] = doc[key];
278
+ if (key in docObj) {
279
+ reordered[key] = docObj[key];
250
280
  }
251
281
  }
252
- for (const [key, value] of Object.entries(doc)) {
282
+ for (const [key, value] of Object.entries(docObj)) {
253
283
  if (!priorityKeys.includes(key)) {
254
284
  reordered[key] = value;
255
285
  }
@@ -257,12 +287,11 @@ export function reorderDocumentProperties(doc) {
257
287
  return reordered;
258
288
  }
259
289
  export function applyOverlay(openapi, overlay) {
260
- if (!overlay.overlay || !overlay.actions) {
261
- throw new Error("Invalid overlay format. Must include 'overlay' version and 'actions' array.");
262
- }
263
- const result = deepClone(openapi);
290
+ const validatedOverlay = validateOverlayDocument(overlay);
291
+ const validatedOpenApi = validateOpenApiDocument(openapi);
292
+ const result = deepClone(validatedOpenApi);
264
293
  const stats = { applied: 0, skipped: 0, totalNodes: 0 };
265
- for (const action of overlay.actions) {
294
+ for (const action of validatedOverlay.actions) {
266
295
  const { applied, count } = applyAction(result, action);
267
296
  if (applied) {
268
297
  stats.applied++;
@@ -272,7 +301,7 @@ export function applyOverlay(openapi, overlay) {
272
301
  stats.skipped++;
273
302
  }
274
303
  }
275
- const pathOrder = extractPathOrder(overlay);
304
+ const pathOrder = extractPathOrder(validatedOverlay);
276
305
  reorderPaths(result, pathOrder);
277
306
  const reordered = reorderDocumentProperties(result);
278
307
  return { result: reordered, stats };
@@ -1 +1 @@
1
- {"version":3,"file":"overlay-engine.js","sourceRoot":"","sources":["../../../src/open-api/overlay/overlay-engine.ts"],"names":[],"mappings":"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","sourcesContent":["/** biome-ignore-all lint/suspicious/noExplicitAny: Complex overlay algorithm working with dynamic JSON structures */\nimport { JSONPath } from \"jsonpath-plus\";\n\nexport interface OverlayDocument {\n overlay: string;\n info: {\n title: string;\n version: string;\n };\n actions: OverlayAction[];\n extends?: string;\n}\n\nexport interface OverlayAction {\n target: string;\n description?: string;\n update?: any;\n remove?: boolean | RemoveCondition;\n}\n\nexport interface RemoveCondition {\n empty?: boolean;\n missing?: string;\n}\n\nexport interface ApplyResult {\n applied: boolean;\n count: number;\n}\n\nexport interface OverlayStats {\n applied: number;\n skipped: number;\n totalNodes: number;\n}\n\n/**\n * Deep clone an object using structuredClone for better performance and correctness\n * Falls back to JSON parse/stringify for environments without structuredClone\n */\nexport function deepClone<T>(obj: T): T {\n if (typeof structuredClone === \"function\") {\n return structuredClone(obj);\n }\n return JSON.parse(JSON.stringify(obj));\n}\n\n/**\n * Deep merge two objects\n * Special handling for OpenAPI parameters (merge by name+in)\n */\nexport function deepMerge(target: any, source: any): any {\n if (!source || typeof source !== \"object\") {\n return source;\n }\n\n if (!target || typeof target !== \"object\") {\n return deepClone(source);\n }\n\n if (Array.isArray(source)) {\n if (!Array.isArray(target)) {\n return deepClone(source);\n }\n\n // Special case for OpenAPI parameters - merge by name+in\n if (\n source.length > 0 &&\n source[0].name !== undefined &&\n source[0].in !== undefined\n ) {\n const merged = [...target];\n for (const sourceParam of source) {\n const existingIndex = merged.findIndex(\n (p) => p.name === sourceParam.name && p.in === sourceParam.in\n );\n if (existingIndex >= 0) {\n merged[existingIndex] = deepMerge(merged[existingIndex], sourceParam);\n } else {\n merged.push(deepClone(sourceParam));\n }\n }\n return merged;\n }\n\n // For other arrays, concatenate\n return [...target, ...deepClone(source)];\n }\n\n const result = { ...target };\n for (const [key, value] of Object.entries(source)) {\n if (key in result) {\n result[key] = deepMerge(result[key], value);\n } else {\n result[key] = deepClone(value);\n }\n }\n\n return result;\n}\n\n/**\n * Check if a condition is met for conditional removal\n */\nexport function checkCondition(\n value: any,\n condition: RemoveCondition\n): boolean {\n // Support \"empty\" condition to check if object has no keys\n if (condition.empty) {\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n return Object.keys(value).length === 0;\n }\n if (Array.isArray(value)) {\n return value.length === 0;\n }\n return false;\n }\n\n // Support \"missing\" condition to check if a property doesn't exist\n if (condition.missing) {\n const parts = condition.missing.split(\".\");\n let current = value;\n for (const part of parts) {\n if (current && typeof current === \"object\" && part in current) {\n current = current[part];\n } else {\n return true; // Property is missing\n }\n }\n return false; // Property exists\n }\n\n return false;\n}\n\n/**\n * Regular expression for parsing JSONPath segments\n * Matches: word, or [quoted], or ['quoted'], or [\"quoted\"], or numeric indices [0]\n */\nconst JSONPATH_SEGMENT_PATTERN = /[^.[]+|\\[[^\\]]+\\]/g;\n\n/**\n * Extract key from bracket notation, handling various formats:\n * - ['key'] -> key\n * - [\"key\"] -> key\n * - [key] -> key\n * - [0] -> 0 (as string for consistency)\n */\nfunction extractBracketKey(bracketNotation: string): string {\n const inner = bracketNotation.slice(1, -1); // Remove [ and ]\n\n // Handle single-quoted: ['key'] -> key\n if (inner.startsWith(\"'\") && inner.endsWith(\"'\")) {\n return inner.slice(1, -1);\n }\n\n // Handle double-quoted: [\"key\"] -> key\n if (inner.startsWith('\"') && inner.endsWith('\"')) {\n return inner.slice(1, -1);\n }\n\n // Handle unquoted: [key] or [0] -> key or 0\n return inner;\n}\n\n/**\n * Set a value at a JSONPath, creating parent paths as needed\n * Handles patterns like:\n * $.paths['/route'].method\n * $.components.parameters['name']\n * $.paths[\"/users\"].get\n * $.paths[users].get\n * $.items[0].value\n */\nexport function setValueAtPath(doc: any, target: string, value: any): void {\n // Parse the path manually to handle both bracket and dot notation\n // Example: $.paths['/route'].post -> [\"paths\", \"['/route']\", \"post\"]\n\n // Remove leading $.\n const targetPath = target.replace(/^\\$\\./, \"\");\n\n // Split by dots, but keep bracket notation together\n const parts = targetPath.match(JSONPATH_SEGMENT_PATTERN) || [];\n\n let current = doc;\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n\n // Handle bracket notation\n if (part.startsWith(\"[\")) {\n const key = extractBracketKey(part);\n if (!(key in current)) {\n current[key] = {};\n }\n current = current[key];\n } else {\n // Handle regular property\n if (!(part in current)) {\n current[part] = {};\n }\n current = current[part];\n }\n }\n\n // Set the final value\n const lastPart = parts[parts.length - 1];\n if (lastPart.startsWith(\"[\")) {\n const key = extractBracketKey(lastPart);\n current[key] = value;\n } else {\n current[lastPart] = value;\n }\n}\n\n/**\n * Apply a single overlay action to the document\n */\nexport function applyAction(doc: any, action: OverlayAction): ApplyResult {\n if (action.update) {\n // Handle root level updates specially\n if (action.target === \"$\") {\n Object.assign(doc, deepMerge(doc, action.update));\n return { applied: true, count: 1 };\n }\n\n try {\n // Try to find existing nodes\n const results = JSONPath({\n path: action.target,\n json: doc,\n resultType: \"all\",\n });\n\n if (results.length > 0) {\n // Path exists, merge the update\n let count = 0;\n for (const result of results) {\n const { parent, parentProperty } = result;\n if (parent && parentProperty !== undefined) {\n parent[parentProperty] = deepMerge(\n parent[parentProperty],\n action.update\n );\n count++;\n }\n }\n return { applied: true, count };\n }\n\n // Path doesn't exist, create it manually\n setValueAtPath(doc, action.target, action.update);\n return { applied: true, count: 1 };\n } catch (error) {\n throw new Error(`Failed to apply update: ${(error as Error).message}`);\n }\n } else if (action.remove) {\n try {\n const results = JSONPath({\n path: action.target,\n json: doc,\n resultType: \"all\",\n });\n\n if (results.length === 0) {\n return { applied: false, count: 0 };\n }\n\n let count = 0;\n // Process in reverse to avoid index issues when removing\n for (let i = results.length - 1; i >= 0; i--) {\n const result = results[i];\n const { parent, parentProperty, value } = result;\n\n // Check condition if specified\n const shouldRemove =\n typeof action.remove === \"object\"\n ? checkCondition(value, action.remove)\n : true;\n\n if (shouldRemove && parent && parentProperty !== undefined) {\n if (Array.isArray(parent)) {\n parent.splice(parentProperty as number, 1);\n } else {\n delete parent[parentProperty];\n }\n count++;\n }\n }\n\n return { applied: true, count };\n } catch (error) {\n throw new Error(`Failed to remove: ${(error as Error).message}`);\n }\n }\n\n return { applied: false, count: 0 };\n}\n\n/**\n * Extract path order from overlay actions\n * Returns array of paths in the order they appear in overlay\n */\nexport function extractPathOrder(overlay: OverlayDocument): string[] {\n const pathOrder: string[] = [];\n const seenPaths = new Set<string>();\n\n for (const action of overlay.actions) {\n // Match patterns like $.paths['/route'].method\n const match = action.target.match(/^\\$\\.paths\\['([^']+)'\\]/);\n if (match) {\n const targetPath = match[1];\n if (!seenPaths.has(targetPath)) {\n pathOrder.push(targetPath);\n seenPaths.add(targetPath);\n }\n }\n }\n\n return pathOrder;\n}\n\n/**\n * Reorder operation properties so summary, description, operationId come first\n */\nexport function reorderOperationProperties(operation: any): any {\n const priorityKeys = [\"summary\", \"description\", \"operationId\"];\n const reordered: any = {};\n\n // Add priority keys first (in order)\n for (const key of priorityKeys) {\n if (key in operation) {\n reordered[key] = operation[key];\n }\n }\n\n // Add remaining keys\n for (const [key, value] of Object.entries(operation)) {\n if (!priorityKeys.includes(key)) {\n reordered[key] = value;\n }\n }\n\n return reordered;\n}\n\n/**\n * Reorder paths object to match the order from overlay\n */\nexport function reorderPaths(doc: any, pathOrder: string[]): any {\n if (!doc.paths || pathOrder.length === 0) {\n return doc;\n }\n\n const originalPaths = doc.paths;\n const reorderedPaths: any = {};\n\n // First, add paths in the order specified by overlay\n for (const targetPath of pathOrder) {\n if (targetPath in originalPaths) {\n reorderedPaths[targetPath] = originalPaths[targetPath];\n }\n }\n\n // Then add any remaining paths that weren't in the overlay\n for (const [targetPath, value] of Object.entries(originalPaths)) {\n if (!(targetPath in reorderedPaths)) {\n reorderedPaths[targetPath] = value;\n }\n }\n\n // Reorder operation properties within each path\n for (const pathItem of Object.values(reorderedPaths)) {\n const httpMethods = [\n \"get\",\n \"post\",\n \"put\",\n \"patch\",\n \"delete\",\n \"options\",\n \"head\",\n \"trace\",\n ];\n\n for (const method of httpMethods) {\n if (\n (pathItem as any)[method] &&\n typeof (pathItem as any)[method] === \"object\"\n ) {\n (pathItem as any)[method] = reorderOperationProperties(\n (pathItem as any)[method]\n );\n }\n }\n }\n\n doc.paths = reorderedPaths;\n return doc;\n}\n\n/**\n * Reorder top-level document properties to ensure proper ordering\n * OpenAPI spec should have: openapi, info, tags, servers, paths, components, etc.\n */\nexport function reorderDocumentProperties(doc: any): any {\n const priorityKeys = [\"openapi\", \"info\", \"tags\"];\n const reordered: any = {};\n\n // Add priority keys first\n for (const key of priorityKeys) {\n if (key in doc) {\n reordered[key] = doc[key];\n }\n }\n\n // Add remaining keys\n for (const [key, value] of Object.entries(doc)) {\n if (!priorityKeys.includes(key)) {\n reordered[key] = value;\n }\n }\n\n return reordered;\n}\n\n/**\n * Apply overlay to OpenAPI document\n * @param openapi The OpenAPI document to modify\n * @param overlay The overlay document to apply\n * @returns The modified OpenAPI document and statistics\n */\nexport function applyOverlay(\n openapi: any,\n overlay: OverlayDocument\n): { result: any; stats: OverlayStats } {\n // Validate overlay format\n if (!overlay.overlay || !overlay.actions) {\n throw new Error(\n \"Invalid overlay format. Must include 'overlay' version and 'actions' array.\"\n );\n }\n\n const result = deepClone(openapi);\n const stats: OverlayStats = { applied: 0, skipped: 0, totalNodes: 0 };\n\n for (const action of overlay.actions) {\n const { applied, count } = applyAction(result, action);\n\n if (applied) {\n stats.applied++;\n stats.totalNodes += count;\n } else {\n stats.skipped++;\n }\n }\n\n // Reorder paths to match overlay order\n const pathOrder = extractPathOrder(overlay);\n reorderPaths(result, pathOrder);\n\n // Reorder top-level document properties\n const reordered = reorderDocumentProperties(result);\n\n return { result: reordered, stats };\n}\n"]}
1
+ {"version":3,"file":"overlay-engine.js","sourceRoot":"","sources":["../../../src/open-api/overlay/overlay-engine.ts"],"names":[],"mappings":"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","sourcesContent":["import { JSONPath } from \"jsonpath-plus\";\nimport {\n HTTP_METHODS,\n validateOpenApiDocument,\n validateOverlayDocument,\n type OverlayAction,\n type OverlayDocument,\n type RemoveCondition,\n} from \"../../common/open-api/index.js\";\n\nexport interface ApplyResult {\n applied: boolean;\n count: number;\n}\n\nexport interface OverlayStats {\n applied: number;\n skipped: number;\n totalNodes: number;\n}\n\n/**\n * Safely validate and return a generic object\n * Used internally for dynamic JSON manipulation\n */\nfunction toRecord(value: unknown, context: string): Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new Error(\n `Expected object at ${context}, but got ${Array.isArray(value) ? \"array\" : typeof value}`\n );\n }\n return value as Record<string, unknown>;\n}\n\n/**\n * Deep clone an object using structuredClone for better performance and correctness\n * Falls back to JSON parse/stringify for environments without structuredClone\n */\nexport function deepClone<T>(obj: T): T {\n if (typeof structuredClone === \"function\") {\n return structuredClone(obj);\n }\n return JSON.parse(JSON.stringify(obj));\n}\n\n/**\n * Deep merge two objects\n * Special handling for OpenAPI parameters (merge by name+in)\n * Returns the merged result, which will have the shape of source if target is not an object,\n * or a merged object/array if both are objects/arrays\n */\nexport function deepMerge(target: unknown, source: unknown): unknown {\n if (!source || typeof source !== \"object\") {\n return source;\n }\n\n if (!target || typeof target !== \"object\") {\n return deepClone(source);\n }\n\n if (Array.isArray(source)) {\n if (!Array.isArray(target)) {\n return deepClone(source);\n }\n\n // Special case for OpenAPI parameters - merge by name+in\n if (\n source.length > 0 &&\n typeof source[0] === \"object\" &&\n source[0] !== null &&\n \"name\" in source[0] &&\n \"in\" in source[0]\n ) {\n const merged = [...target];\n for (const sourceParam of source) {\n if (typeof sourceParam !== \"object\" || sourceParam === null) continue;\n const existingIndex = merged.findIndex(\n (p) =>\n typeof p === \"object\" &&\n p !== null &&\n \"name\" in p &&\n \"in\" in p &&\n \"name\" in sourceParam &&\n \"in\" in sourceParam &&\n p.name === sourceParam.name &&\n p.in === sourceParam.in\n );\n if (existingIndex >= 0) {\n merged[existingIndex] = deepMerge(merged[existingIndex], sourceParam);\n } else {\n merged.push(deepClone(sourceParam));\n }\n }\n return merged;\n }\n\n // For other arrays, concatenate\n return [...target, ...deepClone(source)];\n }\n\n // Both are objects at this point (checked above)\n const targetObj = target as Record<string, unknown>;\n const sourceObj = source as Record<string, unknown>;\n const result = { ...targetObj };\n\n for (const [key, value] of Object.entries(sourceObj)) {\n if (key in result) {\n result[key] = deepMerge(result[key], value);\n } else {\n result[key] = deepClone(value);\n }\n }\n\n return result;\n}\n\n/**\n * Check if a condition is met for conditional removal\n */\nexport function checkCondition(\n value: unknown,\n condition: RemoveCondition\n): boolean {\n // Support \"empty\" condition to check if object has no keys\n if (condition.empty) {\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n return Object.keys(value).length === 0;\n }\n if (Array.isArray(value)) {\n return value.length === 0;\n }\n return false;\n }\n\n // Support \"missing\" condition to check if a property doesn't exist\n if (condition.missing) {\n const parts = condition.missing.split(\".\");\n let current: unknown = value;\n for (const part of parts) {\n if (\n current &&\n typeof current === \"object\" &&\n !Array.isArray(current) &&\n part in current\n ) {\n current = (current as Record<string, unknown>)[part];\n } else {\n return true; // Property is missing\n }\n }\n return false; // Property exists\n }\n\n return false;\n}\n\n/**\n * Regular expression for parsing JSONPath segments\n * Matches: word, or [quoted], or ['quoted'], or [\"quoted\"], or numeric indices [0]\n */\nconst JSONPATH_SEGMENT_PATTERN = /[^.[]+|\\[[^\\]]+\\]/g;\n\n/**\n * Extract key from bracket notation, handling various formats:\n * - ['key'] -> key\n * - [\"key\"] -> key\n * - [key] -> key\n * - [0] -> 0 (as string for consistency)\n */\nfunction extractBracketKey(bracketNotation: string): string {\n const inner = bracketNotation.slice(1, -1); // Remove [ and ]\n\n // Handle single-quoted: ['key'] -> key\n if (inner.startsWith(\"'\") && inner.endsWith(\"'\")) {\n return inner.slice(1, -1);\n }\n\n // Handle double-quoted: [\"key\"] -> key\n if (inner.startsWith('\"') && inner.endsWith('\"')) {\n return inner.slice(1, -1);\n }\n\n // Handle unquoted: [key] or [0] -> key or 0\n return inner;\n}\n\n/**\n * Set a value at a JSONPath, creating parent paths as needed\n * Handles patterns like:\n * $.paths['/route'].method\n * $.components.parameters['name']\n * $.paths[\"/users\"].get\n * $.paths[users].get\n * $.items[0].value\n */\nexport function setValueAtPath(\n doc: unknown,\n target: string,\n value: unknown\n): void {\n // Parse the path manually to handle both bracket and dot notation\n // Example: $.paths['/route'].post -> [\"paths\", \"['/route']\", \"post\"]\n\n // Remove leading $.\n const targetPath = target.replace(/^\\$\\./, \"\");\n\n // Split by dots, but keep bracket notation together\n const parts = targetPath.match(JSONPATH_SEGMENT_PATTERN) || [];\n\n let current = toRecord(doc, \"setValueAtPath document\");\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n\n // Handle bracket notation\n if (part.startsWith(\"[\")) {\n const key = extractBracketKey(part);\n if (!(key in current)) {\n current[key] = {};\n }\n const next = current[key];\n current = toRecord(next, `setValueAtPath at ${target}[${i}]`);\n } else {\n // Handle regular property\n if (!(part in current)) {\n current[part] = {};\n }\n const next = current[part];\n current = toRecord(next, `setValueAtPath at ${target}.${part}`);\n }\n }\n\n // Set the final value\n const lastPart = parts[parts.length - 1];\n if (lastPart.startsWith(\"[\")) {\n const key = extractBracketKey(lastPart);\n current[key] = value;\n } else {\n current[lastPart] = value;\n }\n}\n\n/**\n * Apply a single overlay action to the document\n */\nexport function applyAction(doc: unknown, action: OverlayAction): ApplyResult {\n const docRecord = toRecord(doc, \"applyAction document\");\n\n if (action.update) {\n // Handle root level updates specially\n if (action.target === \"$\") {\n Object.assign(docRecord, deepMerge(doc, action.update));\n return { applied: true, count: 1 };\n }\n\n try {\n // Try to find existing nodes\n const results = JSONPath({\n path: action.target,\n // biome-ignore lint/suspicious/noExplicitAny: JSONPath library requires loose typing\n json: doc as any,\n resultType: \"all\",\n }) as Array<{ parent: unknown; parentProperty: unknown; value: unknown }>;\n\n if (results.length > 0) {\n // Path exists, merge the update\n let count = 0;\n for (const result of results) {\n const { parent, parentProperty } = result;\n if (\n parent &&\n typeof parent === \"object\" &&\n !Array.isArray(parent) &&\n parentProperty !== undefined\n ) {\n const parentRecord = parent as Record<string, unknown>;\n parentRecord[parentProperty as string] = deepMerge(\n parentRecord[parentProperty as string],\n action.update\n );\n count++;\n }\n }\n return { applied: true, count };\n }\n\n // Path doesn't exist, create it manually\n setValueAtPath(doc, action.target, action.update);\n return { applied: true, count: 1 };\n } catch (cause) {\n throw new Error(\"Failed to apply update\", { cause });\n }\n } else if (action.remove) {\n try {\n const results = JSONPath({\n path: action.target,\n // biome-ignore lint/suspicious/noExplicitAny: JSONPath library requires loose typing\n json: doc as any,\n resultType: \"all\",\n }) as Array<{ parent: unknown; parentProperty: unknown; value: unknown }>;\n\n if (results.length === 0) {\n return { applied: false, count: 0 };\n }\n\n let count = 0;\n // Process in reverse to avoid index issues when removing\n for (let i = results.length - 1; i >= 0; i--) {\n const result = results[i];\n const { parent, parentProperty, value } = result;\n\n // Check condition if specified\n const shouldRemove =\n typeof action.remove === \"object\"\n ? checkCondition(value, action.remove)\n : true;\n\n if (shouldRemove && parent && parentProperty !== undefined) {\n if (Array.isArray(parent)) {\n parent.splice(parentProperty as number, 1);\n } else if (typeof parent === \"object\" && parent !== null) {\n delete (parent as Record<string, unknown>)[\n parentProperty as string\n ];\n }\n count++;\n }\n }\n\n return { applied: true, count };\n } catch (cause) {\n throw new Error(\"Failed to remove\", { cause });\n }\n }\n\n return { applied: false, count: 0 };\n}\n\n/**\n * Extract path order from overlay actions\n * Returns array of paths in the order they appear in overlay\n */\nexport function extractPathOrder(overlay: OverlayDocument): string[] {\n const pathOrder: string[] = [];\n const seenPaths = new Set<string>();\n\n for (const action of overlay.actions) {\n // Match patterns like $.paths['/route'].method\n const match = action.target.match(/^\\$\\.paths\\['([^']+)'\\]/);\n if (match) {\n const targetPath = match[1];\n if (!seenPaths.has(targetPath)) {\n pathOrder.push(targetPath);\n seenPaths.add(targetPath);\n }\n }\n }\n\n return pathOrder;\n}\n\n/**\n * Reorder operation properties so summary, description, operationId come first\n * Returns a new object with reordered keys\n */\nexport function reorderOperationProperties(\n operation: unknown\n): Record<string, unknown> {\n const operationObj = toRecord(operation, \"reorderOperationProperties\");\n const priorityKeys = [\"summary\", \"description\", \"operationId\"];\n const reordered: Record<string, unknown> = {};\n\n // Add priority keys first (in order)\n for (const key of priorityKeys) {\n if (key in operationObj) {\n reordered[key] = operationObj[key];\n }\n }\n\n // Add remaining keys\n for (const [key, value] of Object.entries(operationObj)) {\n if (!priorityKeys.includes(key)) {\n reordered[key] = value;\n }\n }\n\n return reordered;\n}\n\n/**\n * Reorder paths object to match the order from overlay\n * Modifies the document in place and returns it\n */\nexport function reorderPaths<T>(doc: T, pathOrder: string[]): T {\n // Early return if no paths to reorder\n if (\n typeof doc !== \"object\" ||\n doc === null ||\n !(\"paths\" in doc) ||\n pathOrder.length === 0\n ) {\n return doc;\n }\n\n const docRecord = doc as Record<string, unknown>;\n const originalPaths = toRecord(docRecord.paths, \"paths\");\n const reorderedPaths: Record<string, unknown> = {};\n\n // First, add paths in the order specified by overlay\n for (const targetPath of pathOrder) {\n if (targetPath in originalPaths) {\n reorderedPaths[targetPath] = originalPaths[targetPath];\n }\n }\n\n // Then add any remaining paths that weren't in the overlay\n for (const [targetPath, value] of Object.entries(originalPaths)) {\n if (!(targetPath in reorderedPaths)) {\n reorderedPaths[targetPath] = value;\n }\n }\n\n // Reorder operation properties within each path\n for (const pathItem of Object.values(reorderedPaths)) {\n if (typeof pathItem !== \"object\" || pathItem === null) continue;\n\n const pathItemRecord = pathItem as Record<string, unknown>;\n\n for (const method of HTTP_METHODS) {\n if (\n method in pathItemRecord &&\n typeof pathItemRecord[method] === \"object\" &&\n pathItemRecord[method] !== null\n ) {\n pathItemRecord[method] = reorderOperationProperties(\n pathItemRecord[method]\n );\n }\n }\n }\n\n docRecord.paths = reorderedPaths;\n return doc;\n}\n\n/**\n * Reorder top-level document properties to ensure proper ordering\n * OpenAPI spec should have: openapi, info, tags, servers, paths, components, etc.\n * Returns a new object with reordered keys\n */\nexport function reorderDocumentProperties(\n doc: unknown\n): Record<string, unknown> {\n const docObj = toRecord(doc, \"reorderDocumentProperties\");\n const priorityKeys = [\"openapi\", \"info\", \"tags\"];\n const reordered: Record<string, unknown> = {};\n\n // Add priority keys first\n for (const key of priorityKeys) {\n if (key in docObj) {\n reordered[key] = docObj[key];\n }\n }\n\n // Add remaining keys\n for (const [key, value] of Object.entries(docObj)) {\n if (!priorityKeys.includes(key)) {\n reordered[key] = value;\n }\n }\n\n return reordered;\n}\n\n/**\n * Apply overlay to OpenAPI document\n *\n * Validates both the OpenAPI document and overlay upfront, then applies\n * all actions defined in the overlay to produce a modified OpenAPI document.\n *\n * @param openapi The OpenAPI document to modify (will be validated)\n * @param overlay The overlay document to apply (will be validated)\n * @returns The modified OpenAPI document and statistics about what was applied\n * @throws {Error} if either document is invalid, with actionable error messages\n *\n * @example\n * ```typescript\n * const result = applyOverlay(openapiDoc, overlayDoc);\n * console.log(`Applied ${result.stats.applied} actions`);\n * ```\n */\nexport function applyOverlay(\n openapi: unknown,\n overlay: unknown\n): { result: Record<string, unknown>; stats: OverlayStats } {\n // Validate inputs upfront with actionable error messages\n const validatedOverlay = validateOverlayDocument(overlay);\n const validatedOpenApi = validateOpenApiDocument(openapi);\n\n // Clone the document for modification\n const result = deepClone(validatedOpenApi);\n const stats: OverlayStats = { applied: 0, skipped: 0, totalNodes: 0 };\n\n // Apply each action\n for (const action of validatedOverlay.actions) {\n const { applied, count } = applyAction(result, action);\n\n if (applied) {\n stats.applied++;\n stats.totalNodes += count;\n } else {\n stats.skipped++;\n }\n }\n\n // Reorder paths to match overlay order\n const pathOrder = extractPathOrder(validatedOverlay);\n reorderPaths(result, pathOrder);\n\n // Reorder top-level document properties\n const reordered = reorderDocumentProperties(result);\n\n return { result: reordered, stats };\n}\n"]}