bump-cli 2.9.11 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,8 +5,11 @@ import { parseWithPointers, safeStringify } from '@stoplight/yaml';
5
5
  import debug from 'debug';
6
6
  import { default as nodePath } from 'node:path';
7
7
  import { Overlay } from './core/overlay.js';
8
+ import arazzoSchemas from './core/schemas/arazzo-schemas/index.js';
9
+ import flowerSchemas from './core/schemas/flower-schemas/index.js';
8
10
  import openapiSchemas from './core/schemas/oas-schemas/index.js';
9
11
  class SupportedFormat {
12
+ static arazzo = arazzoSchemas.schemas;
10
13
  static asyncapi = {
11
14
  '2.0': asyncapi.schemas['2.0.0'],
12
15
  '2.1': asyncapi.schemas['2.1.0'],
@@ -16,12 +19,8 @@ class SupportedFormat {
16
19
  '2.5': asyncapi.schemas['2.5.0'],
17
20
  '2.6': asyncapi.schemas['2.6.0'],
18
21
  };
19
- static openapi = {
20
- '2.0': openapiSchemas.schemas['2.0'],
21
- '3.0': openapiSchemas.schemas['3.0'],
22
- '3.1': openapiSchemas.schemas['3.1'],
23
- '3.2': openapiSchemas.schemas['3.2'],
24
- };
22
+ static flower = flowerSchemas.schemas;
23
+ static openapi = openapiSchemas.schemas;
25
24
  }
26
25
  class UnsupportedFormat extends CLIError {
27
26
  constructor(message = '') {
@@ -40,31 +39,42 @@ class API {
40
39
  overlayedDefinition;
41
40
  rawDefinition;
42
41
  references;
43
- spec;
44
42
  specName;
45
43
  version;
46
- constructor(location, values) {
44
+ constructor(location, data) {
47
45
  this.location = location;
48
- this.references = [];
49
- const [raw, parsed] = this.resolveContent(values);
46
+ const [raw, parsed, references] = this._resolveContentFrom(data);
47
+ this.references = references || [];
50
48
  this.rawDefinition = raw;
51
49
  this.definition = parsed;
52
50
  this.specName = this.getSpecName(parsed);
53
51
  this.version = this.getVersion(parsed);
54
- this.spec = this.getSpec(parsed);
55
- if (this.spec === undefined) {
52
+ if (!this.getSpec(parsed)) {
56
53
  throw new UnsupportedFormat(`${this.specName} ${this.version}`);
57
54
  }
58
55
  }
56
+ static isArazzo(definition) {
57
+ return 'arazzo' in definition;
58
+ }
59
59
  static isAsyncAPI(definition) {
60
60
  return 'asyncapi' in definition;
61
61
  }
62
+ static isFlower(definition) {
63
+ return 'flower' in definition;
64
+ }
62
65
  static isOpenAPI(definition) {
63
66
  return typeof definition.openapi === 'string' || typeof definition.swagger === 'string';
64
67
  }
65
68
  static isOpenAPIOverlay(definition) {
66
69
  return 'overlay' in definition;
67
70
  }
71
+ static isSupportedFormat(definition) {
72
+ return (API.isArazzo(definition) ||
73
+ API.isAsyncAPI(definition) ||
74
+ API.isFlower(definition) ||
75
+ API.isOpenAPI(definition) ||
76
+ API.isOpenAPIOverlay(definition));
77
+ }
68
78
  static async load(path) {
69
79
  const { json, text, yaml } = getJsonSchemaRefParserDefaultOptions().parse;
70
80
  // Not sure why the lib types the parser as potentially
@@ -109,8 +119,17 @@ class API {
109
119
  },
110
120
  })
111
121
  .then(($refs) => {
112
- const values = $refs.values();
113
- return new API(path, values);
122
+ // JSON schema refs parser lib doesn't type the output of this
123
+ // method well (it types it as a generic JSON schema) where as
124
+ // it builds a Map of string (the path/URLs of each reference)
125
+ // to JSONSchema (the reference value)
126
+ //
127
+ // We also change the reference values in our custom parsers
128
+ // defined above to include the raw values which gets “widen”
129
+ // by the lib. We thus need to force the type output to a more
130
+ // precise type.
131
+ const data = $refs.values();
132
+ return new API(path, data);
114
133
  })
115
134
  .catch((error) => {
116
135
  throw new CLIError(error);
@@ -142,36 +161,69 @@ class API {
142
161
  }
143
162
  /* eslint-enable no-await-in-loop */
144
163
  }
164
+ if (API.isArazzo(this.definition)) {
165
+ await this._resolveArazzoSourceDescriptions();
166
+ }
145
167
  const references = [];
146
168
  for (let i = 0; i < this.references.length; i++) {
147
- const reference = this.references[i];
148
- references.push({
149
- content: reference.content,
150
- location: reference.location,
151
- });
169
+ const { content, location, name } = this.references[i];
170
+ references.push({ content, location, name });
152
171
  }
153
172
  return [this.serializeDefinition(outputPath), references];
154
173
  }
155
174
  getSpec(definition) {
175
+ if (API.isArazzo(definition)) {
176
+ return SupportedFormat.arazzo[this.versionWithoutPatch()];
177
+ }
156
178
  if (API.isAsyncAPI(definition)) {
157
179
  return SupportedFormat.asyncapi[this.versionWithoutPatch()];
158
180
  }
181
+ if (API.isFlower(definition)) {
182
+ return SupportedFormat.flower[this.versionWithoutPatch()];
183
+ }
184
+ if (API.isOpenAPI(definition)) {
185
+ return SupportedFormat.openapi[this.versionWithoutPatch()];
186
+ }
159
187
  if (API.isOpenAPIOverlay(definition)) {
160
188
  return { overlay: { type: 'string' } };
161
189
  }
162
- return SupportedFormat.openapi[this.versionWithoutPatch()];
190
+ return undefined;
163
191
  }
164
192
  getSpecName(definition) {
193
+ if (API.isArazzo(definition)) {
194
+ return 'Arazzo';
195
+ }
165
196
  if (API.isAsyncAPI(definition)) {
166
197
  return 'AsyncAPI';
167
198
  }
168
- return 'OpenAPI';
199
+ if (API.isFlower(definition)) {
200
+ return 'Flower';
201
+ }
202
+ if (API.isOpenAPI(definition)) {
203
+ return 'OpenAPI';
204
+ }
205
+ if (API.isOpenAPIOverlay(definition)) {
206
+ return 'OpenAPIOverlay';
207
+ }
208
+ return undefined;
169
209
  }
170
210
  getVersion(definition) {
211
+ if (API.isArazzo(definition)) {
212
+ return definition.arazzo;
213
+ }
171
214
  if (API.isAsyncAPI(definition)) {
172
215
  return definition.asyncapi;
173
216
  }
174
- return (definition.openapi || definition.swagger);
217
+ if (API.isFlower(definition)) {
218
+ return definition.flower;
219
+ }
220
+ if (API.isOpenAPI(definition)) {
221
+ return (definition.openapi || definition.swagger);
222
+ }
223
+ if (API.isOpenAPIOverlay(definition)) {
224
+ return definition.overlay;
225
+ }
226
+ return undefined;
175
227
  }
176
228
  guessFormat(output) {
177
229
  return (output || this.location).endsWith('.json') ? 'json' : 'yaml';
@@ -186,65 +238,100 @@ class API {
186
238
  .join(nodePath?.posix?.sep ?? '/');
187
239
  return path === this.location || path === resolvedAbsLocation;
188
240
  }
189
- resolveContent(values) {
190
- let mainReference = { parsed: {}, raw: '' };
191
- for (const [absPath, reference] of Object.entries(values)) {
241
+ serializeDefinition(outputPath) {
242
+ if (this.overlayedDefinition) {
243
+ const { comments } = parseWithPointers(this.rawDefinition, { attachComments: true });
244
+ const dumpOptions = { comments, lineWidth: Number.POSITIVE_INFINITY, noRefs: true };
245
+ return this.guessFormat(outputPath) === 'json'
246
+ ? JSON.stringify(this.overlayedDefinition)
247
+ : safeStringify(this.overlayedDefinition, dumpOptions);
248
+ }
249
+ return this.rawDefinition;
250
+ }
251
+ versionWithoutPatch() {
252
+ if (!this.version) {
253
+ return '';
254
+ }
255
+ const [major, minor] = this.version.split('.', 3);
256
+ return `${major}.${minor}`;
257
+ }
258
+ async _resolveArazzoSourceDescriptions() {
259
+ if (!API.isArazzo(this.definition)) {
260
+ debug('bump-cli:definition')('This is not an Arazzo definition, no source descriptions to resolve');
261
+ }
262
+ else if (this.definition.sourceDescriptions) {
263
+ const sources = this.definition.sourceDescriptions;
264
+ for (const { name, type: sourceType, url: location } of sources) {
265
+ if (sourceType === 'openapi') {
266
+ const relativeLocation = this._resolveRelativeLocation(location);
267
+ /* eslint-disable no-await-in-loop */
268
+ const api = await API.load(relativeLocation);
269
+ const [content] = await api.extractDefinition();
270
+ /* eslint-enable no-await-in-loop */
271
+ this.references.push({ content, location: relativeLocation, name });
272
+ }
273
+ else {
274
+ debug('bump-cli:definition')(`Arazzo source description of type ${sourceType} is not yet supported.`);
275
+ }
276
+ }
277
+ }
278
+ else {
279
+ debug('bump-cli:definition')("Arazzo definition doesn't have any sourceDescriptions");
280
+ }
281
+ }
282
+ _resolveContentFrom(data) {
283
+ let definition;
284
+ let rawDefinition;
285
+ const references = [];
286
+ // data contains all refs as a map of paths/URLs and their
287
+ // correspond values
288
+ for (const [absPath, reference] of Object.entries(data)) {
192
289
  if (this.isMainRefPath(absPath)) {
193
- // $refs.values is not properly typed so we need to force it
194
- // with the resulting type of our custom defined parser
195
- mainReference = reference;
290
+ ;
291
+ ({ parsed: definition, raw: rawDefinition } = reference);
196
292
  }
197
293
  else {
198
- // $refs.values is not properly typed so we need to force it
199
- // with the resulting type of our custom defined parser
200
- const { raw } = reference;
201
- if (!raw) {
294
+ if (!reference.raw) {
202
295
  throw new UnsupportedFormat(`Reference ${absPath} is empty`);
203
296
  }
204
- this.references.push({
205
- content: raw,
206
- location: this.resolveRelativeLocation(absPath),
297
+ references.push({
298
+ content: reference.raw,
299
+ location: this._resolveRelativeLocation(absPath),
207
300
  });
208
301
  }
209
302
  }
210
- const { parsed, raw } = mainReference;
211
- if (!parsed || !raw || !(parsed instanceof Object) || !('info' in parsed)) {
212
- debug('bump-cli:definition')(`Main location (${this.location}) not found or empty (within ${JSON.stringify(Object.keys(values))})`);
213
- throw new UnsupportedFormat("Definition needs to be an object with at least an 'info' key");
303
+ if (!definition ||
304
+ !rawDefinition ||
305
+ !(definition instanceof Object) ||
306
+ !('info' in definition || 'flower' in definition)) {
307
+ debug('bump-cli:definition')(`Main location (${this.location}) not found or empty (within ${JSON.stringify(Object.keys(data))})`);
308
+ throw new UnsupportedFormat('Definition needs to be a valid Object');
214
309
  }
215
- if (!API.isOpenAPI(parsed) && !API.isAsyncAPI(parsed) && !API.isOpenAPIOverlay(parsed)) {
310
+ if (!API.isSupportedFormat(definition)) {
216
311
  throw new UnsupportedFormat();
217
312
  }
218
- return [raw, parsed];
313
+ return [rawDefinition, definition, references];
219
314
  }
220
- /* Resolve reference absolute paths to the main api location when possible */
221
- resolveRelativeLocation(absPath) {
315
+ /* Resolve reference paths to the main api location when possible */
316
+ _resolveRelativeLocation(path) {
222
317
  const definitionUrl = this.url();
223
- const refUrl = this.url(absPath);
224
- if ((refUrl.hostname === '' && // filesystem path
225
- (/^\//.test(absPath) || // Unix style
226
- /^[A-Za-z]+:[/\\]/.test(absPath))) || // Windows style
227
- (/^https?:\/\//.test(absPath) && definitionUrl.hostname === refUrl.hostname) // Same domain URLs
228
- ) {
229
- const relativeLocation = nodePath.relative(nodePath.dirname(this.location), absPath);
230
- debug('bump-cli:definition')(`Resolved relative $ref location: ${relativeLocation}`);
231
- return relativeLocation;
232
- }
233
- return absPath;
234
- }
235
- serializeDefinition(outputPath) {
236
- if (this.overlayedDefinition) {
237
- const { comments } = parseWithPointers(this.rawDefinition, { attachComments: true });
238
- const dumpOptions = { comments, lineWidth: Number.POSITIVE_INFINITY, noRefs: true };
239
- return this.guessFormat(outputPath) === 'json'
240
- ? JSON.stringify(this.overlayedDefinition)
241
- : safeStringify(this.overlayedDefinition, dumpOptions);
318
+ const refUrl = this.url(path);
319
+ const unixStyle = /^\//.test(path);
320
+ const windowsStyle = /^[A-Za-z]+:[/\\]/.test(path);
321
+ const isUrl = /^https?:\/\//.test(path);
322
+ // Guard: Absolute URL on different domain we return an untouched
323
+ // path
324
+ if (isUrl && definitionUrl.hostname !== refUrl.hostname) {
325
+ return path;
242
326
  }
243
- return this.rawDefinition;
244
- }
245
- versionWithoutPatch() {
246
- const [major, minor] = this.version.split('.', 3);
247
- return `${major}.${minor}`;
327
+ const isAbsolutePath = refUrl.hostname === '' && (unixStyle || windowsStyle);
328
+ // Absolute path or URL on **same domain**
329
+ const isAbsolute = isAbsolutePath || isUrl;
330
+ const relativeLocation = isAbsolute
331
+ ? nodePath.relative(nodePath.dirname(this.location), path)
332
+ : nodePath.join(nodePath.dirname(this.location), path);
333
+ debug('bump-cli:definition')(`Resolved relative $ref location: ${relativeLocation}`);
334
+ return relativeLocation;
248
335
  }
249
336
  url(location = this.location) {
250
337
  try {
package/dist/flags.d.ts CHANGED
@@ -46,4 +46,8 @@ declare const overlay: Interfaces.FlagDefinition<string[], Interfaces.CustomOpti
46
46
  multiple: true;
47
47
  requiredOrDefaulted: false;
48
48
  }>;
49
- export { autoCreate, branch, doc, docName, dryRun, expires, failOnBreaking, filenamePattern, format, hub, interactive, live, open, out, overlay, preview, token, };
49
+ declare const mcpServer: Interfaces.FlagDefinition<string, Interfaces.CustomOptions, {
50
+ multiple: false;
51
+ requiredOrDefaulted: false;
52
+ }>;
53
+ export { autoCreate, branch, doc, docName, dryRun, expires, failOnBreaking, filenamePattern, format, hub, interactive, live, mcpServer, open, out, overlay, preview, token, };
package/dist/flags.js CHANGED
@@ -45,7 +45,7 @@ const token = Flags.custom({
45
45
  if (envToken)
46
46
  return envToken;
47
47
  },
48
- description: 'Documentation or Hub token. Can be provided via BUMP_TOKEN environment variable',
48
+ description: 'Documentation, Hub or Organization token. Can be provided via BUMP_TOKEN environment variable',
49
49
  required: true,
50
50
  });
51
51
  const autoCreate = (opts = {}) => {
@@ -123,4 +123,8 @@ const overlay = Flags.custom({
123
123
  description: 'Path or URL of overlay file(s) to apply before deploying',
124
124
  multiple: true,
125
125
  });
126
- export { autoCreate, branch, doc, docName, dryRun, expires, failOnBreaking, filenamePattern, format, hub, interactive, live, open, out, overlay, preview, token, };
126
+ const mcpServer = Flags.custom({
127
+ char: 'm',
128
+ description: 'MCP server id or slug',
129
+ });
130
+ export { autoCreate, branch, doc, docName, dryRun, expires, failOnBreaking, filenamePattern, format, hub, interactive, live, mcpServer, open, out, overlay, preview, token, };
@@ -14,7 +14,8 @@
14
14
  "Deploy a new version of an existing documentation\n\n$ bump deploy FILE --doc <your_doc_id_or_slug> --token <your_doc_token>\n* Let's deploy on Bump.sh... done\n* Your new documentation version will soon be ready\n",
15
15
  "Deploy a new version of an existing documentation attached to a hub\n\n$ bump deploy FILE --doc <doc_slug> --hub <your_hub_id_or_slug> --token <your_doc_token>\n* Let's deploy on Bump.sh... done\n* Your new documentation version will soon be ready\n",
16
16
  "Deploy a whole directory of API definitions files to a hub\n\n$ bump deploy DIR --filename-pattern *-{slug}-api --hub <hub_slug> --token <hub_token>\nWe've found 2 valid API definitions to deploy\n└─ DIR\n └─ source-my-service-api.yml (OpenAPI spec version 3.1.0)\n └─ source-my-jobs-service-api.yml (AsyncAPI spec version 2.6.0)\n\nLet's deploy those documentations to your <hub_slug> hub on Bump.sh\n\n* Your new documentation version will soon be ready\nLet's deploy a new version to your my-service documentation on Bump.sh... done\n\n* Your new documentation version will soon be ready\nLet's deploy a new version to your my-jobs-service documentation on Bump.sh... done\n",
17
- "Validate a new documentation version before deploying it\n\n$ bump deploy FILE --dry-run --doc <doc_slug> --token <your_doc_token>\n* Let's validate on Bump.sh... done\n* Definition is valid\n"
17
+ "Validate a new documentation version before deploying it\n\n$ bump deploy FILE --dry-run --doc <doc_slug> --token <your_doc_token>\n* Let's validate on Bump.sh... done\n* Definition is valid\n",
18
+ "Deploy a new workflow document of an existing MCP server\n\n$ bump deploy FILE --mcp-server <your_mcp_server_id_or_slug> --token <your_organization_token>\n* Let's deploy on Bump.sh... done\n* Your <your_mcp_server_id_or_slug> MCP server... has received a new workflow definition which will soon be ready.\n"
18
19
  ],
19
20
  "flags": {
20
21
  "auto-create": {
@@ -84,6 +85,14 @@
84
85
  "allowNo": false,
85
86
  "type": "boolean"
86
87
  },
88
+ "mcp-server": {
89
+ "char": "m",
90
+ "description": "MCP server id or slug",
91
+ "name": "mcp-server",
92
+ "hasDynamicHelp": false,
93
+ "multiple": false,
94
+ "type": "option"
95
+ },
87
96
  "overlay": {
88
97
  "char": "o",
89
98
  "description": "Path or URL of overlay file(s) to apply before deploying",
@@ -101,7 +110,7 @@
101
110
  },
102
111
  "token": {
103
112
  "char": "t",
104
- "description": "Documentation or Hub token. Can be provided via BUMP_TOKEN environment variable",
113
+ "description": "Documentation, Hub or Organization token. Can be provided via BUMP_TOKEN environment variable",
105
114
  "name": "token",
106
115
  "required": true,
107
116
  "default": "99f52837852249b328c0a00249f846a3",
@@ -203,7 +212,7 @@
203
212
  },
204
213
  "token": {
205
214
  "char": "t",
206
- "description": "Documentation or Hub token. Can be provided via BUMP_TOKEN environment variable",
215
+ "description": "Documentation, Hub or Organization token. Can be provided via BUMP_TOKEN environment variable",
207
216
  "name": "token",
208
217
  "required": false,
209
218
  "default": "99f52837852249b328c0a00249f846a3",
@@ -294,5 +303,5 @@
294
303
  "strict": true
295
304
  }
296
305
  },
297
- "version": "2.9.11"
306
+ "version": "2.10.0"
298
307
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bump-cli",
3
3
  "description": "The Bump CLI is used to interact with your API documentation hosted on Bump.sh by using the API of developers.bump.sh",
4
- "version": "2.9.11",
4
+ "version": "2.10.0",
5
5
  "author": "Paul Bonaud <paulr@bump.sh>",
6
6
  "bin": {
7
7
  "bump": "./bin/run.js"
@@ -14,8 +14,8 @@
14
14
  "@types/debug": "^4.1.12",
15
15
  "@types/jsonpath": "^0.2.4",
16
16
  "@types/mocha": "^10",
17
- "@types/node": "^24",
18
- "@types/sinon": "^17.0.3",
17
+ "@types/node": "^25",
18
+ "@types/sinon": "^21.0.0",
19
19
  "@typescript-eslint/eslint-plugin": "^8.13.0",
20
20
  "chai": "^6.2.2",
21
21
  "eslint": "^8",
@@ -92,7 +92,7 @@
92
92
  "dependencies": {
93
93
  "@apidevtools/json-schema-ref-parser": "^11.7.2",
94
94
  "@asyncapi/specs": "^6.8.0",
95
- "@clack/prompts": "^0.11.0",
95
+ "@clack/prompts": "^1.0.0",
96
96
  "@oclif/core": "^4",
97
97
  "@oclif/plugin-help": "^6",
98
98
  "@oclif/plugin-warn-if-update-available": "^3.1.20",
@@ -103,6 +103,6 @@
103
103
  "debug": "^4.3.7",
104
104
  "jsonpathly": "^3.0.0",
105
105
  "mergician": "^2.0.2",
106
- "open": "^10.1.0"
106
+ "open": "^11.0.0"
107
107
  }
108
108
  }