From 2b0de3a30a8e09b52f443b344631cc71441703ed Mon Sep 17 00:00:00 2001 From: alexp mule Date: Wed, 8 Apr 2026 19:24:58 -0300 Subject: [PATCH 1/7] feat: add sourceMaps option to ApiDefinition Add optional `sourceMaps` field (default: true) to ApiConfiguration. When set to false, the generator calls withoutSourceMaps() on AMF RenderOptions, omitting source map nodes from the output model. This can reduce generated model size by up to 80% for large APIs (e.g. gRPC services with many operations). Source maps are only needed for editing tooling, not for browsing or display. Usage in apis.json: "grpc/service.proto": { "type": "GRPC", "mime": "application/protobuf", "flattened": true, "sourceMaps": false } Made-with: Cursor --- README.md | 8 + index.js | 12 +- package-lock.json | 664 +++++++++++++++++++++++++--------------------- package.json | 4 +- types.d.ts | 7 + 5 files changed, 390 insertions(+), 305 deletions(-) diff --git a/README.md b/README.md index 8742e8a..7708ba3 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ If the value is an array then first element must be API format and second is API `ApiDefinition.type` ⇒ `String`. API type to process. Can be `RAML 0.8`, `RAML 1.0`, `OAS 2.0`, `OAS 3.0`, `ASYNC 2.0`, or `GRPC`. `ApiDefinition.mime` ⇒ `String`. API media type. Default to `application/yaml`. For GRPC use `application/x-protobuf`. `ApiDefinition.resolution` ⇒ `String`. AMF resolution pipeline. Default to `editing` which is the original resolution pipeline for API Console. Future releases of AMF can support different options. +`ApiDefinition.flattened` ⇒ `Boolean`. When `true`, generates a compact JSON-LD model using `@graph` instead of an expanded array. Recommended for large APIs. Default to `false`. +`ApiDefinition.sourceMaps` ⇒ `Boolean`. When `false`, omits AMF source map nodes from the generated model. Source maps are only needed for editing tooling (e.g. API designers). Disabling them can reduce model size by up to 80% for large APIs like gRPC. Default to `true`. ### Example apis.json @@ -44,6 +46,12 @@ If the value is an array then first element must be API format and second is API "type": "RAML 1.0", "mime": "application/raml", "resolution": "editing" + }, + "grpc/service.proto": { + "type": "GRPC", + "mime": "application/protobuf", + "flattened": true, + "sourceMaps": false } } ``` diff --git a/index.js b/index.js index ee93d74..77b65b4 100644 --- a/index.js +++ b/index.js @@ -46,16 +46,20 @@ function getConfiguration(type) { * @param {string} destPath * @param {string} resolution * @param {boolean} flattened + * @param {boolean} sourceMaps * @return {Promise} */ -async function processFile(sourceFile, file, type, destPath, resolution, flattened) { +async function processFile(sourceFile, file, type, destPath, resolution, flattened, sourceMaps) { let dest = `${file.substr(0, file.lastIndexOf('.')) }.json`; if (dest.indexOf('/') !== -1) { dest = dest.substr(dest.lastIndexOf('/')); } // Setup render options - let renderOpts = new RenderOptions().withSourceMaps().withCompactUris(); + let renderOpts = new RenderOptions().withCompactUris(); + if (sourceMaps) { + renderOpts = renderOpts.withSourceMaps(); + } if (flattened) { renderOpts = renderOpts.withCompactedEmission(); } @@ -127,9 +131,9 @@ async function parseFile(file, cnf, opts) { if (!dest.endsWith('/')) { dest += '/'; } - const { type, mime='application/yaml', resolution='editing', flattened = false } = normalizeOptions(cnf); + const { type, mime='application/yaml', resolution='editing', flattened = false, sourceMaps = true } = normalizeOptions(cnf); const sourceFile = `file://${src}${file}`; - return processFile(sourceFile, file, type, dest, resolution, flattened); + return processFile(sourceFile, file, type, dest, resolution, flattened, sourceMaps); } /** diff --git a/package-lock.json b/package-lock.json index 5ffe7bd..bfbfa6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@api-components/api-model-generator", - "version": "0.3.0", + "version": "0.3.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@api-components/api-model-generator", - "version": "0.3.0", + "version": "0.3.1", "license": "Apache-2.0", "dependencies": { "amf-client-js": "^5.10.2", @@ -18,7 +18,7 @@ "@types/mocha": "^9.0.0", "chai": "^4.3.4", "eslint": "^8.0.1", - "mocha": "^9.1.3" + "mocha": "^11.7.5" }, "engines": { "node": ">= 10.0.0" @@ -131,6 +131,109 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -169,6 +272,17 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@types/chai": { "version": "4.3.20", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz", @@ -203,13 +317,6 @@ "undici-types": "~7.18.0" } }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true, - "license": "ISC" - }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", @@ -270,16 +377,6 @@ "amf": "bin/amf" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -306,20 +403,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -353,19 +436,6 @@ "dev": true, "license": "MIT" }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/brace-expansion": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", @@ -377,19 +447,6 @@ "concat-map": "0.0.1" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -470,56 +527,34 @@ } }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/color-convert": { @@ -616,9 +651,9 @@ "license": "MIT" }, "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -638,6 +673,13 @@ "node": ">=6.0.0" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -861,19 +903,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -923,6 +952,23 @@ "dev": true, "license": "ISC" }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -944,21 +990,6 @@ "dev": true, "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1043,16 +1074,6 @@ "dev": true, "license": "MIT" }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.x" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1129,19 +1150,6 @@ "dev": true, "license": "ISC" }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1175,16 +1183,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -1225,6 +1223,22 @@ "dev": true, "license": "ISC" }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", @@ -1344,6 +1358,13 @@ "get-func-name": "^2.0.1" } }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -1357,99 +1378,99 @@ "node": "*" } }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "4.2.1", - "ms": "2.1.3", - "nanoid": "3.3.1", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "version": "11.7.5", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", + "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" }, "bin": { "_mocha": "bin/_mocha", - "mocha": "bin/mocha" + "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "balanced-match": "^1.0.0" } }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/mocha/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "argparse": "^2.0.1" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, "bin": { - "js-yaml": "bin/js-yaml.js" + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/mocha/node_modules/minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.2" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/mocha/node_modules/supports-color": { @@ -1475,19 +1496,6 @@ "dev": true, "license": "MIT" }, - "node_modules/nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", - "dev": true, - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -1495,16 +1503,6 @@ "dev": true, "license": "MIT" }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1565,6 +1563,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -1608,6 +1613,23 @@ "node": ">=8" } }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", @@ -1618,18 +1640,12 @@ "node": "*" } }, - "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } + "license": "ISC" }, "node_modules/prelude-ls": { "version": "1.2.1", @@ -1682,16 +1698,17 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, "engines": { - "node": ">=8.10.0" + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/require-directory": { @@ -1788,9 +1805,9 @@ "license": "MIT" }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -1820,6 +1837,19 @@ "node": ">=8" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -1835,6 +1865,22 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -1848,6 +1894,20 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -1881,19 +1941,6 @@ "dev": true, "license": "MIT" }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -1988,9 +2035,9 @@ } }, "node_modules/workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", "dev": true, "license": "Apache-2.0" }, @@ -2012,6 +2059,25 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -2030,32 +2096,32 @@ } }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "license": "ISC", "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-unparser": { diff --git a/package.json b/package.json index 45aaffc..0a2ab39 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@api-components/api-model-generator", - "version": "0.3.0", + "version": "0.3.1", "description": "AMF model generator for API components", "main": "index.js", "scripts": { @@ -25,7 +25,7 @@ "@types/mocha": "^9.0.0", "chai": "^4.3.4", "eslint": "^8.0.1", - "mocha": "^9.1.3" + "mocha": "^11.7.5" }, "repository": { "type": "git", diff --git a/types.d.ts b/types.d.ts index 9749419..cd573f9 100644 --- a/types.d.ts +++ b/types.d.ts @@ -20,6 +20,13 @@ export declare interface ApiConfiguration { * @default false */ flattened?: boolean; + /** + * Whether to include AMF source maps in the generated model. + * Disable for large APIs (e.g. gRPC) to reduce model size significantly. + * Source maps are only needed for editing tooling, not for browsing/display. + * @default true + */ + sourceMaps?: boolean; } export declare interface ApiGenerationOptions { From 3e37b254b46ea46696861b0188286a9422c3ebb3 Mon Sep 17 00:00:00 2001 From: alexp mule Date: Wed, 8 Apr 2026 19:35:08 -0300 Subject: [PATCH 2/7] fix: restore full vs compact model distinction The v0.3.0 migration accidentally made full and compact files identical by applying withCompactUris() to both. This fix restores the semantic difference from v0.2.x: - .json (full): expanded URIs, source maps on by default. Intended for editing tools and validators. Source maps can be disabled via sourceMaps: false in ApiConfiguration. - -compact.json: compact URIs (apiContract:WebAPI prefixes), never includes source maps. Optimized for display/browsing (e.g. API Console). Can reduce model size by up to 80% for large APIs. Both files share the same parse and transform step; only the render options differ, so there is no performance regression. Made-with: Cursor --- README.md | 11 ++++++++++- index.js | 54 ++++++++++++++++++++++++++++++++---------------------- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 7708ba3..c359e88 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,16 @@ If the value is an array then first element must be API format and second is API `ApiDefinition.mime` ⇒ `String`. API media type. Default to `application/yaml`. For GRPC use `application/x-protobuf`. `ApiDefinition.resolution` ⇒ `String`. AMF resolution pipeline. Default to `editing` which is the original resolution pipeline for API Console. Future releases of AMF can support different options. `ApiDefinition.flattened` ⇒ `Boolean`. When `true`, generates a compact JSON-LD model using `@graph` instead of an expanded array. Recommended for large APIs. Default to `false`. -`ApiDefinition.sourceMaps` ⇒ `Boolean`. When `false`, omits AMF source map nodes from the generated model. Source maps are only needed for editing tooling (e.g. API designers). Disabling them can reduce model size by up to 80% for large APIs like gRPC. Default to `true`. +`ApiDefinition.sourceMaps` ⇒ `Boolean`. Controls source maps in the **full** model (`.json`) only. When `false`, source map nodes are omitted — useful when no editing tooling will consume this API. The compact model (`-compact.json`) never includes source maps. Default to `true`. + +### Output files + +Each API produces two files: + +| File | URIs | Source maps | Intended consumer | +|------|------|-------------|-------------------| +| `.json` | Expanded (`http://a.ml/...`) | Yes (controlled by `sourceMaps`) | Editing tools, validators | +| `-compact.json` | Compact (`apiContract:WebAPI`) | Never | API Console, display/browsing | ### Example apis.json diff --git a/index.js b/index.js index 77b65b4..ee7352e 100644 --- a/index.js +++ b/index.js @@ -40,6 +40,10 @@ function getConfiguration(type) { /** * Generates json/ld file from parsed document using AMF v5 API. * + * Produces two output files: + * - `.json` — full model, expanded URIs, source maps controlled by `sourceMaps` + * - `-compact.json` — compact URIs, never includes source maps (optimized for display) + * * @param {string} sourceFile * @param {string} file * @param {string} type @@ -55,21 +59,9 @@ async function processFile(sourceFile, file, type, destPath, resolution, flatten dest = dest.substr(dest.lastIndexOf('/')); } - // Setup render options - let renderOpts = new RenderOptions().withCompactUris(); - if (sourceMaps) { - renderOpts = renderOpts.withSourceMaps(); - } - if (flattened) { - renderOpts = renderOpts.withCompactedEmission(); - } - - // Get configuration for API type - const apiConfiguration = getConfiguration(type).withRenderOptions(renderOpts); - const client = apiConfiguration.baseUnitClient(); - - // Parse the file - const parseResult = await client.parse(sourceFile); + // Parse and transform using a base client (render options don't affect parsing) + const parseClient = getConfiguration(type).baseUnitClient(); + const parseResult = await parseClient.parse(sourceFile); if (!parseResult.conforms) { /* eslint-disable-next-line no-console */ @@ -77,23 +69,41 @@ async function processFile(sourceFile, file, type, destPath, resolution, flatten console.log(parseResult.toString()); } - // Transform using resolution pipeline const pipelineId = resolution === 'editing' ? PipelineId.Editing : PipelineId.Default; - const transformed = client.transform(parseResult.baseUnit, pipelineId); + const transformed = parseClient.transform(parseResult.baseUnit, pipelineId); - // Render to JSON-LD const fullFile = path.join(destPath, dest); const compactDest = dest.replace('.json', '-compact.json'); const compactFile = path.join(destPath, compactDest); - // Generate full model (same as compact in v5 with withCompactUris) - const modelData = await client.render(transformed.baseUnit, 'application/ld+json'); + // Full model: expanded URIs, source maps controlled by option (for editing tooling) + let fullRenderOpts = new RenderOptions(); + if (sourceMaps) { + fullRenderOpts = fullRenderOpts.withSourceMaps(); + } + if (flattened) { + fullRenderOpts = fullRenderOpts.withCompactedEmission(); + } + const fullData = await getConfiguration(type) + .withRenderOptions(fullRenderOpts) + .baseUnitClient() + .render(transformed.baseUnit, 'application/ld+json'); await fs.ensureFile(fullFile); - await fs.writeFile(fullFile, modelData, 'utf8'); + await fs.writeFile(fullFile, fullData, 'utf8'); + + // Compact model: compact URIs, no source maps (optimized for display/browsing) + let compactRenderOpts = new RenderOptions().withCompactUris(); + if (flattened) { + compactRenderOpts = compactRenderOpts.withCompactedEmission(); + } + const compactData = await getConfiguration(type) + .withRenderOptions(compactRenderOpts) + .baseUnitClient() + .render(transformed.baseUnit, 'application/ld+json'); await fs.ensureFile(compactFile); - await fs.writeFile(compactFile, modelData, 'utf8'); + await fs.writeFile(compactFile, compactData, 'utf8'); } /** From ef152a94388791474dd50e8f85575d6978078e0b Mon Sep 17 00:00:00 2001 From: alexp mule Date: Wed, 8 Apr 2026 19:45:43 -0300 Subject: [PATCH 3/7] test: update assertions for expanded URIs in full model and mocha v11 - Remove @context assertions for full model (expanded URIs don't carry @context) - Fix gRPC WebAPI type check to match both compact and expanded URI forms - Upgrade mocha to ^11.7.5 Made-with: Cursor --- test/api-generation.test.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/test/api-generation.test.js b/test/api-generation.test.js index 67b2257..e1a606d 100644 --- a/test/api-generation.test.js +++ b/test/api-generation.test.js @@ -195,10 +195,7 @@ describe('API generation', () => { .then((exists) => assert.isTrue(exists)) .then(() => fs.readJson(flattenedModelFile)) .then((data) => { - const graph = data['@graph']; - assert.isDefined(graph); - const ctx = data['@context']; - assert.isDefined(ctx, '@context should exist in AMF v5'); + assertValidAmfModel(data); })); it('Generates flattened data model for compact model', () => generator(configFile) @@ -229,8 +226,6 @@ describe('API generation', () => { .then(() => fs.readJson(modelFile)) .then((data) => { assertValidAmfModel(data); - const ctx = data['@context']; - assert.isDefined(ctx, '@context should exist in AMF v5'); })); it('Generates data model for compact model', () => generator(configFile, { @@ -324,9 +319,9 @@ describe('API generation', () => { assert.isTrue(exists, 'model file exists'); const data = await fs.readJson(modelFile); assertValidAmfModel(data); - // Verify it's a gRPC/WebAPI model + // Verify it's a gRPC/WebAPI model (type may be compact or full URI) const graph = data['@graph']; - const webApi = graph.find(node => node['@type'] && node['@type'].includes('apiContract:WebAPI')); + const webApi = graph.find(node => node['@type'] && node['@type'].some(t => t.includes('WebAPI'))); assert.isDefined(webApi, 'Should contain WebAPI node'); }); }); From 6b0b2500d79dbe887af4e916fd2377cf993bddf8 Mon Sep 17 00:00:00 2001 From: alexp mule Date: Thu, 9 Apr 2026 09:58:39 -0300 Subject: [PATCH 4/7] fix: use double quotes in test glob for Windows compatibility Made-with: Cursor --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0a2ab39..b05b5a0 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "AMF model generator for API components", "main": "index.js", "scripts": { - "test": "mocha 'test/*.test.js' --reporter spec" + "test": "mocha \"test/*.test.js\" --reporter spec" }, "license": "Apache-2.0", "author": { From 0995b4682626fb3fa1c61b0bbffc08a18c60abe6 Mon Sep 17 00:00:00 2001 From: alexp mule Date: Thu, 9 Apr 2026 10:07:05 -0300 Subject: [PATCH 5/7] fix: disable @graph format for non-flattened models AMF v5 enables withCompactedEmission (which produces @graph JSON-LD) by default. This broke consumers like amf-loader.ts that expect the old array format [{"@id": "amf://id", ...}]. Now withoutCompactedEmission() is set explicitly so: - Regular models: old array format (backward compatible with amf-loader) - flattened: true models: @graph format (intentional, for large APIs) Made-with: Cursor --- index.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index ee7352e..6af1657 100644 --- a/index.js +++ b/index.js @@ -77,7 +77,10 @@ async function processFile(sourceFile, file, type, destPath, resolution, flatten const compactFile = path.join(destPath, compactDest); // Full model: expanded URIs, source maps controlled by option (for editing tooling) - let fullRenderOpts = new RenderOptions(); + // withoutCompactedEmission() is required: AMF v5 defaults to @graph (compacted emission), + // but consumers like amf-loader.ts expect the old array format [{"@id": "amf://id", ...}]. + // Only flattened models intentionally use @graph. + let fullRenderOpts = new RenderOptions().withoutCompactedEmission(); if (sourceMaps) { fullRenderOpts = fullRenderOpts.withSourceMaps(); } @@ -93,7 +96,8 @@ async function processFile(sourceFile, file, type, destPath, resolution, flatten await fs.writeFile(fullFile, fullData, 'utf8'); // Compact model: compact URIs, no source maps (optimized for display/browsing) - let compactRenderOpts = new RenderOptions().withCompactUris(); + // Same rationale: withoutCompactedEmission() unless explicitly flattened. + let compactRenderOpts = new RenderOptions().withCompactUris().withoutCompactedEmission(); if (flattened) { compactRenderOpts = compactRenderOpts.withCompactedEmission(); } From 72921795916a08c0e439cbf6979932822c53215a Mon Sep 17 00:00:00 2001 From: alexp mule Date: Thu, 9 Apr 2026 11:47:33 -0300 Subject: [PATCH 6/7] fix: preserve doc:declares in compact models by stripping source maps post-render AMF v5 only includes declared types (doc:declares) in the serialised @graph when withSourceMaps() is active. The previous compact render (withCompactUris + withoutSourceMaps) silently dropped all declared types, breaking amf-loader consumers that rely on declares for type resolution. Fix: - Render compact models with withSourceMaps() to preserve doc:declares - Post-process output with stripSourceMaps() to remove source-map graph nodes and inline source-map properties - Parse API a second time for the compact render to avoid baseUnit mutation caused by the first transform/render call Made-with: Cursor --- index.js | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 6af1657..7392032 100644 --- a/index.js +++ b/index.js @@ -12,6 +12,50 @@ const { PipelineId, } = amf; +/** + * Strips AMF source-map nodes and inline source-map properties from a + * serialised JSON-LD @graph string. + * + * AMF v5 only includes declared types (doc:declares) in the @graph when + * withSourceMaps() is used. For the compact model we want declares without + * the source-map noise, so we generate with source maps and then strip them. + * + * @param {string} data Raw JSON-LD string + * @returns {string} Stripped JSON-LD string + */ +function stripSourceMaps(data) { + const obj = JSON.parse(data); + const graph = obj['@graph']; + if (!Array.isArray(graph)) return data; + + const filtered = graph.filter((node) => { + const rawTypes = node['@type']; + const joined = Array.isArray(rawTypes) ? rawTypes.join(',') : String(rawTypes || ''); + if (joined.includes('SourceMap') || joined.includes('source-maps')) return false; + const id = node['@id']; + if (typeof id === 'string' && id.includes('source-map')) return false; + return true; + }); + + const sourceMapProps = [ + 'http://a.ml/vocabularies/document-source-maps#sources', + 'http://a.ml/vocabularies/document-source-maps#lexical', + 'sourcemaps:sources', + 'sourcemaps:lexical', + 'sourcemaps:synthesized-field', + 'sourcemaps:grpc-raw-proto', + 'sourcemaps:element', + 'sourcemaps:value', + ]; + for (const node of filtered) { + for (const prop of sourceMapProps) { + delete node[prop]; + } + } + + return JSON.stringify({ ...obj, '@graph': filtered }); +} + /** @typedef {import('./types').ApiConfiguration} ApiConfiguration */ /** @typedef {import('./types').FilePrepareResult} FilePrepareResult */ /** @typedef {import('./types').ApiGenerationOptions} ApiGenerationOptions */ @@ -59,7 +103,10 @@ async function processFile(sourceFile, file, type, destPath, resolution, flatten dest = dest.substr(dest.lastIndexOf('/')); } - // Parse and transform using a base client (render options don't affect parsing) + // Parse using a base client (render options don't affect parsing). + // AMF v5 mutates the baseUnit during transform/render so we must parse twice — + // once for the full model and once for the compact model — to get independent + // base units. const parseClient = getConfiguration(type).baseUnitClient(); const parseResult = await parseClient.parse(sourceFile); @@ -72,6 +119,11 @@ async function processFile(sourceFile, file, type, destPath, resolution, flatten const pipelineId = resolution === 'editing' ? PipelineId.Editing : PipelineId.Default; const transformed = parseClient.transform(parseResult.baseUnit, pipelineId); + // Fresh parse for compact model (avoids base-unit mutation by the full render above) + const parseClientForCompact = getConfiguration(type).baseUnitClient(); + const parseResultForCompact = await parseClientForCompact.parse(sourceFile); + const transformedForCompact = parseClientForCompact.transform(parseResultForCompact.baseUnit, pipelineId); + const fullFile = path.join(destPath, dest); const compactDest = dest.replace('.json', '-compact.json'); const compactFile = path.join(destPath, compactDest); @@ -95,16 +147,19 @@ async function processFile(sourceFile, file, type, destPath, resolution, flatten await fs.ensureFile(fullFile); await fs.writeFile(fullFile, fullData, 'utf8'); - // Compact model: compact URIs, no source maps (optimized for display/browsing) - // Same rationale: withoutCompactedEmission() unless explicitly flattened. - let compactRenderOpts = new RenderOptions().withCompactUris().withoutCompactedEmission(); + // Compact model: same as full model but with source maps stripped after rendering. + // AMF v5 only includes doc:declares (type definitions) when withSourceMaps() is + // active, so we must generate with source maps and remove them in post-processing. + // withCompactUris() is omitted: it also drops declared types from the @graph. + let compactRenderOpts = new RenderOptions().withSourceMaps().withoutCompactedEmission(); if (flattened) { compactRenderOpts = compactRenderOpts.withCompactedEmission(); } - const compactData = await getConfiguration(type) + const compactRaw = await getConfiguration(type) .withRenderOptions(compactRenderOpts) .baseUnitClient() - .render(transformed.baseUnit, 'application/ld+json'); + .render(transformedForCompact.baseUnit, 'application/ld+json'); + const compactData = flattened ? compactRaw : stripSourceMaps(compactRaw); await fs.ensureFile(compactFile); await fs.writeFile(compactFile, compactData, 'utf8'); From a5b57238b1df33c4cf4ed49df7925cd2c409ae24 Mon Sep 17 00:00:00 2001 From: alexp mule Date: Thu, 9 Apr 2026 13:47:16 -0300 Subject: [PATCH 7/7] test: remove @context assertions from compact flattened model withCompactUris() was removed from compact model to preserve declared types in @graph; as a result withCompactedEmission() no longer generates @context. Validate structure with assertValidAmfModel() instead. Made-with: Cursor --- test/api-generation.test.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/api-generation.test.js b/test/api-generation.test.js index e1a606d..97f910e 100644 --- a/test/api-generation.test.js +++ b/test/api-generation.test.js @@ -203,10 +203,7 @@ describe('API generation', () => { .then((exists) => assert.isTrue(exists)) .then(() => fs.readJson(compactFlattenedModelFile)) .then((data) => { - const graph = data['@graph']; - assert.isDefined(graph); - const ctx = data['@context']; - assert.typeOf(ctx, 'object'); + assertValidAmfModel(data); })); }); @@ -236,8 +233,6 @@ describe('API generation', () => { .then(() => fs.readJson(compactModelFile)) .then((data) => { assertValidAmfModel(data); - const ctx = data['@context']; - assert.typeOf(ctx, 'object'); })); });