beer-swagger 1.1.1 → 4.0.2

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 (2) hide show
  1. package/generate-apis.js +231 -52
  2. package/package.json +1 -1
package/generate-apis.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  import fs from 'fs';
4
4
  import path from 'path';
5
- import { createInterface } from 'readline';
5
+ import {createInterface} from 'readline';
6
6
 
7
7
  const ENV_PATH = path.join(process.cwd(), '.env');
8
8
  const SKIPPED_SERVICES = new Set([
@@ -116,7 +116,7 @@ function appendParamSuffix(baseName, paramSuffix) {
116
116
  }
117
117
 
118
118
  // Add a verb when the HTTP method should be reflected in the name.
119
- function addVerbForSingleSegment(name, method, hasPathParams = false) {
119
+ function addVerbForSingleSegment(name, method) {
120
120
  const lower = name.toLowerCase();
121
121
  if (KEEP_METHOD_NAMES.has(lower)) {
122
122
  return name;
@@ -125,7 +125,7 @@ function addVerbForSingleSegment(name, method, hasPathParams = false) {
125
125
  const suffix = name[0].toUpperCase() + name.slice(1);
126
126
  return `removeBy${suffix}`;
127
127
  }
128
- if ((method === 'post' || method === 'put') && !hasPathParams) {
128
+ if (method === 'post' || method === 'put') {
129
129
  return name;
130
130
  }
131
131
  const verbMap = {
@@ -166,12 +166,12 @@ function getBodyTypeName(requestBody, schemas) {
166
166
  if (!requestBody || !requestBody.content) {
167
167
  return '';
168
168
  }
169
- const { content } = requestBody;
169
+ const {content} = requestBody;
170
170
  const firstContent = content['application/json'] || content['application/*+json'] || Object.values(content)[0];
171
171
  if (!firstContent || !firstContent.schema) {
172
172
  return '';
173
173
  }
174
- const { schema } = firstContent;
174
+ const {schema} = firstContent;
175
175
  if (!schema.$ref) {
176
176
  return '';
177
177
  }
@@ -182,10 +182,53 @@ function getBodyTypeName(requestBody, schemas) {
182
182
  return stripped || normalized;
183
183
  }
184
184
 
185
+ // Return the path segment immediately above the operation segment.
186
+ function getParentPathSegment(pathStr) {
187
+ const segments = pathStr.split('/')
188
+ .filter(Boolean)
189
+ .filter((part) => !part.startsWith('{'));
190
+ return segments.length > 1 ? segments[segments.length - 2] : '';
191
+ }
192
+
193
+ // Compare two names using normalized Levenshtein similarity.
194
+ function nameSimilarity(left, right) {
195
+ const a = String(left || '').replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
196
+ const b = String(right || '').replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
197
+ if (!a || !b) {
198
+ return 0;
199
+ }
200
+ if (a === b) {
201
+ return 1;
202
+ }
203
+ const previous = Array.from({length: b.length + 1}, (_, index) => index);
204
+ for (let i = 1; i <= a.length; i += 1) {
205
+ let diagonal = previous[0];
206
+ previous[0] = i;
207
+ for (let j = 1; j <= b.length; j += 1) {
208
+ const above = previous[j];
209
+ previous[j] = a[i - 1] === b[j - 1]
210
+ ? diagonal
211
+ : Math.min(previous[j] + 1, previous[j - 1] + 1, diagonal + 1);
212
+ diagonal = above;
213
+ }
214
+ }
215
+ return 1 - previous[b.length] / Math.max(a.length, b.length);
216
+ }
217
+
218
+ // A POST whose parent resource matches its defined request entity is a save.
219
+ function shouldSaveByPathEntity(pathStr, method, requestBody, schemas) {
220
+ if (method !== 'post') {
221
+ return false;
222
+ }
223
+ const parentPath = getParentPathSegment(pathStr);
224
+ const bodyTypeName = getBodyTypeName(requestBody, schemas);
225
+ return !!parentPath && !!bodyTypeName && nameSimilarity(parentPath, bodyTypeName) >= 0.8;
226
+ }
227
+
185
228
  // Map a Swagger schema to a runtime return type.
186
229
  function mapSchemaToType(schema, schemas, useTypes) {
187
230
  if (!schema) {
188
- return '{}';
231
+ return 'object';
189
232
  }
190
233
  const {
191
234
  $ref,
@@ -201,18 +244,18 @@ function mapSchemaToType(schema, schemas, useTypes) {
201
244
  if (responseSchema && responseSchema.properties && responseSchema.properties.data) {
202
245
  return mapSchemaToType(responseSchema.properties.data, schemas, useTypes);
203
246
  }
204
- return '{}';
247
+ return 'object';
205
248
  }
206
249
  const refName = normalizeDtoName(rawRefName);
207
250
  const refSchema = schemas[rawRefName] || schemas[refName];
208
251
  if (refSchema && refSchema.properties && refSchema.properties.data) {
209
252
  return mapSchemaToType(refSchema.properties.data, schemas, useTypes);
210
253
  }
211
- return useTypes ? `Types.${refName}` : '{}';
254
+ return useTypes ? `Types.${refName}` : 'object';
212
255
  }
213
256
  if (!type && items) {
214
257
  const itemType = mapSchemaToType(items, schemas, useTypes);
215
- if (itemType === '{}' || itemType === '[]') {
258
+ if (itemType === '[]') {
216
259
  return '[]';
217
260
  }
218
261
  return `${itemType}[]`;
@@ -234,7 +277,7 @@ function mapSchemaToType(schema, schemas, useTypes) {
234
277
  }
235
278
  if (type === 'array') {
236
279
  const itemType = mapSchemaToType(items, schemas, useTypes);
237
- if (itemType === '{}' || itemType === '[]') {
280
+ if (itemType === '[]') {
238
281
  return '[]';
239
282
  }
240
283
  return `${itemType}[]`;
@@ -246,20 +289,20 @@ function mapSchemaToType(schema, schemas, useTypes) {
246
289
  }
247
290
 
248
291
  // Map parameter schema to a type for method signatures.
249
- function mapParamSchemaToType(schema, schemas, useTypes) {
292
+ function mapParamSchemaToType(schema, schemas, useTypes, isQuery = false) {
250
293
  if (!schema) {
251
- return '{}';
294
+ return isQuery ? 'Record<string, string | undefined>' : 'object';
252
295
  }
253
296
  const {
254
297
  $ref,
255
298
  type,
256
299
  format
257
300
  } = schema;
258
- const { items } = schema;
301
+ const {items} = schema;
259
302
  if ($ref) {
260
303
  const refName = normalizeDtoName($ref.split('/')
261
304
  .pop());
262
- return useTypes ? `Types.${refName}` : '{}';
305
+ return useTypes ? `Types.${refName}` : 'object';
263
306
  }
264
307
  if (!type && items) {
265
308
  return '[]';
@@ -283,26 +326,50 @@ function mapParamSchemaToType(schema, schemas, useTypes) {
283
326
  return '[]';
284
327
  }
285
328
  if (type === 'object') {
286
- return 'object';
329
+ return isQuery ? 'Record<string, string | undefined>' : 'object';
287
330
  }
288
- return 'object';
331
+ return isQuery ? 'Record<string, string | undefined>' : 'object';
332
+ }
333
+
334
+ // Resolve parameter type for a parameter definition.
335
+ function resolveParamType(param, schemas) {
336
+ const schemaToUse = param.schema || (param.type ? {
337
+ type: param.type,
338
+ format: param.format,
339
+ items: param.items
340
+ } : null);
341
+ const isQuery = param.in === 'query';
342
+ return mapParamSchemaToType(schemaToUse, schemas, true, isQuery);
343
+ }
344
+
345
+ // Detect event streams in successful or default responses.
346
+ function isEventStreamResponse(op) {
347
+ return Object.entries(op.responses || {}).some(([status, response]) => {
348
+ if (!/^2(?:\d{2}|XX)$/i.test(status) && status !== 'default') {
349
+ return false;
350
+ }
351
+ return Object.keys(response.content || {}).some((mediaType) => {
352
+ const type = mediaType.split(';')[0].trim().toLowerCase();
353
+ return type === 'text/event-stream' || type === 'event-stream';
354
+ });
355
+ });
289
356
  }
290
357
 
291
358
  // Resolve default response type from Swagger responses.
292
359
  function getDefaultReturnType(op, schemas, useTypes) {
293
- const { responses = {} } = op;
360
+ const {responses = {}} = op;
294
361
  const res = responses['200'] || responses['201'] || responses.default || {};
295
- const { content = {} } = res;
362
+ const {content = {}} = res;
296
363
  const firstContent = content['application/json'] || content['application/*+json'] || Object.values(content)[0];
297
364
  if (!firstContent) {
298
- return '{}';
365
+ return 'object';
299
366
  }
300
367
  return mapSchemaToType(firstContent.schema, schemas, useTypes);
301
368
  }
302
369
 
303
370
  // Collect query/path parameters.
304
371
  function getParams(op) {
305
- const { parameters = [] } = op;
372
+ const {parameters = []} = op;
306
373
  const params = Array.isArray(parameters) ? parameters : [];
307
374
  return params.filter((p) => p.in === 'query' || p.in === 'path');
308
375
  }
@@ -401,15 +468,28 @@ function toPascal(value) {
401
468
  return value[0].toUpperCase() + value.slice(1);
402
469
  }
403
470
 
471
+ // Join path prefixes while keeping a single separator at their boundary.
472
+ function joinPathPrefixes(...prefixes) {
473
+ return prefixes
474
+ .map((prefix) => typeof prefix === 'string' ? prefix.trim() : '')
475
+ .filter(Boolean)
476
+ .reduce((result, prefix) => {
477
+ if (!result) {
478
+ return prefix;
479
+ }
480
+ return `${result.replace(/\/+$/, '')}/${prefix.replace(/^\/+/, '')}`;
481
+ }, '');
482
+ }
483
+
404
484
  // Generate API file content for a service.
405
- function generateForSpec(spec, serviceName, fileBase, isDefaultFetch) {
485
+ function generateForSpec(spec, serviceName, fileBase, isDefaultFetch, responsePathPrefix = '') {
406
486
  const {
407
487
  components = {},
408
488
  tags: specTags = [],
409
489
  paths = {}
410
490
  } = spec;
411
- const defaultPathPrefix = typeof spec.pathPrefix === 'string' ? spec.pathPrefix : '';
412
- const { schemas = {} } = components;
491
+ const defaultPathPrefix = joinPathPrefixes(responsePathPrefix, spec.pathPrefix);
492
+ const {schemas = {}} = components;
413
493
  const tagDescriptions = {};
414
494
  if (Array.isArray(specTags)) {
415
495
  for (const tag of specTags) {
@@ -455,8 +535,8 @@ function generateForSpec(spec, serviceName, fileBase, isDefaultFetch) {
455
535
  const pathSegments = p.split('/')
456
536
  .filter(Boolean)
457
537
  .filter((part) => !part.startsWith('{'));
458
- const shouldAddVerb = pathSegments.length === 1 || ((method === 'post' || method === 'put') && pathParams.length > 0);
459
- name = shouldAddVerb ? addVerbForSingleSegment(inferred, method, pathParams.length > 0) : inferred;
538
+ const shouldAddVerb = pathSegments.length === 1;
539
+ name = shouldAddVerb ? addVerbForSingleSegment(inferred, method) : inferred;
460
540
  }
461
541
  if (!KEEP_METHOD_NAMES.has(name.toLowerCase())) {
462
542
  name = fixVerbCase(name);
@@ -498,6 +578,11 @@ function generateForSpec(spec, serviceName, fileBase, isDefaultFetch) {
498
578
  const className = toClassName(tag);
499
579
  const facadePrefix = toFacadePrefix(className);
500
580
  const sortedEntries = [...entries].sort((a, b) => {
581
+ const aDynamic = a.params.some((p) => p.in === 'path') || a.path.includes('{') ? 1 : 0;
582
+ const bDynamic = b.params.some((p) => p.in === 'path') || b.path.includes('{') ? 1 : 0;
583
+ if (aDynamic !== bDynamic) {
584
+ return aDynamic - bDynamic;
585
+ }
501
586
  const aScore = (a.hasBody ? 1 : 0) + (a.params.length ? 1 : 0);
502
587
  const bScore = (b.hasBody ? 1 : 0) + (b.params.length ? 1 : 0);
503
588
  return aScore - bScore;
@@ -518,21 +603,33 @@ function generateForSpec(spec, serviceName, fileBase, isDefaultFetch) {
518
603
  if (!KEEP_METHOD_NAMES.has(name.toLowerCase()) && (name === 'get' || name === 'delete') && facadePrefix) {
519
604
  name = `${name}${facadePrefix}`;
520
605
  }
606
+ const isDynamicPath = entryParams.some((p) => p.in === 'path') || entryPath.includes('{');
607
+ const saveByPathEntity = shouldSaveByPathEntity(
608
+ entryPath,
609
+ entryMethod,
610
+ entryHasBody ? entryOp.requestBody : null,
611
+ schemas
612
+ );
613
+ if (saveByPathEntity || (seen.has(name) && isDynamicPath && (entryMethod === 'post' || entryMethod === 'put'))) {
614
+ const hasSavePrefix = name.startsWith('save') && (name.length === 4 || /^[A-Z]/.test(name.slice(4)));
615
+ name = hasSavePrefix ? name : `save${toPascal(name)}`;
616
+ }
617
+ const baseName = name;
521
618
  if (seen.has(name)) {
522
619
  const bodyTypeName = entryHasBody ? getBodyTypeName(entryOp.requestBody, schemas) : '';
523
620
  if (bodyTypeName) {
524
- name = `${entryName}By${toPascal(bodyTypeName)}`;
621
+ name = `${baseName}By${toPascal(bodyTypeName)}`;
525
622
  }
526
623
  }
527
624
  if (seen.has(name)) {
528
625
  const paramSuffix = toParamSuffix(entryParams, paramNameMap);
529
626
  if (paramSuffix) {
530
- name = appendParamSuffix(entryName, paramSuffix);
627
+ name = appendParamSuffix(baseName, paramSuffix);
531
628
  }
532
629
  }
533
630
  if (seen.has(name)) {
534
631
  const methodSuffix = entryMethod[0].toUpperCase() + entryMethod.slice(1);
535
- name = `${entryName}${methodSuffix}`;
632
+ name = `${baseName}${methodSuffix}`;
536
633
  }
537
634
  seen.add(name);
538
635
  const defaultType = entryDefaultReturnType;
@@ -543,20 +640,19 @@ function generateForSpec(spec, serviceName, fileBase, isDefaultFetch) {
543
640
 
544
641
  const paramArgs = orderedParams.map((p) => {
545
642
  const {
546
- schema: paramSchema,
547
643
  required,
548
644
  name: paramName
549
645
  } = p;
550
646
  const mappedName = paramNameMap.get(paramName) || paramName;
551
- const paramType = mapParamSchemaToType(paramSchema, schemas, true);
647
+ const paramType = resolveParamType(p, schemas);
552
648
  const optional = required ? '' : '?';
553
649
  return `${mappedName}${optional}: ${paramType}`;
554
650
  });
555
651
 
556
- let bodyType = '{}';
557
- const { requestBody: opRequestBody } = entryOp;
652
+ let bodyType = 'object';
653
+ const {requestBody: opRequestBody} = entryOp;
558
654
  if (entryHasBody && opRequestBody && opRequestBody.content) {
559
- const { content } = opRequestBody;
655
+ const {content} = opRequestBody;
560
656
  const firstContent = content['application/json'] || content['application/*+json'] || Object.values(content)[0];
561
657
  if (firstContent && firstContent.schema) {
562
658
  bodyType = mapSchemaToType(firstContent.schema, schemas, true);
@@ -605,17 +701,43 @@ function generateForSpec(spec, serviceName, fileBase, isDefaultFetch) {
605
701
  methods.push(' */');
606
702
  }
607
703
 
608
- const queryParamNames = entryParams.filter((p) => p.in === 'query')
609
- .map((p) => {
704
+ const queryParams = entryParams.filter((p) => p.in === 'query');
705
+ let paramsObject = 'undefined';
706
+ if (queryParams.length === 1) {
707
+ const p = queryParams[0];
708
+ const paramType = resolveParamType(p, schemas);
709
+ const mappedName = paramNameMap.get(p.name) || p.name;
710
+ if (paramType === 'Record<string, string | undefined>') {
711
+ paramsObject = mappedName;
712
+ } else {
713
+ const key = formatObjectKey(p.name);
714
+ const prop = mappedName === p.name ? key : `${key}: ${mappedName}`;
715
+ paramsObject = `{ ${prop} }`;
716
+ }
717
+ } else if (queryParams.length > 1) {
718
+ const entries = queryParams.map((p) => {
719
+ const paramType = resolveParamType(p, schemas);
610
720
  const mappedName = paramNameMap.get(p.name) || p.name;
721
+ if (paramType === 'Record<string, string | undefined>') {
722
+ return `...${mappedName}`;
723
+ }
611
724
  const key = formatObjectKey(p.name);
612
725
  return mappedName === p.name ? key : `${key}: ${mappedName}`;
613
726
  });
614
- const paramsObject = queryParamNames.length ? `{ ${queryParamNames.join(', ')} }` : 'undefined';
727
+ paramsObject = `{ ${entries.join(', ')} }`;
728
+ }
615
729
 
616
730
  const pathTemplate = withPathParams(entryPath, entryParams, paramNameMap);
617
731
  const pathLiteral = pathTemplate.includes('${') ? `\`${pathTemplate}\`` : `'${pathTemplate}'`;
618
732
 
733
+ if (isEventStreamResponse(entryOp)) {
734
+ methods.push(` public ${name}(${sigParts}): EventSource {`);
735
+ methods.push(` return this.sse(${pathLiteral}, ${paramsObject});`);
736
+ methods.push(' }');
737
+ methods.push('');
738
+ continue;
739
+ }
740
+
619
741
  if (entryMethod === 'get') {
620
742
  methods.push(` public async ${name}<T = ${defaultType}>(${sigParts}): Promise<JsonResponse<T>> {`);
621
743
  methods.push(` return await this.get<T>(${pathLiteral}, ${paramsObject});`);
@@ -733,7 +855,7 @@ function generateForSpec(spec, serviceName, fileBase, isDefaultFetch) {
733
855
  // Map Swagger schema to a TS type for DTO output.
734
856
  function mapSchemaToTsType(schema, schemas) {
735
857
  if (!schema) {
736
- return '{}';
858
+ return 'object';
737
859
  }
738
860
  const {
739
861
  $ref,
@@ -751,7 +873,7 @@ function mapSchemaToTsType(schema, schemas) {
751
873
  if (refSchema && refSchema.properties && refSchema.properties.data) {
752
874
  return mapSchemaToTsType(refSchema.properties.data, schemas);
753
875
  }
754
- return '{}';
876
+ return 'object';
755
877
  }
756
878
  return normalizeDtoName(refName);
757
879
  }
@@ -763,6 +885,9 @@ function mapSchemaToTsType(schema, schemas) {
763
885
  return schemaEnum.map((v) => (typeof v === 'string' ? `'${v}'` : String(v)))
764
886
  .join(' | ');
765
887
  }
888
+ if (type === 'any') {
889
+ return 'any';
890
+ }
766
891
  if (type === 'string') {
767
892
  return 'string';
768
893
  }
@@ -784,17 +909,44 @@ function mapSchemaToTsType(schema, schemas) {
784
909
  }
785
910
  if (type === 'object') {
786
911
  if (additionalProperties) {
787
- return 'Record<string, {}>';
912
+ return 'Record<string, object>';
788
913
  }
789
- return '{}';
914
+ return 'object';
790
915
  }
791
- return '{}';
916
+ return 'object';
917
+ }
918
+
919
+ // Swagger treats schemas without a type constraint as any.
920
+ function isAnySchema(schema) {
921
+ if (schema === true || schema === null || schema === undefined) {
922
+ return true;
923
+ }
924
+ if (typeof schema !== 'object' || Array.isArray(schema)) {
925
+ return false;
926
+ }
927
+ if (schema.type === 'any') {
928
+ return true;
929
+ }
930
+ const typeConstraints = [
931
+ '$ref',
932
+ 'type',
933
+ 'items',
934
+ 'enum',
935
+ 'const',
936
+ 'properties',
937
+ 'additionalProperties',
938
+ 'allOf',
939
+ 'oneOf',
940
+ 'anyOf',
941
+ 'not'
942
+ ];
943
+ return !typeConstraints.some((key) => schema[key] !== undefined);
792
944
  }
793
945
 
794
946
  // Generate DTO/type definitions file content.
795
947
  function generateTypes(spec) {
796
- const { components = {} } = spec;
797
- const { schemas = {} } = components;
948
+ const {components = {}} = spec;
949
+ const {schemas = {}} = components;
798
950
  const lines = [];
799
951
  lines.push('/*');
800
952
  lines.push(' * This file is auto-generated. Do not edit manually.');
@@ -829,9 +981,16 @@ function generateTypes(spec) {
829
981
  const def = typeDefs.get(normalizedName);
830
982
  const requiredSet = new Set(required || []);
831
983
  for (const [prop, propSchema] of Object.entries(properties)) {
832
- def.props[prop] = mapSchemaToTsType(propSchema, schemas);
984
+ if (isAnySchema(propSchema)) {
985
+ continue;
986
+ }
987
+ const propType = mapSchemaToTsType(propSchema, schemas);
988
+ if (propType === 'any') {
989
+ continue;
990
+ }
991
+ def.props[prop] = propType;
833
992
  def.required[prop] = requiredSet.has(prop);
834
- const { description } = propSchema || {};
993
+ const {description} = propSchema || {};
835
994
  const desc = description
836
995
  ? description.replace(/\n/g, ' ')
837
996
  .trim()
@@ -883,6 +1042,26 @@ async function fetchJson(url) {
883
1042
  return response.json();
884
1043
  }
885
1044
 
1045
+ // Fetch JSON together with response headers needed by API generation.
1046
+ async function fetchJsonWithHeaders(url) {
1047
+ console.log(`Fetching ${url}`);
1048
+ const response = await fetch(url);
1049
+ if (!response.ok) {
1050
+ throw new Error(`Failed to fetch ${url}: ${response.status}`);
1051
+ }
1052
+ const responseText = await response.text();
1053
+ if (responseText === '') {
1054
+ return {
1055
+ data: {},
1056
+ pathPrefix: response.headers.get('x-path-prefix') || ''
1057
+ };
1058
+ }
1059
+ return {
1060
+ data: JSON.parse(responseText),
1061
+ pathPrefix: response.headers.get('x-path-prefix') || ''
1062
+ };
1063
+ }
1064
+
886
1065
  // Read key/value pairs from .env without extra dependencies.
887
1066
  function readEnvFile() {
888
1067
  if (!fs.existsSync(ENV_PATH)) {
@@ -911,7 +1090,7 @@ function readEnvFile() {
911
1090
  // Persist values into .env (overwrite or append keys).
912
1091
  function writeEnvFile(nextEnv) {
913
1092
  const existing = readEnvFile();
914
- const merged = { ...existing, ...nextEnv };
1093
+ const merged = {...existing, ...nextEnv};
915
1094
  const lines = Object.entries(merged)
916
1095
  .map(([key, value]) => `${key}=${value}`);
917
1096
  fs.writeFileSync(ENV_PATH, `${lines.join('\n')}\n`, 'utf8');
@@ -946,7 +1125,7 @@ async function promptForMissing(env) {
946
1125
  rl.question(q, resolve);
947
1126
  });
948
1127
 
949
- const result = { ...env };
1128
+ const result = {...env};
950
1129
  if (!result.SWAGGER_BASE_URL) {
951
1130
  result.SWAGGER_BASE_URL = (await ask('BASE_URL: ')).trim();
952
1131
  }
@@ -970,7 +1149,7 @@ async function main() {
970
1149
  SWAGGER_BASE_URL: baseUrl,
971
1150
  SWAGGER_OUT_DIRECTORY: directory,
972
1151
  IS_DEFAULT_FETCH: isDefaultFetch
973
- } = { ...process.env, ...envFile };
1152
+ } = {...process.env, ...envFile};
974
1153
  isDefaultFetch = normalizeBoolean(isDefaultFetch);
975
1154
 
976
1155
  if (!baseUrl || !directory || isDefaultFetch === undefined) {
@@ -1015,10 +1194,10 @@ async function main() {
1015
1194
  }
1016
1195
 
1017
1196
  if (!fs.existsSync(outputDirectory)) {
1018
- fs.mkdirSync(outputDirectory, { recursive: true });
1197
+ fs.mkdirSync(outputDirectory, {recursive: true});
1019
1198
  }
1020
1199
  if (!fs.existsSync(typesDirectory)) {
1021
- fs.mkdirSync(typesDirectory, { recursive: true });
1200
+ fs.mkdirSync(typesDirectory, {recursive: true});
1022
1201
  }
1023
1202
 
1024
1203
  for (const service of services) {
@@ -1033,9 +1212,9 @@ async function main() {
1033
1212
  continue;
1034
1213
  }
1035
1214
  const specUrl = `${baseUrl}${url}`;
1036
- const spec = await fetchJson(specUrl);
1215
+ const {data: spec, pathPrefix} = await fetchJsonWithHeaders(specUrl);
1037
1216
  const fileBase = toFileBase(serviceName);
1038
- const content = generateForSpec(spec, serviceName, fileBase, isDefaultFetch);
1217
+ const content = generateForSpec(spec, serviceName, fileBase, isDefaultFetch, pathPrefix);
1039
1218
  const typesContent = generateTypes(spec);
1040
1219
 
1041
1220
  const outPath = path.join(outputDirectory, `${fileBase}.ts`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beer-swagger",
3
- "version": "1.1.1",
3
+ "version": "4.0.2",
4
4
  "bin": {
5
5
  "beer-swagger": "./generate-apis.js"
6
6
  },