@accelbyte/codegen 1.0.0-alpha.9 → 1.0.0-beta.11

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.
@@ -7,8 +7,8 @@ var zod = require('zod');
7
7
  var fs = require('fs');
8
8
  var path = require('path');
9
9
  var SwaggerParser = require('@apidevtools/swagger-parser');
10
- var _ = require('lodash');
11
10
  var fastJsonPatch = require('fast-json-patch');
11
+ var _ = require('lodash');
12
12
  var https = require('https');
13
13
 
14
14
  function _interopNamespaceDefault(e) {
@@ -74,17 +74,55 @@ class CliParser {
74
74
  static isAdmin = () => {
75
75
  return CliParser.instance().argv.admin;
76
76
  };
77
+ static getSnippetOutputPath = () => {
78
+ return CliParser.instance().argv.snippetOutput;
79
+ };
77
80
  }
78
81
 
79
- const getImportableVarMap = () => ({
82
+ const getImportableVarMap$1 = () => ({
80
83
  "@accelbyte/sdk": ["CodeGenUtil", "SdkCache", "IResponse", "IResponseWithSync", "Validate"],
81
84
  axios: ["AxiosRequestConfig", "AxiosResponse"],
82
85
  zod: ["z"]
83
86
  });
84
- const makeNewImportVarMap = () => ({
87
+ const makeNewImportVarMap$1 = () => ({
85
88
  axios: ["AxiosInstance"],
86
89
  "@accelbyte/sdk": ["SDKRequestConfig"]
87
90
  });
91
+ const generateImports$1 = (body, importStatements) => {
92
+ const usedImportVarMap = makeNewImportVarMap$1();
93
+ const importableVarMap = getImportableVarMap$1();
94
+ for (const [moduleSource, importableVars] of Object.entries(importableVarMap)) {
95
+ for (const importableVar of importableVars) {
96
+ const importVarRegex = new RegExp(`(?<![\\d\\w_])${importableVar}(?![\\d\\w_])`);
97
+ if (body.match(importVarRegex)) {
98
+ usedImportVarMap[moduleSource] = [...usedImportVarMap[moduleSource] || [], importableVar];
99
+ }
100
+ }
101
+ }
102
+ const generatedImports = Object.keys(usedImportVarMap).sort().map((moduleSource) => `import { ${usedImportVarMap[moduleSource].sort().join(", ")} } from '${moduleSource}'`).join("\n");
103
+ return `${generatedImports}
104
+ ${importStatements.sort().join("\n")}`;
105
+ };
106
+ const templateClass = (className, body, importStatements) => {
107
+ return `/**
108
+ * AUTO GENERATED
109
+ */
110
+ ${generateImports$1(body, importStatements)}
111
+
112
+ export class ${className} {
113
+ // @ts-ignore
114
+ constructor(private axiosInstance: AxiosInstance, private namespace: string, private cache = false) {}
115
+ ${body}
116
+ }
117
+ `;
118
+ };
119
+
120
+ const getImportableVarMap = () => ({
121
+ "@accelbyte/sdk": ["CodeGenUtil", "SdkCache", "IResponse", "IResponseWithSync", "Validate", "ApiArgs", "Network", "AccelbyteSDK"]
122
+ });
123
+ const makeNewImportVarMap = () => ({
124
+ "@accelbyte/sdk": ["AccelbyteSDK", "ApiArgs", "ApiUtils"]
125
+ });
88
126
  const generateImports = (body, importStatements) => {
89
127
  const usedImportVarMap = makeNewImportVarMap();
90
128
  const importableVarMap = getImportableVarMap();
@@ -100,27 +138,55 @@ const generateImports = (body, importStatements) => {
100
138
  return `${generatedImports}
101
139
  ${importStatements.sort().join("\n")}`;
102
140
  };
103
- const templateClass = (className, body, importStatements) => `/**
104
- * DON'T EDIT THIS FILE, it is AUTO GENERATED
105
- */
106
- ${generateImports(body, importStatements)}
141
+ const templateApiClass = (className, body, importStatements, returnMethods) => {
142
+ return `/**
143
+ * AUTO GENERATED
144
+ */
145
+ /* eslint-disable camelcase */
146
+ ${generateImports(body, importStatements)}
107
147
 
108
- export class ${className} {
109
- // @ts-ignore
110
- constructor(private axiosInstance: AxiosInstance, private namespace: string, private cache = false) {}
148
+ export function ${className}(sdk: AccelbyteSDK, args?: ApiArgs) {
149
+ const sdkAssembly = sdk.assembly()
150
+
151
+ const namespace = args?.namespace ? args?.namespace : sdkAssembly.namespace
152
+ const cache = args?.cache ? args?.cache : sdkAssembly.cache
153
+ const requestConfig = ApiUtils.mergedConfigs(sdkAssembly.config, args)
111
154
  ${body}
155
+
156
+ return {
157
+ ${returnMethods}
112
158
  }
159
+ }
113
160
  `;
161
+ };
114
162
 
115
- const templateJsdocFile = (apiName, body) => `
116
- \`\`\`
117
- SDK.getService('${apiName}', authHeader):
118
- ${body}
119
-
120
- \`\`\`
121
- `.replace(/, \)/g, ")").trim();
122
-
163
+ const UNDEFINED_SWAGGER_SEMVER = "0.0.0";
164
+ const REMOVED_KEYWORDS = [
165
+ "/admin/",
166
+ "/public/",
167
+ "/v1/",
168
+ "/v2/",
169
+ "/v3/",
170
+ "/v4/",
171
+ "/v5/",
172
+ "/namespace/",
173
+ "/namespaces/",
174
+ "/{namespace}/"
175
+ ];
123
176
  class ParserUtils {
177
+ static replaceAll = (string, search, replace) => {
178
+ return string.split(search).join(replace);
179
+ };
180
+ static generateClassName = (tag) => {
181
+ const className = _.upperFirst(_.camelCase(tag));
182
+ const classGenName = CliParser.isAdmin() ? className + "Admin$" : className + "$";
183
+ return { className, classGenName };
184
+ };
185
+ static generateApiName = (tag) => {
186
+ const apiName = _.upperFirst(_.camelCase(tag));
187
+ const apiGenName = CliParser.isAdmin() ? apiName + "AdminApi" : apiName + "Api";
188
+ return { apiName, apiGenName };
189
+ };
124
190
  static parseQueryParamAttributeDefault = (definition) => {
125
191
  const attrName = definition.name.slice(definition.name.lastIndexOf(".") + 1);
126
192
  const defaultValue = definition.type === "string" ? `'${definition.default}'` : definition.default;
@@ -138,17 +204,26 @@ class ParserUtils {
138
204
  return pathParam.type;
139
205
  };
140
206
  static parseQueryParamsType = (queryParams) => {
141
- return queryParams.map((queryParam) => ParserUtils.parseAttributeType(queryParam)).join(",");
207
+ return queryParams.map((queryParam) => ParserUtils.parseAttributeType(queryParam)).join(", ");
142
208
  };
143
209
  static isAnyQueryParamRequired = (queryParams) => {
144
210
  return queryParams.some((queryParam) => queryParam.required);
145
211
  };
212
+ static convertDashesToTitleCase = (str) => {
213
+ const result = str.split("-").map((word, index) => {
214
+ if (index === 0) {
215
+ return word.charAt(0).toUpperCase() + word.slice(1);
216
+ }
217
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
218
+ }).join("");
219
+ return result;
220
+ };
146
221
  static parseQueryParamsDefault = (queryParams) => {
147
222
  const result = queryParams.filter((queryParam) => !!queryParam.default && !queryParam.required).map(ParserUtils.parseQueryParamAttributeDefault).join(",");
148
223
  return result ? `${result},` : "";
149
224
  };
150
225
  static parseBodyParamsImports = (bodyParams) => {
151
- return bodyParams.map(ParserUtils.parseRefImport).filter(Boolean);
226
+ return bodyParams.map((bodyParams2) => ParserUtils.parseRefImport(bodyParams2)).filter(Boolean);
152
227
  };
153
228
  static parseImportDir = ($ref) => {
154
229
  let ref = $ref.replace(".", "/");
@@ -167,7 +242,7 @@ class ParserUtils {
167
242
  return null;
168
243
  }
169
244
  const type = ParserUtils.parseRefType($ref);
170
- return `import { ${type} } from './definitions/${type}'`;
245
+ return `import { ${type} } from '../definitions/${type}'`;
171
246
  };
172
247
  static parseRefType = ($ref) => {
173
248
  let ref = $ref.replace(".", "/");
@@ -261,34 +336,59 @@ class ParserUtils {
261
336
  }
262
337
  return parameters.filter((parameter) => parameter.in === "path");
263
338
  }
264
- static generateClassMethod({
265
- path: path2,
266
- endpoint,
267
- httpMethod,
268
- className
269
- }) {
270
- let replacedIdsPath = path2;
271
- if (endpoint.parameters) {
272
- for (const parameter of endpoint.parameters) {
273
- if (parameter.in === "path") {
274
- replacedIdsPath = replacedIdsPath.replace("{" + parameter.name + "}", "By" + ParserUtils.toTitleCaseWord(parameter.name));
275
- replacedIdsPath = replacedIdsPath.replace("/iam", "");
276
- replacedIdsPath = replacedIdsPath.replace("/odin-config", "");
277
- }
339
+ static generateNaturalLangMethod = ({ servicePrefix, path: path2, httpMethod, isForm, existingMethods }) => {
340
+ let path_ = path2;
341
+ path_ = path_.replace(`/${servicePrefix}/`, "/");
342
+ REMOVED_KEYWORDS.forEach((prefix) => {
343
+ path_ = path_.replace(prefix, "/");
344
+ });
345
+ path_ = path_.substring(1);
346
+ const isPlural = httpMethod === "get" && !(path2.slice(-1) === "}");
347
+ if (!isPlural) {
348
+ path_ = ParserUtils.replaceAll(path_, "ies/", "y/");
349
+ path_ = ParserUtils.replaceAll(path_, "s/", "/");
350
+ if (path_.indexOf("status") < 0) {
351
+ path_ = path_.replace(/ies$/, "y");
352
+ path_ = path_.replace(/s$/, "");
278
353
  }
279
354
  }
280
- let classMethod = httpMethod + "/" + replacedIdsPath;
281
- classMethod = _.camelCase(classMethod);
282
- classMethod = classMethod.replace("PublicNamespacesByNamespace", "Ns");
283
- classMethod = classMethod.replace("AdminNamespacesByNamespace", "AdminNs");
284
- const searchWord = "NamespacesByNamespace";
285
- const nsExistInsideMethod = classMethod.indexOf(searchWord) > 0 && classMethod.indexOf(searchWord) + searchWord.length < classMethod.length;
286
- const excludedClasses = ["Policies"];
287
- if (nsExistInsideMethod && !excludedClasses.includes(className)) {
288
- classMethod = classMethod.replace(searchWord, "");
289
- }
290
- return classMethod;
291
- }
355
+ const arrLastWords = path_.split("}/");
356
+ let lastWords = arrLastWords[arrLastWords.length - 1];
357
+ const extractLastWord = (lastWords_) => {
358
+ let res = lastWords_;
359
+ res = res.split("/{")[0];
360
+ return res;
361
+ };
362
+ lastWords = extractLastWord(lastWords);
363
+ if (lastWords.indexOf("{") >= 0 && arrLastWords.length > 1) {
364
+ lastWords = arrLastWords[arrLastWords.length - 2];
365
+ lastWords = extractLastWord(lastWords);
366
+ }
367
+ const listBeforeLastWords = [];
368
+ let foundParam = false;
369
+ const listByParams = [];
370
+ const pathElements = path_.split("/");
371
+ pathElements.slice().reverse().forEach((item) => {
372
+ if (item.indexOf("}") >= 0) {
373
+ foundParam = true;
374
+ let param = item.replace("{", "");
375
+ param = param.replace("}", "");
376
+ param = "Byword" + _.upperFirst(param);
377
+ listByParams.push(param);
378
+ } else if (!foundParam) {
379
+ if (lastWords.indexOf(item) === -1) {
380
+ listBeforeLastWords.push(item);
381
+ }
382
+ } else {
383
+ foundParam = false;
384
+ }
385
+ });
386
+ const genPath = _.upperFirst(lastWords) + "/" + listBeforeLastWords.join("/") + listByParams.reverse().join("/");
387
+ let generatedMethod = _.camelCase(mappedMethod(httpMethod, isForm) + genPath);
388
+ generatedMethod = ParserUtils.replaceAll(generatedMethod, "Byword", "_By");
389
+ generatedMethod = resolveConflicts(path2, generatedMethod, existingMethods);
390
+ return generatedMethod;
391
+ };
292
392
  static filterBodyParams(parameters) {
293
393
  if (Array.isArray(parameters) && parameters.length > 0) {
294
394
  return parameters.filter((parameter) => parameter.in === "body" || parameter.in === "formData");
@@ -310,9 +410,22 @@ class ParserUtils {
310
410
  const fileContent = templateClass(apiName, apiBuffer, imports);
311
411
  fs.writeFileSync(`${distDir}/${apiName}.ts`, ParserUtils.prependCopyrightHeader(fileContent));
312
412
  }
313
- static writeJsdocFile(distDir, nameArray, apiBuffer) {
314
- const jsdocFile = templateJsdocFile(nameArray[0], apiBuffer);
315
- fs.writeFileSync(`${distDir}/docs/${nameArray[0]}.md`, jsdocFile);
413
+ static writeApiFile(distDir, apiName, apiBuffer, imports, returnMethods) {
414
+ const newImports = [];
415
+ imports.forEach((el, index) => {
416
+ newImports.push(el.replace("../definitions", "./definitions"));
417
+ });
418
+ const fileContent = templateApiClass(apiName, apiBuffer, newImports, returnMethods);
419
+ fs.writeFileSync(`${distDir}/${apiName}.ts`, ParserUtils.prependCopyrightHeader(fileContent));
420
+ }
421
+ static writeApiMainFile(distDir, serviceName, fileContent) {
422
+ fs.writeFileSync(`${distDir}/${serviceName}.ts`, ParserUtils.prependCopyrightHeader(fileContent));
423
+ }
424
+ static writeSnippetFile(distDir, name, docBuffer) {
425
+ let snippetFileName = ParserUtils.replaceAll(name, " ", "-").toLowerCase();
426
+ snippetFileName = snippetFileName.replace("justice-", "");
427
+ snippetFileName = "snippet-" + snippetFileName + ".json";
428
+ fs.writeFileSync(`${distDir}/${snippetFileName}`, docBuffer);
316
429
  }
317
430
  static writeDefinitionFile(distDir, name, buffer) {
318
431
  ParserUtils.mkdirIfNotExist(distDir);
@@ -322,6 +435,37 @@ class ParserUtils {
322
435
  ParserUtils.mkdirIfNotExist(distDir);
323
436
  fs.writeFileSync(path.join(distDir, `all-${isAdminWebSdk ? "admin" : "public"}-imports.ts`), ParserUtils.prependCopyrightHeader(buffer));
324
437
  }
438
+ static syncChangelog(packageVersion) {
439
+ const currDir = process.cwd();
440
+ const pathToChangelog = path.join(currDir, "./CHANGELOG.md");
441
+ let fileContent = fs.readFileSync(pathToChangelog, "utf-8");
442
+ const date = new Date().toISOString().slice(0, 10);
443
+ fileContent = "### " + packageVersion + " - " + date + `
444
+
445
+ - code-generated update
446
+
447
+ ` + fileContent.trim();
448
+ fs.writeFileSync(pathToChangelog, fileContent, "utf-8");
449
+ }
450
+ static syncPackageVersion(apiInfo, isAdminWebSdk) {
451
+ if (isAdminWebSdk) {
452
+ return;
453
+ }
454
+ const currDir = process.cwd();
455
+ const { packageJSON, pathToPackageJSON } = ParserUtils.getPackageJSONInfo(currDir);
456
+ let swaggerVersion = apiInfo.version ? apiInfo.version : UNDEFINED_SWAGGER_SEMVER;
457
+ swaggerVersion = Number(swaggerVersion.replace(".", "").replace(".", ""));
458
+ swaggerVersion = isNaN(swaggerVersion) ? 0 : swaggerVersion;
459
+ const semver = packageJSON.version.split(".").map((numStr) => Number(numStr));
460
+ const newSemver = [semver[0], swaggerVersion, ++semver[2]];
461
+ packageJSON.version = newSemver[0] + "." + newSemver[1] + "." + newSemver[2];
462
+ fs.writeFileSync(pathToPackageJSON, JSON.stringify(packageJSON, null, 2));
463
+ ParserUtils.syncChangelog(packageJSON.version);
464
+ }
465
+ static getPackageJSONInfo(dir) {
466
+ const pathToPackageJSON = path.join(dir, "./package.json");
467
+ return { packageJSON: JSON.parse(fs.readFileSync(pathToPackageJSON, "utf-8")), pathToPackageJSON };
468
+ }
325
469
  static toCamelCase(str) {
326
470
  return str.split("/").map(function(word, index) {
327
471
  if (index === 0) {
@@ -355,6 +499,29 @@ class ParserUtils {
355
499
  }
356
500
  const swaggerContent = JSON.parse(fs.readFileSync(swaggerFilePath, "utf8"));
357
501
  const swaggerPatchFileContent = JSON.parse(fs.readFileSync(possibleSwaggerPatchFilePath, "utf8"));
502
+ for (const patchEntry of swaggerPatchFileContent) {
503
+ const segments = patchEntry.path.split("/").filter(Boolean);
504
+ let currentNode = swaggerContent;
505
+ let aggregatedPath = "";
506
+ for (let i = 0; i < segments.length; i++) {
507
+ const segment = segments[i];
508
+ aggregatedPath += `/${segment}`;
509
+ const effectiveSegment = segment.replace(/(~1)/g, "/").replace(/(~0)/g, "~");
510
+ if (!currentNode[effectiveSegment]) {
511
+ if (i + 1 === segments.length && patchEntry.op === "add") ; else {
512
+ throw new Error([
513
+ `JSON patch error: operation "${patchEntry.op}" on path "${aggregatedPath}" fails because the path doesn't exist in ${swaggerFilePath}. This may be caused by:
514
+ `,
515
+ "1. The related service has patched the service, so patch is no longer needed.",
516
+ "2. There is a breaking change on the service that causes the path to change.\n",
517
+ `In any case, revisit this file: "${possibleSwaggerPatchFilePath}", then try again.
518
+ `
519
+ ].join("\n"));
520
+ }
521
+ }
522
+ currentNode = currentNode[effectiveSegment];
523
+ }
524
+ }
358
525
  const { newDocument } = fastJsonPatch.applyPatch(swaggerContent, swaggerPatchFileContent);
359
526
  fs.writeFileSync(swaggerPatchedFilePath, JSON.stringify(newDocument, null, 2));
360
527
  }
@@ -372,6 +539,67 @@ class ParserUtils {
372
539
  ${content}`;
373
540
  };
374
541
  }
542
+ const mappedMethod = (httpMethod, isForm) => {
543
+ if (httpMethod === "get") {
544
+ return "get";
545
+ } else if (httpMethod === "post" && isForm) {
546
+ return "post";
547
+ } else if (httpMethod === "post") {
548
+ return "create";
549
+ } else if (httpMethod === "put") {
550
+ return "update";
551
+ } else if (httpMethod === "patch") {
552
+ return "patch";
553
+ } else if (httpMethod === "delete") {
554
+ return "delete";
555
+ }
556
+ };
557
+ const resolveConflicts = (path2, generatedMethod, existingMethods) => {
558
+ try {
559
+ testConflict(path2, generatedMethod, existingMethods);
560
+ } catch (e) {
561
+ if (path2.indexOf("/namespaces/") >= 0) {
562
+ generatedMethod += "_ByNS";
563
+ }
564
+ }
565
+ try {
566
+ testConflict(path2, generatedMethod, existingMethods);
567
+ } catch (e) {
568
+ if (path2.indexOf("/v4/") >= 0) {
569
+ generatedMethod += "_v4";
570
+ }
571
+ }
572
+ try {
573
+ testConflict(path2, generatedMethod, existingMethods);
574
+ } catch (e) {
575
+ if (path2.indexOf("/v3/") >= 0) {
576
+ generatedMethod += "_v3";
577
+ }
578
+ }
579
+ try {
580
+ testConflict(path2, generatedMethod, existingMethods);
581
+ } catch (e) {
582
+ if (path2.indexOf("/v2/") >= 0) {
583
+ generatedMethod += "_v2";
584
+ }
585
+ }
586
+ try {
587
+ testConflict(path2, generatedMethod, existingMethods);
588
+ } catch (e) {
589
+ if (path2.indexOf("/admin/") >= 0) {
590
+ generatedMethod += "_admin";
591
+ }
592
+ }
593
+ testConflict(path2, generatedMethod, existingMethods);
594
+ return generatedMethod;
595
+ };
596
+ const testConflict = (path2, generatedMethod, existingMethods) => {
597
+ if (existingMethods[generatedMethod]) {
598
+ const conflictingMethod = { path: path2, generatedMethod };
599
+ throw Error(`Duplicate method conflict in ${JSON.stringify(conflictingMethod)},
600
+ existingMethods: ${JSON.stringify(existingMethods, null, 2)}`);
601
+ }
602
+ };
375
603
 
376
604
  const Schema = zod.z.object({
377
605
  $ref: zod.z.string().nullish(),
@@ -463,50 +691,6 @@ zod.z.object({
463
691
  }).nullish()
464
692
  });
465
693
 
466
- const templateJsdocMethod = ({
467
- classMethod,
468
- httpMethod,
469
- path,
470
- pathParams,
471
- bodyParams,
472
- queryParams
473
- }) => {
474
- let jsdoc = "";
475
- let methodSignature = "";
476
- let newPath = path;
477
- for (const p of pathParams) {
478
- methodSignature += p.name + ", ";
479
- newPath = newPath.replace("{" + p.name + "}", `' + ${p.name} + '`);
480
- jsdoc += `
481
- * @pathParam '${p.name}${p.required ? "" : "?"}' - ${p.description}`;
482
- }
483
- let payloads = "";
484
- for (const p of bodyParams) {
485
- methodSignature += p.name + ", ";
486
- payloads += p.name;
487
- jsdoc += `
488
- * @payload '${p.name}${p.required ? "" : "?"}' - ${p.description} ${p?.schema?.properties ? "=> " + JSON.stringify(p.schema.properties) : ""}`;
489
- }
490
- for (const p of queryParams) {
491
- const optionalMark = p.required ? "" : "?";
492
- methodSignature += p.name + optionalMark + ", ";
493
- jsdoc += `
494
- * @queryParam '${p.name}${p.required ? "" : "?"}' - ${p.description}`;
495
- }
496
- let queryString = "";
497
- for (const p of queryParams) {
498
- queryString += `${p.name}=__&`;
499
- }
500
- queryString = queryString.length > 0 ? "?" + queryString : "";
501
- return `
502
- /**
503
- * ${httpMethod.toUpperCase()} '${newPath}${queryString}' ${payloads ? "payload: '" + payloads + "'" : ""}
504
- * ${jsdoc}
505
- */
506
- ${classMethod}(${methodSignature}): Promise
507
- `;
508
- };
509
-
510
694
  const templateMethod = ({
511
695
  classMethod,
512
696
  description,
@@ -518,13 +702,17 @@ const templateMethod = ({
518
702
  isFormUrlEncoded,
519
703
  responseClass
520
704
  }) => {
521
- let methodSignature = "";
705
+ let methodParams = "";
706
+ let methodParamsNoTypes = "";
522
707
  let newPath = `'${path}'`;
523
- let dependencies = [];
708
+ let importStatements = [];
709
+ let snippetString = "";
710
+ let snippetMethodSignature = "";
524
711
  for (const pathParam of pathParams) {
525
712
  const type = ParserUtils.parseType(pathParam);
526
713
  if (pathParam.name !== "namespace") {
527
- methodSignature += pathParam.name + `:${type}, `;
714
+ methodParams += pathParam.name + `:${type}, `;
715
+ methodParamsNoTypes += pathParam.name + ", ";
528
716
  }
529
717
  const pName = pathParam.name === "namespace" ? "this.namespace" : pathParam.name;
530
718
  if (path.match(`{${pathParam.name}}`)) {
@@ -538,8 +726,16 @@ const templateMethod = ({
538
726
  let dataType = null;
539
727
  if (httpMethod !== "get") {
540
728
  dataType = ParserUtils.parseBodyParamsType(bodyParams);
541
- dependencies = ParserUtils.parseBodyParamsImports(bodyParams);
542
- methodSignature += dataType ? `data: ${dataType},` : "";
729
+ importStatements = ParserUtils.parseBodyParamsImports(bodyParams);
730
+ methodParams += dataType ? `data: ${dataType},` : "";
731
+ methodParamsNoTypes += dataType ? `data,` : "";
732
+ const snippetParams = bodyParams?.map((ob) => {
733
+ return ` <span class='sn-purple'>${ob.name}</span>`;
734
+ });
735
+ snippetMethodSignature += snippetParams ? `data: { ${snippetParams} }` : "";
736
+ bodyParams?.map((ob) => {
737
+ return ` <span class='sn-purple'>"${ob.name}": ""</span>`;
738
+ });
543
739
  }
544
740
  const isAnyRequired = ParserUtils.isAnyQueryParamRequired(queryParams);
545
741
  const queryParamsType = queryParams.length ? `queryParams${isAnyRequired ? "" : "?"}: {${ParserUtils.parseQueryParamsType(queryParams)}}` : "";
@@ -549,7 +745,7 @@ const templateMethod = ({
549
745
  let dataPayload = "{params}";
550
746
  const descriptionText = description ? `
551
747
  /**
552
- * ${(description || "").replace(/\n/g, "\n * ")}
748
+ * ${description.replace(/\n/g, "\n * ")}
553
749
  */` : "";
554
750
  let formPayloadString = "";
555
751
  if (isFormUrlEncoded) {
@@ -561,47 +757,57 @@ const templateMethod = ({
561
757
  } else if (isDelete) {
562
758
  dataPayload = dataType ? `{data, params}` : "{params}";
563
759
  }
564
- const isFileUpload = methodSignature.indexOf("data: {file") > -1;
760
+ const isFileUpload = methodParams.indexOf("data: {file") > -1;
565
761
  const resolvedResponseClass = responseClass || "unknown";
566
762
  const resolvedResponseClassValidated = responseClass || "z.unknown()";
567
- const parameters = (queryParamsType ? `${methodSignature} ${queryParamsType}` : methodSignature).replace(/,\s*$/, "");
763
+ methodParams = (queryParamsType ? `${methodParams} ${queryParamsType}` : methodParams).replace(/,\s*$/, "");
764
+ methodParamsNoTypes = queryParamsType ? `${methodParamsNoTypes} queryParams` : methodParamsNoTypes;
568
765
  let methodImpl = "";
569
766
  const isCacheFetch = ["get"].includes(httpMethod) && resolvedResponseClass !== "unknown";
570
- const cachedFetchMethod = classMethod.replace("get", "fetch");
767
+ const cachedFetchMethod = classMethod;
571
768
  const deprecateTag = isCacheFetch ? `/**
572
- * @deprecated Use "${cachedFetchMethod}()" instead.
769
+ * @deprecated Use "${classMethod}()" instead.
573
770
  */` : "";
574
771
  const isGuardInvoked = ["get", "post", "put", "patch", "delete"].includes(httpMethod);
575
772
  const methodName = httpMethod === "get" ? cachedFetchMethod : ["post", "put", "patch", "delete"].includes(httpMethod) ? classMethod : "";
576
- const methodGenerics = resolvedResponseClass !== "unknown" ? `<T = ${resolvedResponseClass}>` : "";
577
- const responseType = resolvedResponseClass !== "unknown" ? `T` : "unknown";
773
+ const responseType = resolvedResponseClass !== "unknown" ? `${resolvedResponseClass}` : "unknown";
774
+ const generateMethodName = () => `${methodName}(${methodParams}): Promise<${responseSyncType}<${responseType}>>`;
775
+ const generateSnippetMethodName = () => `${methodName}(${snippetMethodSignature})`;
776
+ snippetString += `${generateSnippetMethodName()}`;
578
777
  const responseSyncType = httpMethod === "get" ? "IResponseWithSync" : ["post", "put", "patch", "delete"].includes(httpMethod) ? "IResponse" : "";
579
778
  methodImpl = `${descriptionText}
580
- ${methodName}${methodGenerics}(${parameters}): Promise<${responseSyncType}<${responseType}>> {
779
+ ${generateMethodName()} {
581
780
  ${queryParamsDefault}
582
781
  const url = ${newPath} ${formPayloadString} ${isFileUpload ? "\n// TODO file upload not implemented" : ""}
583
782
  const resultPromise = this.axiosInstance.${httpMethod}(url, ${dataPayload})
584
783
 
585
- ${httpMethod === "get" ? `const res = () => Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})
784
+ ${httpMethod === "get" ? ` const res = () => Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})
586
785
 
587
786
  if (!this.cache) {
588
787
  return SdkCache.withoutCache(res)
589
788
  }
590
789
  const cacheKey = url + CodeGenUtil.hashCode(JSON.stringify({ params }))
591
- return SdkCache.withCache(cacheKey, res)` : ""}${["post", "put", "patch", "delete"].includes(httpMethod) ? `return Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})` : ""}
790
+ return SdkCache.withCache(cacheKey, res)` : ""}${["post", "put", "patch", "delete"].includes(httpMethod) ? ` return Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})` : ""}
592
791
  }
593
792
  `;
594
793
  if (!isGuardInvoked) {
595
794
  methodImpl = `${descriptionText}
596
795
  ${deprecateTag}
597
- TODO_${classMethod}<T = ${resolvedResponseClass}>(${parameters}): Promise<AxiosResponse<T>> {
796
+ TODO_${classMethod}(${methodParams}): Promise<AxiosResponse<${resolvedResponseClass}>> {
598
797
  ${queryParamsDefault}
599
798
  const url = ${newPath} ${formPayloadString} ${isFileUpload ? "\n// TODO file upload not implemented" : ""}
600
799
  return this.axiosInstance.${httpMethod}(url, ${dataPayload})
601
800
  }
602
801
  `;
603
802
  }
604
- return [methodImpl, dependencies];
803
+ const res = {
804
+ methodImpl,
805
+ methodParams,
806
+ methodParamsNoTypes,
807
+ importStatements,
808
+ snippetString
809
+ };
810
+ return res;
605
811
  };
606
812
 
607
813
  class TemplateZod {
@@ -617,32 +823,32 @@ class TemplateZod {
617
823
  }
618
824
  let imports = "";
619
825
  for (const cl of Array.from(this.importClasses).sort()) {
620
- imports += ` import { ${cl} } from './${cl}'
826
+ imports += `import { ${cl} } from './${cl}'
621
827
  `;
622
828
  }
623
829
  let exportedVariableString;
624
830
  let exportedTypeString;
625
831
  if (containsRecursiveType) {
626
832
  exportedVariableString = `
627
- export const ${fileName}: z.ZodType<${fileName}> = z.lazy(() =>
833
+ export const ${fileName}: z.ZodType<${fileName}> = z.lazy(() =>
628
834
  ${content.schemaString}
629
835
  )
630
836
  `;
631
837
  exportedTypeString = `
632
- export type ${fileName} = {
838
+ export interface ${fileName} {
633
839
  ${content.typeString}
634
- }
635
- `;
840
+ }
841
+ `;
636
842
  } else {
637
843
  exportedVariableString = `export const ${fileName} = ${content.schemaString}`;
638
- exportedTypeString = `export type ${fileName} = z.TypeOf<typeof ${fileName}>`;
844
+ exportedTypeString = `export interface ${fileName} extends z.TypeOf<typeof ${fileName}> {}`;
639
845
  }
640
846
  const template = `import { z } from 'zod'
641
- ${imports}
847
+ ${imports}
642
848
 
643
- ${exportedVariableString}
849
+ ${exportedVariableString}
644
850
 
645
- ${exportedTypeString}
851
+ ${exportedTypeString}
646
852
  `;
647
853
  return { buffer: template, duplicateFound: this.duplicateFound };
648
854
  };
@@ -727,7 +933,25 @@ class TemplateZod {
727
933
  typeString: refType
728
934
  };
729
935
  } else if (items) {
730
- model2 = this.parseEnumItems(items);
936
+ if (items.type === "array") {
937
+ const ref3 = items.items?.$ref;
938
+ if (ref3) {
939
+ const refType = ParserUtils.parseRefType(ref3);
940
+ this.importClasses.add(refType);
941
+ model2 = {
942
+ schemaString: refType,
943
+ typeString: refType
944
+ };
945
+ } else if (items.items) {
946
+ model2 = this.parseEnumItems(items.items);
947
+ }
948
+ return {
949
+ schemaString: `${schemaAttribute} z.array(z.array(${model2.schemaString}))${schemaRequired}`,
950
+ typeString: `${typeAttribute} ${model2.typeString}[]${typeNullishability}`
951
+ };
952
+ } else {
953
+ model2 = this.parseEnumItems(items);
954
+ }
731
955
  } else {
732
956
  return {
733
957
  schemaString: `${schemaAttribute} z.array(z.any())${schemaRequired}`,
@@ -799,11 +1023,11 @@ class TemplateZodArray {
799
1023
  render = (name) => {
800
1024
  const cls = name.replace("Array", "");
801
1025
  const template = `import { z } from 'zod'
802
- import { ${cls} } from './${cls}'
1026
+ import { ${cls} } from './${cls}'
803
1027
 
804
- export const ${name} = z.array(${cls})
1028
+ export const ${name} = z.array(${cls})
805
1029
 
806
- export type ${name} = z.TypeOf<typeof ${name}>
1030
+ export interface ${name} extends z.TypeOf<typeof ${name}> {}
807
1031
  `;
808
1032
  return template;
809
1033
  };
@@ -838,24 +1062,147 @@ const extractEnumObject = (type, isRequired, enumArr) => {
838
1062
  };
839
1063
  };
840
1064
 
1065
+ const templateApiMethod = ({
1066
+ classMethod,
1067
+ description,
1068
+ httpMethod,
1069
+ path,
1070
+ pathParams,
1071
+ bodyParams,
1072
+ queryParams,
1073
+ responseClass,
1074
+ classGenName,
1075
+ methodParams,
1076
+ methodParamsNoTypes
1077
+ }) => {
1078
+ let methodSignature = "";
1079
+ let newPath = `'${path}'`;
1080
+ let dependencies = [];
1081
+ let snippetSdk = "";
1082
+ let snippetShell = "";
1083
+ for (const pathParam of pathParams) {
1084
+ const type = ParserUtils.parseType(pathParam);
1085
+ if (pathParam.name !== "namespace") {
1086
+ methodSignature += pathParam.name + `:${type}, `;
1087
+ }
1088
+ const pName = pathParam.name === "namespace" ? "this.namespace" : pathParam.name;
1089
+ if (path.match(`{${pathParam.name}}`)) {
1090
+ if (type === "string") {
1091
+ newPath = `${newPath}.replace('{${pathParam.name}}', ${pName})`;
1092
+ } else {
1093
+ newPath = `${newPath}.replace('{${pathParam.name}}', String(${pName}))`;
1094
+ }
1095
+ }
1096
+ }
1097
+ snippetShell = `<span class='sn-blue'>curl</span> --location --request <span class='sn-blue'>${httpMethod}</span> '<span class='sn-green'>__DOMAIN__${path}</span>' --header 'accept: application/json'`;
1098
+ let dataType = null;
1099
+ if (httpMethod !== "get") {
1100
+ dataType = ParserUtils.parseBodyParamsType(bodyParams);
1101
+ dependencies = ParserUtils.parseBodyParamsImports(bodyParams);
1102
+ methodSignature += dataType ? `data: ${dataType},` : "";
1103
+ bodyParams?.map((ob) => {
1104
+ return ` <span class='sn-purple'>${ob.name}</span>`;
1105
+ });
1106
+ const curlParams = bodyParams?.map((ob) => {
1107
+ return ` <span class='sn-purple'>"${ob.name}": ""</span>`;
1108
+ });
1109
+ snippetShell += ` --data-raw { ${curlParams}}`;
1110
+ }
1111
+ const isAnyRequired = ParserUtils.isAnyQueryParamRequired(queryParams);
1112
+ queryParams.length ? `queryParams${isAnyRequired ? "" : "?"}: {${ParserUtils.parseQueryParamsType(queryParams)}}` : "";
1113
+ queryParams.length ? `const params = {${ParserUtils.parseQueryParamsDefault(queryParams)} ...queryParams} as SDKRequestConfig` : "const params = {} as SDKRequestConfig";
1114
+ description ? `
1115
+ /**
1116
+ * ${description.replace(/\n/g, "\n * ")}
1117
+ */` : "";
1118
+ const resolvedResponseClass = responseClass || "unknown";
1119
+ const responseType = resolvedResponseClass !== "unknown" ? `${resolvedResponseClass}` : "unknown";
1120
+ const methodImpl = `
1121
+ async function ${classMethod}(${methodParams}): Promise<${responseType}> {
1122
+ const $ = new ${classGenName}(Network.create(requestConfig), namespace, cache)
1123
+ const resp = await $.${classMethod}(${methodParamsNoTypes})
1124
+ if (resp.error) throw resp.error
1125
+ return resp.response.data
1126
+ }
1127
+ `;
1128
+ snippetSdk += `${classMethod}(${methodParams}) // return Promise<${responseType}>`;
1129
+ return [methodImpl, dependencies, snippetSdk, snippetShell];
1130
+ };
1131
+
1132
+ const templateApiIndex = (serviceName, serviceNameTitle, apiList) => {
1133
+ let imports = "";
1134
+ let returnStatement = "";
1135
+ for (const cl of apiList) {
1136
+ imports += `
1137
+ import { ${cl} } from './${serviceName}/${cl}'`;
1138
+ returnStatement += `
1139
+ ${cl}, `;
1140
+ }
1141
+ return `/**
1142
+ * AUTO GENERATED
1143
+ */
1144
+ ${imports}
1145
+
1146
+ const apis = {
1147
+ ${returnStatement}
1148
+ }
1149
+
1150
+ export const ${ParserUtils.convertDashesToTitleCase(serviceNameTitle)} = apis
1151
+ `;
1152
+ };
1153
+
1154
+ const templateSdkSnippet = (serviceNameTitle, apiName, methodSnippet) => {
1155
+ const sdkSnippet = `
1156
+ import { Accelbyte } from '@accelbyte/sdk'
1157
+ import { ${serviceNameTitle} } from '@accelbyte/sdk-${serviceNameTitle.toLowerCase()}'
1158
+
1159
+ const SDK_CONFIG = {
1160
+ baseURL: 'https://demo.accelbyte.io',
1161
+ clientId: '77f88506b6174c3ea4d925f5b4096ce8',
1162
+ namespace: 'accelbyte',
1163
+ redirectURI: 'http://localhost:3030'
1164
+ }
1165
+
1166
+ const sdk = Accelbyte.SDK({
1167
+ options: SDK_CONFIG
1168
+ })
1169
+
1170
+ ${serviceNameTitle}.${apiName}(sdk).${methodSnippet}
1171
+ `;
1172
+ return sdkSnippet;
1173
+ };
1174
+
841
1175
  class CodeGenerator {
842
1176
  static getPatchedDir = () => path.join(CliParser.getSwaggersOutputPath(), "patched");
843
1177
  static getGeneratedPublicFolder = () => `${CliParser.getOutputPath()}/generated-public`;
844
1178
  static getGeneratedAdminFolder = () => `${CliParser.getOutputPath()}/generated-admin`;
845
- static iterateApi = async (api) => {
1179
+ static getGeneratedSnippetsFolder = () => `${CliParser.getSnippetOutputPath()}/generated-snippets`;
1180
+ static getServicePrefix = (servicePaths) => servicePaths[servicePaths.length - 1].split("/")[1];
1181
+ static iterateApi = async (api, serviceName) => {
846
1182
  const apiBufferByTag = {};
847
- const jsDocApiBufferByTag = {};
1183
+ const apiArgumentsByTag = {};
1184
+ const classBufferByTag = {};
848
1185
  const dependenciesByTag = {};
849
1186
  const classImports = {};
850
1187
  let arrayDefinitions = [];
851
- for (const path2 in api.paths) {
1188
+ const snippetMap = {};
1189
+ const mapClassMethods = {};
1190
+ const sortedPathsByLength = new Map(Object.entries(api.paths).sort((a, b) => {
1191
+ if (a[0].length === b[0].length) {
1192
+ return a[0].localeCompare(b[0]);
1193
+ } else {
1194
+ return a[0].length - b[0].length;
1195
+ }
1196
+ }));
1197
+ const sortedKeys = Array.from(sortedPathsByLength.keys());
1198
+ const servicePrefix = CodeGenerator.getServicePrefix(sortedKeys);
1199
+ for (const [path2, operation] of sortedPathsByLength) {
852
1200
  const isAdminEndpoint = path2.indexOf("/admin/") > -1;
853
1201
  if (CliParser.isAdmin() && !isAdminEndpoint) {
854
1202
  continue;
855
1203
  } else if (!CliParser.isAdmin() && isAdminEndpoint) {
856
1204
  continue;
857
1205
  }
858
- const operation = api.paths[path2];
859
1206
  const httpMethods = Object.keys(operation);
860
1207
  for (const httpMethod of httpMethods) {
861
1208
  const endpoint = await Endpoint.parseAsync(operation[httpMethod]).catch((error) => {
@@ -865,59 +1212,98 @@ class CodeGenerator {
865
1212
  if (!endpoint.tags) {
866
1213
  continue;
867
1214
  }
1215
+ if (endpoint.deprecated) {
1216
+ continue;
1217
+ }
868
1218
  const [tag] = endpoint.tags;
869
- const description = endpoint.description;
870
- const isDeprecated = endpoint.deprecated;
1219
+ mapClassMethods[tag] = mapClassMethods[tag] ? mapClassMethods[tag] : {};
1220
+ const isForm = endpoint.consumes && endpoint.consumes[0] === "application/x-www-form-urlencoded";
1221
+ const classMethod = ParserUtils.generateNaturalLangMethod({
1222
+ servicePrefix,
1223
+ path: path2,
1224
+ httpMethod,
1225
+ isForm,
1226
+ existingMethods: mapClassMethods[tag]
1227
+ });
1228
+ mapClassMethods[tag][classMethod] = `${path2} ${httpMethod}`;
1229
+ snippetMap[path2] = snippetMap[path2] ? snippetMap[path2] : {};
1230
+ let description = endpoint.description;
1231
+ description = description || "";
1232
+ description = description.replace(/\s+/g, " ");
871
1233
  const responseClass = ParserUtils.get2xxResponse(endpoint.responses);
872
- const className = _.upperFirst(_.camelCase(tag));
1234
+ const { className, classGenName } = ParserUtils.generateClassName(tag);
873
1235
  classImports[className] = classImports[className] ? classImports[className] : {};
874
- if (!isDeprecated) {
875
- if (responseClass) {
876
- const importTypeClass = ParserUtils.parseRefType(responseClass);
877
- classImports[className][importTypeClass] = `import { ${importTypeClass} } from './definitions/${importTypeClass}'`;
878
- }
879
- if (responseClass && responseClass.endsWith("Array")) {
880
- arrayDefinitions.push(responseClass);
881
- }
882
- const isFormUrlEncoded = ParserUtils.isFormUrlEncoded(httpMethod, endpoint.consumes);
883
- const queryParams = ParserUtils.filterQueryParameters(endpoint.parameters);
884
- const pathParams = ParserUtils.filterPathParams(endpoint.parameters);
885
- let bodyParams = ParserUtils.filterBodyParams(endpoint.parameters);
886
- const classMethod = ParserUtils.generateClassMethod({
887
- path: path2,
888
- endpoint,
889
- httpMethod,
890
- className
891
- });
892
- if (endpoint.requestBody) {
893
- bodyParams = [
894
- {
895
- name: "body",
896
- in: "body",
897
- schema: endpoint.requestBody.content["application/json"].schema
898
- }
899
- ];
900
- }
901
- const pathWithBase = `${api.basePath ?? ""}${path2}`;
902
- const [generatedMethodString, importStatements] = templateMethod({
903
- classMethod,
904
- description,
905
- httpMethod,
906
- path: pathWithBase,
907
- pathParams,
908
- bodyParams,
909
- queryParams,
910
- isFormUrlEncoded,
911
- responseClass
912
- });
913
- apiBufferByTag[tag] = (apiBufferByTag[tag] || "") + generatedMethodString;
914
- jsDocApiBufferByTag[tag] = (jsDocApiBufferByTag[tag] || "") + templateJsdocMethod({ classMethod, httpMethod, path: path2, pathParams, bodyParams, queryParams });
915
- dependenciesByTag[tag] = dependenciesByTag[tag] ? [.../* @__PURE__ */ new Set([...importStatements, ...dependenciesByTag[tag]])] : [...new Set(importStatements)];
1236
+ if (responseClass) {
1237
+ const importTypeClass = ParserUtils.parseRefType(responseClass);
1238
+ classImports[className][importTypeClass] = `import { ${importTypeClass} } from '../definitions/${importTypeClass}'`;
1239
+ }
1240
+ if (responseClass && responseClass.endsWith("Array")) {
1241
+ arrayDefinitions.push(responseClass);
1242
+ }
1243
+ const queryParams = ParserUtils.filterQueryParameters(endpoint.parameters);
1244
+ const pathWithBase = `${api.basePath ?? ""}${path2}`;
1245
+ const isFormUrlEncoded = ParserUtils.isFormUrlEncoded(httpMethod, endpoint.consumes);
1246
+ const pathParams = ParserUtils.filterPathParams(endpoint.parameters);
1247
+ let bodyParams = ParserUtils.filterBodyParams(endpoint.parameters);
1248
+ if (endpoint.requestBody) {
1249
+ bodyParams = [
1250
+ {
1251
+ name: "body",
1252
+ in: "body",
1253
+ schema: endpoint.requestBody.content["application/json"].schema
1254
+ }
1255
+ ];
916
1256
  }
1257
+ const { methodImpl, methodParams, methodParamsNoTypes, importStatements, snippetString } = templateMethod({
1258
+ classMethod,
1259
+ description,
1260
+ httpMethod,
1261
+ path: pathWithBase,
1262
+ pathParams,
1263
+ bodyParams,
1264
+ queryParams,
1265
+ isFormUrlEncoded,
1266
+ responseClass
1267
+ });
1268
+ classBufferByTag[tag] = (classBufferByTag[tag] || "") + methodImpl;
1269
+ const [generatedMethodString1, importStatements1, snippetMethod, snippetShell] = templateApiMethod({
1270
+ classMethod,
1271
+ description,
1272
+ httpMethod,
1273
+ path: pathWithBase,
1274
+ pathParams,
1275
+ bodyParams,
1276
+ queryParams,
1277
+ responseClass,
1278
+ classGenName,
1279
+ methodParams,
1280
+ methodParamsNoTypes
1281
+ });
1282
+ apiBufferByTag[tag] = (apiBufferByTag[tag] || "") + generatedMethodString1;
1283
+ apiArgumentsByTag[tag] = (apiArgumentsByTag[tag] || "") + classMethod + ",";
1284
+ dependenciesByTag[tag] = dependenciesByTag[tag] ? [.../* @__PURE__ */ new Set([...importStatements, ...dependenciesByTag[tag]])] : [...new Set(importStatements)];
1285
+ const serviceNameTitle = ParserUtils.convertDashesToTitleCase(serviceName);
1286
+ const { apiGenName } = ParserUtils.generateApiName(tag);
1287
+ let resultSnippet = templateSdkSnippet(serviceNameTitle, apiGenName, snippetMethod);
1288
+ resultSnippet = ParserUtils.replaceAll(resultSnippet, "<", "&lt;");
1289
+ resultSnippet = ParserUtils.replaceAll(resultSnippet, ">", "&gt;");
1290
+ resultSnippet = ParserUtils.replaceAll(resultSnippet, "\n", "<br/>");
1291
+ snippetMap[path2][httpMethod] = {
1292
+ web: resultSnippet,
1293
+ shell: snippetShell
1294
+ };
917
1295
  }
918
1296
  }
919
1297
  arrayDefinitions = [...new Set(arrayDefinitions)];
920
- return { apiBufferByTag, jsDocApiBufferByTag, dependenciesByTag, classImports, arrayDefinitions };
1298
+ return {
1299
+ apiArgumentsByTag,
1300
+ apiBufferByTag,
1301
+ classBufferByTag,
1302
+ dependenciesByTag,
1303
+ classImports,
1304
+ arrayDefinitions,
1305
+ snippetMap
1306
+ };
921
1307
  };
922
1308
  static main = async (nameArray) => {
923
1309
  const serviceName = nameArray[0];
@@ -925,7 +1311,7 @@ class CodeGenerator {
925
1311
  const parser = new SwaggerParser();
926
1312
  const generatedFolder = CliParser.isAdmin() ? CodeGenerator.getGeneratedAdminFolder() : CodeGenerator.getGeneratedPublicFolder();
927
1313
  const DIST_DIR = `${generatedFolder}/${serviceName}`;
928
- const DIST_DOCS_DIR = path.join(DIST_DIR, "docs");
1314
+ const DIST_ENDPOINTS_DIR = path.join(DIST_DIR, "endpoints");
929
1315
  const DIST_DEFINITION_DIR = path.join(DIST_DIR, "definitions");
930
1316
  const swaggerFilePath = `${CliParser.getSwaggersOutputPath()}/${swaggerFile}`;
931
1317
  const swaggerPatchedFilePath = `${CodeGenerator.getPatchedDir()}/${swaggerFile}`;
@@ -933,20 +1319,35 @@ class CodeGenerator {
933
1319
  ParserUtils.applyPatchIfExists(swaggerFilePath, swaggerPatchFilePath, swaggerPatchedFilePath, CodeGenerator.getPatchedDir());
934
1320
  const api = await parser.parse(swaggerPatchedFilePath);
935
1321
  const indexImportsSet = /* @__PURE__ */ new Set();
936
- console.log("\n----------\n API name: %s, Version: %s schemes %s", api, api.info.title, api.info.version, api.schemes);
1322
+ console.log("----------\nGenerating API:", { title: api.info.title, version: api.info.version });
937
1323
  ParserUtils.mkdirIfNotExist(DIST_DIR);
938
- ParserUtils.mkdirIfNotExist(DIST_DOCS_DIR);
939
1324
  ParserUtils.mkdirIfNotExist(DIST_DEFINITION_DIR);
940
- const { apiBufferByTag, dependenciesByTag, classImports, arrayDefinitions } = await CodeGenerator.iterateApi(api);
1325
+ ParserUtils.mkdirIfNotExist(DIST_ENDPOINTS_DIR);
1326
+ ParserUtils.syncPackageVersion(api.info, CliParser.isAdmin());
1327
+ const { apiArgumentsByTag, apiBufferByTag, classBufferByTag, dependenciesByTag, classImports, arrayDefinitions, snippetMap } = await CodeGenerator.iterateApi(api, serviceName);
1328
+ if (CliParser.getSnippetOutputPath()) {
1329
+ ParserUtils.writeSnippetFile(CodeGenerator.getGeneratedSnippetsFolder(), api.info.title, JSON.stringify(snippetMap, null, 2));
1330
+ }
941
1331
  const targetSrcFolder = `${CliParser.getOutputPath()}/`;
942
- for (const tag in apiBufferByTag) {
943
- const className = _.upperFirst(_.camelCase(tag));
944
- const apiBuffer = apiBufferByTag[tag];
1332
+ const apiList = [];
1333
+ for (const tag in classBufferByTag) {
1334
+ const { className, classGenName } = ParserUtils.generateClassName(tag);
1335
+ const classBuffer = classBufferByTag[tag];
945
1336
  const imports = [.../* @__PURE__ */ new Set([...dependenciesByTag[tag], ...Object.values(classImports[className])])];
946
- const classGenName = CliParser.isAdmin() ? className + "Admin$" : className + "$";
947
- ParserUtils.writeClassFile(DIST_DIR, classGenName, apiBuffer, imports);
948
- indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path.join(DIST_DIR, `${classGenName}`), targetSrcFolder));
1337
+ const apiImports = [.../* @__PURE__ */ new Set([...dependenciesByTag[tag], ...Object.values(classImports[className])])];
1338
+ apiImports.push(`import { ${classGenName} } from './endpoints/${classGenName}'`);
1339
+ ParserUtils.writeClassFile(DIST_ENDPOINTS_DIR, classGenName, classBuffer, imports);
1340
+ const apiBuffer = apiBufferByTag[tag];
1341
+ const { apiGenName } = ParserUtils.generateApiName(tag);
1342
+ ParserUtils.writeApiFile(DIST_DIR, apiGenName, apiBuffer, apiImports, apiArgumentsByTag[tag]);
1343
+ apiList.push(apiGenName);
1344
+ indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path.join(DIST_ENDPOINTS_DIR, `${classGenName}`), targetSrcFolder));
1345
+ indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path.join(DIST_DIR, `${apiGenName}`), targetSrcFolder));
949
1346
  }
1347
+ const serviceNameTitle = ParserUtils.convertDashesToTitleCase(serviceName);
1348
+ const apiIndexBuff = templateApiIndex(serviceName, serviceNameTitle, apiList);
1349
+ ParserUtils.writeApiMainFile(generatedFolder, serviceNameTitle, apiIndexBuff);
1350
+ indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path.join(generatedFolder, serviceNameTitle), targetSrcFolder));
950
1351
  const duplicates = /* @__PURE__ */ new Map();
951
1352
  const definitions = api?.components?.schemas || api.definitions;
952
1353
  for (const ref in definitions) {
@@ -975,7 +1376,7 @@ class CodeGenerator {
975
1376
  ParserUtils.writeDefinitionFile(DIST_DEFINITION_DIR, arrayClass, buffer);
976
1377
  indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path.join(DIST_DEFINITION_DIR, arrayClass), targetSrcFolder));
977
1378
  }
978
- console.log("\n----------\nCOMPLETED.\n----------\n\n");
1379
+ console.log("\nCOMPLETED\n----------\n\n");
979
1380
  return indexImportsSet;
980
1381
  };
981
1382
  }
@@ -1002,7 +1403,7 @@ class SwaggerDownloader {
1002
1403
  });
1003
1404
  });
1004
1405
  request.on("error", (err) => {
1005
- console.log(`SwaggerDownloader dl failed for "${targetFileName}" and "${url}"`, err);
1406
+ console.log(`SwaggerDownloader failed for "${targetFileName}" and "${url}"`, err);
1006
1407
  });
1007
1408
  };
1008
1409
  static main = () => {