@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.
@@ -1,12 +1,12 @@
1
1
  import yargs from 'yargs';
2
2
  import { z } from 'zod';
3
3
  import * as fs from 'fs';
4
- import fs__default from 'fs';
4
+ import fs__default, { readFileSync, writeFileSync } from 'fs';
5
5
  import * as path from 'path';
6
6
  import path__default from 'path';
7
7
  import SwaggerParser from '@apidevtools/swagger-parser';
8
- import _ from 'lodash';
9
8
  import { applyPatch } from 'fast-json-patch';
9
+ import _ from 'lodash';
10
10
  import * as https from 'https';
11
11
 
12
12
  const SwaggersConfig = z.array(z.array(z.string()));
@@ -51,17 +51,55 @@ class CliParser {
51
51
  static isAdmin = () => {
52
52
  return CliParser.instance().argv.admin;
53
53
  };
54
+ static getSnippetOutputPath = () => {
55
+ return CliParser.instance().argv.snippetOutput;
56
+ };
54
57
  }
55
58
 
56
- const getImportableVarMap = () => ({
59
+ const getImportableVarMap$1 = () => ({
57
60
  "@accelbyte/sdk": ["CodeGenUtil", "SdkCache", "IResponse", "IResponseWithSync", "Validate"],
58
61
  axios: ["AxiosRequestConfig", "AxiosResponse"],
59
62
  zod: ["z"]
60
63
  });
61
- const makeNewImportVarMap = () => ({
64
+ const makeNewImportVarMap$1 = () => ({
62
65
  axios: ["AxiosInstance"],
63
66
  "@accelbyte/sdk": ["SDKRequestConfig"]
64
67
  });
68
+ const generateImports$1 = (body, importStatements) => {
69
+ const usedImportVarMap = makeNewImportVarMap$1();
70
+ const importableVarMap = getImportableVarMap$1();
71
+ for (const [moduleSource, importableVars] of Object.entries(importableVarMap)) {
72
+ for (const importableVar of importableVars) {
73
+ const importVarRegex = new RegExp(`(?<![\\d\\w_])${importableVar}(?![\\d\\w_])`);
74
+ if (body.match(importVarRegex)) {
75
+ usedImportVarMap[moduleSource] = [...usedImportVarMap[moduleSource] || [], importableVar];
76
+ }
77
+ }
78
+ }
79
+ const generatedImports = Object.keys(usedImportVarMap).sort().map((moduleSource) => `import { ${usedImportVarMap[moduleSource].sort().join(", ")} } from '${moduleSource}'`).join("\n");
80
+ return `${generatedImports}
81
+ ${importStatements.sort().join("\n")}`;
82
+ };
83
+ const templateClass = (className, body, importStatements) => {
84
+ return `/**
85
+ * AUTO GENERATED
86
+ */
87
+ ${generateImports$1(body, importStatements)}
88
+
89
+ export class ${className} {
90
+ // @ts-ignore
91
+ constructor(private axiosInstance: AxiosInstance, private namespace: string, private cache = false) {}
92
+ ${body}
93
+ }
94
+ `;
95
+ };
96
+
97
+ const getImportableVarMap = () => ({
98
+ "@accelbyte/sdk": ["CodeGenUtil", "SdkCache", "IResponse", "IResponseWithSync", "Validate", "ApiArgs", "Network", "AccelbyteSDK"]
99
+ });
100
+ const makeNewImportVarMap = () => ({
101
+ "@accelbyte/sdk": ["AccelbyteSDK", "ApiArgs", "ApiUtils"]
102
+ });
65
103
  const generateImports = (body, importStatements) => {
66
104
  const usedImportVarMap = makeNewImportVarMap();
67
105
  const importableVarMap = getImportableVarMap();
@@ -77,27 +115,55 @@ const generateImports = (body, importStatements) => {
77
115
  return `${generatedImports}
78
116
  ${importStatements.sort().join("\n")}`;
79
117
  };
80
- const templateClass = (className, body, importStatements) => `/**
81
- * DON'T EDIT THIS FILE, it is AUTO GENERATED
82
- */
83
- ${generateImports(body, importStatements)}
118
+ const templateApiClass = (className, body, importStatements, returnMethods) => {
119
+ return `/**
120
+ * AUTO GENERATED
121
+ */
122
+ /* eslint-disable camelcase */
123
+ ${generateImports(body, importStatements)}
84
124
 
85
- export class ${className} {
86
- // @ts-ignore
87
- constructor(private axiosInstance: AxiosInstance, private namespace: string, private cache = false) {}
125
+ export function ${className}(sdk: AccelbyteSDK, args?: ApiArgs) {
126
+ const sdkAssembly = sdk.assembly()
127
+
128
+ const namespace = args?.namespace ? args?.namespace : sdkAssembly.namespace
129
+ const cache = args?.cache ? args?.cache : sdkAssembly.cache
130
+ const requestConfig = ApiUtils.mergedConfigs(sdkAssembly.config, args)
88
131
  ${body}
132
+
133
+ return {
134
+ ${returnMethods}
89
135
  }
136
+ }
90
137
  `;
138
+ };
91
139
 
92
- const templateJsdocFile = (apiName, body) => `
93
- \`\`\`
94
- SDK.getService('${apiName}', authHeader):
95
- ${body}
96
-
97
- \`\`\`
98
- `.replace(/, \)/g, ")").trim();
99
-
140
+ const UNDEFINED_SWAGGER_SEMVER = "0.0.0";
141
+ const REMOVED_KEYWORDS = [
142
+ "/admin/",
143
+ "/public/",
144
+ "/v1/",
145
+ "/v2/",
146
+ "/v3/",
147
+ "/v4/",
148
+ "/v5/",
149
+ "/namespace/",
150
+ "/namespaces/",
151
+ "/{namespace}/"
152
+ ];
100
153
  class ParserUtils {
154
+ static replaceAll = (string, search, replace) => {
155
+ return string.split(search).join(replace);
156
+ };
157
+ static generateClassName = (tag) => {
158
+ const className = _.upperFirst(_.camelCase(tag));
159
+ const classGenName = CliParser.isAdmin() ? className + "Admin$" : className + "$";
160
+ return { className, classGenName };
161
+ };
162
+ static generateApiName = (tag) => {
163
+ const apiName = _.upperFirst(_.camelCase(tag));
164
+ const apiGenName = CliParser.isAdmin() ? apiName + "AdminApi" : apiName + "Api";
165
+ return { apiName, apiGenName };
166
+ };
101
167
  static parseQueryParamAttributeDefault = (definition) => {
102
168
  const attrName = definition.name.slice(definition.name.lastIndexOf(".") + 1);
103
169
  const defaultValue = definition.type === "string" ? `'${definition.default}'` : definition.default;
@@ -115,17 +181,26 @@ class ParserUtils {
115
181
  return pathParam.type;
116
182
  };
117
183
  static parseQueryParamsType = (queryParams) => {
118
- return queryParams.map((queryParam) => ParserUtils.parseAttributeType(queryParam)).join(",");
184
+ return queryParams.map((queryParam) => ParserUtils.parseAttributeType(queryParam)).join(", ");
119
185
  };
120
186
  static isAnyQueryParamRequired = (queryParams) => {
121
187
  return queryParams.some((queryParam) => queryParam.required);
122
188
  };
189
+ static convertDashesToTitleCase = (str) => {
190
+ const result = str.split("-").map((word, index) => {
191
+ if (index === 0) {
192
+ return word.charAt(0).toUpperCase() + word.slice(1);
193
+ }
194
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
195
+ }).join("");
196
+ return result;
197
+ };
123
198
  static parseQueryParamsDefault = (queryParams) => {
124
199
  const result = queryParams.filter((queryParam) => !!queryParam.default && !queryParam.required).map(ParserUtils.parseQueryParamAttributeDefault).join(",");
125
200
  return result ? `${result},` : "";
126
201
  };
127
202
  static parseBodyParamsImports = (bodyParams) => {
128
- return bodyParams.map(ParserUtils.parseRefImport).filter(Boolean);
203
+ return bodyParams.map((bodyParams2) => ParserUtils.parseRefImport(bodyParams2)).filter(Boolean);
129
204
  };
130
205
  static parseImportDir = ($ref) => {
131
206
  let ref = $ref.replace(".", "/");
@@ -144,7 +219,7 @@ class ParserUtils {
144
219
  return null;
145
220
  }
146
221
  const type = ParserUtils.parseRefType($ref);
147
- return `import { ${type} } from './definitions/${type}'`;
222
+ return `import { ${type} } from '../definitions/${type}'`;
148
223
  };
149
224
  static parseRefType = ($ref) => {
150
225
  let ref = $ref.replace(".", "/");
@@ -238,34 +313,59 @@ class ParserUtils {
238
313
  }
239
314
  return parameters.filter((parameter) => parameter.in === "path");
240
315
  }
241
- static generateClassMethod({
242
- path: path2,
243
- endpoint,
244
- httpMethod,
245
- className
246
- }) {
247
- let replacedIdsPath = path2;
248
- if (endpoint.parameters) {
249
- for (const parameter of endpoint.parameters) {
250
- if (parameter.in === "path") {
251
- replacedIdsPath = replacedIdsPath.replace("{" + parameter.name + "}", "By" + ParserUtils.toTitleCaseWord(parameter.name));
252
- replacedIdsPath = replacedIdsPath.replace("/iam", "");
253
- replacedIdsPath = replacedIdsPath.replace("/odin-config", "");
254
- }
316
+ static generateNaturalLangMethod = ({ servicePrefix, path: path2, httpMethod, isForm, existingMethods }) => {
317
+ let path_ = path2;
318
+ path_ = path_.replace(`/${servicePrefix}/`, "/");
319
+ REMOVED_KEYWORDS.forEach((prefix) => {
320
+ path_ = path_.replace(prefix, "/");
321
+ });
322
+ path_ = path_.substring(1);
323
+ const isPlural = httpMethod === "get" && !(path2.slice(-1) === "}");
324
+ if (!isPlural) {
325
+ path_ = ParserUtils.replaceAll(path_, "ies/", "y/");
326
+ path_ = ParserUtils.replaceAll(path_, "s/", "/");
327
+ if (path_.indexOf("status") < 0) {
328
+ path_ = path_.replace(/ies$/, "y");
329
+ path_ = path_.replace(/s$/, "");
255
330
  }
256
331
  }
257
- let classMethod = httpMethod + "/" + replacedIdsPath;
258
- classMethod = _.camelCase(classMethod);
259
- classMethod = classMethod.replace("PublicNamespacesByNamespace", "Ns");
260
- classMethod = classMethod.replace("AdminNamespacesByNamespace", "AdminNs");
261
- const searchWord = "NamespacesByNamespace";
262
- const nsExistInsideMethod = classMethod.indexOf(searchWord) > 0 && classMethod.indexOf(searchWord) + searchWord.length < classMethod.length;
263
- const excludedClasses = ["Policies"];
264
- if (nsExistInsideMethod && !excludedClasses.includes(className)) {
265
- classMethod = classMethod.replace(searchWord, "");
266
- }
267
- return classMethod;
268
- }
332
+ const arrLastWords = path_.split("}/");
333
+ let lastWords = arrLastWords[arrLastWords.length - 1];
334
+ const extractLastWord = (lastWords_) => {
335
+ let res = lastWords_;
336
+ res = res.split("/{")[0];
337
+ return res;
338
+ };
339
+ lastWords = extractLastWord(lastWords);
340
+ if (lastWords.indexOf("{") >= 0 && arrLastWords.length > 1) {
341
+ lastWords = arrLastWords[arrLastWords.length - 2];
342
+ lastWords = extractLastWord(lastWords);
343
+ }
344
+ const listBeforeLastWords = [];
345
+ let foundParam = false;
346
+ const listByParams = [];
347
+ const pathElements = path_.split("/");
348
+ pathElements.slice().reverse().forEach((item) => {
349
+ if (item.indexOf("}") >= 0) {
350
+ foundParam = true;
351
+ let param = item.replace("{", "");
352
+ param = param.replace("}", "");
353
+ param = "Byword" + _.upperFirst(param);
354
+ listByParams.push(param);
355
+ } else if (!foundParam) {
356
+ if (lastWords.indexOf(item) === -1) {
357
+ listBeforeLastWords.push(item);
358
+ }
359
+ } else {
360
+ foundParam = false;
361
+ }
362
+ });
363
+ const genPath = _.upperFirst(lastWords) + "/" + listBeforeLastWords.join("/") + listByParams.reverse().join("/");
364
+ let generatedMethod = _.camelCase(mappedMethod(httpMethod, isForm) + genPath);
365
+ generatedMethod = ParserUtils.replaceAll(generatedMethod, "Byword", "_By");
366
+ generatedMethod = resolveConflicts(path2, generatedMethod, existingMethods);
367
+ return generatedMethod;
368
+ };
269
369
  static filterBodyParams(parameters) {
270
370
  if (Array.isArray(parameters) && parameters.length > 0) {
271
371
  return parameters.filter((parameter) => parameter.in === "body" || parameter.in === "formData");
@@ -287,9 +387,22 @@ class ParserUtils {
287
387
  const fileContent = templateClass(apiName, apiBuffer, imports);
288
388
  fs__default.writeFileSync(`${distDir}/${apiName}.ts`, ParserUtils.prependCopyrightHeader(fileContent));
289
389
  }
290
- static writeJsdocFile(distDir, nameArray, apiBuffer) {
291
- const jsdocFile = templateJsdocFile(nameArray[0], apiBuffer);
292
- fs__default.writeFileSync(`${distDir}/docs/${nameArray[0]}.md`, jsdocFile);
390
+ static writeApiFile(distDir, apiName, apiBuffer, imports, returnMethods) {
391
+ const newImports = [];
392
+ imports.forEach((el, index) => {
393
+ newImports.push(el.replace("../definitions", "./definitions"));
394
+ });
395
+ const fileContent = templateApiClass(apiName, apiBuffer, newImports, returnMethods);
396
+ fs__default.writeFileSync(`${distDir}/${apiName}.ts`, ParserUtils.prependCopyrightHeader(fileContent));
397
+ }
398
+ static writeApiMainFile(distDir, serviceName, fileContent) {
399
+ fs__default.writeFileSync(`${distDir}/${serviceName}.ts`, ParserUtils.prependCopyrightHeader(fileContent));
400
+ }
401
+ static writeSnippetFile(distDir, name, docBuffer) {
402
+ let snippetFileName = ParserUtils.replaceAll(name, " ", "-").toLowerCase();
403
+ snippetFileName = snippetFileName.replace("justice-", "");
404
+ snippetFileName = "snippet-" + snippetFileName + ".json";
405
+ fs__default.writeFileSync(`${distDir}/${snippetFileName}`, docBuffer);
293
406
  }
294
407
  static writeDefinitionFile(distDir, name, buffer) {
295
408
  ParserUtils.mkdirIfNotExist(distDir);
@@ -299,6 +412,37 @@ class ParserUtils {
299
412
  ParserUtils.mkdirIfNotExist(distDir);
300
413
  fs__default.writeFileSync(path__default.join(distDir, `all-${isAdminWebSdk ? "admin" : "public"}-imports.ts`), ParserUtils.prependCopyrightHeader(buffer));
301
414
  }
415
+ static syncChangelog(packageVersion) {
416
+ const currDir = process.cwd();
417
+ const pathToChangelog = path__default.join(currDir, "./CHANGELOG.md");
418
+ let fileContent = readFileSync(pathToChangelog, "utf-8");
419
+ const date = new Date().toISOString().slice(0, 10);
420
+ fileContent = "### " + packageVersion + " - " + date + `
421
+
422
+ - code-generated update
423
+
424
+ ` + fileContent.trim();
425
+ fs__default.writeFileSync(pathToChangelog, fileContent, "utf-8");
426
+ }
427
+ static syncPackageVersion(apiInfo, isAdminWebSdk) {
428
+ if (isAdminWebSdk) {
429
+ return;
430
+ }
431
+ const currDir = process.cwd();
432
+ const { packageJSON, pathToPackageJSON } = ParserUtils.getPackageJSONInfo(currDir);
433
+ let swaggerVersion = apiInfo.version ? apiInfo.version : UNDEFINED_SWAGGER_SEMVER;
434
+ swaggerVersion = Number(swaggerVersion.replace(".", "").replace(".", ""));
435
+ swaggerVersion = isNaN(swaggerVersion) ? 0 : swaggerVersion;
436
+ const semver = packageJSON.version.split(".").map((numStr) => Number(numStr));
437
+ const newSemver = [semver[0], swaggerVersion, ++semver[2]];
438
+ packageJSON.version = newSemver[0] + "." + newSemver[1] + "." + newSemver[2];
439
+ writeFileSync(pathToPackageJSON, JSON.stringify(packageJSON, null, 2));
440
+ ParserUtils.syncChangelog(packageJSON.version);
441
+ }
442
+ static getPackageJSONInfo(dir) {
443
+ const pathToPackageJSON = path__default.join(dir, "./package.json");
444
+ return { packageJSON: JSON.parse(readFileSync(pathToPackageJSON, "utf-8")), pathToPackageJSON };
445
+ }
302
446
  static toCamelCase(str) {
303
447
  return str.split("/").map(function(word, index) {
304
448
  if (index === 0) {
@@ -332,6 +476,29 @@ class ParserUtils {
332
476
  }
333
477
  const swaggerContent = JSON.parse(fs__default.readFileSync(swaggerFilePath, "utf8"));
334
478
  const swaggerPatchFileContent = JSON.parse(fs__default.readFileSync(possibleSwaggerPatchFilePath, "utf8"));
479
+ for (const patchEntry of swaggerPatchFileContent) {
480
+ const segments = patchEntry.path.split("/").filter(Boolean);
481
+ let currentNode = swaggerContent;
482
+ let aggregatedPath = "";
483
+ for (let i = 0; i < segments.length; i++) {
484
+ const segment = segments[i];
485
+ aggregatedPath += `/${segment}`;
486
+ const effectiveSegment = segment.replace(/(~1)/g, "/").replace(/(~0)/g, "~");
487
+ if (!currentNode[effectiveSegment]) {
488
+ if (i + 1 === segments.length && patchEntry.op === "add") ; else {
489
+ throw new Error([
490
+ `JSON patch error: operation "${patchEntry.op}" on path "${aggregatedPath}" fails because the path doesn't exist in ${swaggerFilePath}. This may be caused by:
491
+ `,
492
+ "1. The related service has patched the service, so patch is no longer needed.",
493
+ "2. There is a breaking change on the service that causes the path to change.\n",
494
+ `In any case, revisit this file: "${possibleSwaggerPatchFilePath}", then try again.
495
+ `
496
+ ].join("\n"));
497
+ }
498
+ }
499
+ currentNode = currentNode[effectiveSegment];
500
+ }
501
+ }
335
502
  const { newDocument } = applyPatch(swaggerContent, swaggerPatchFileContent);
336
503
  fs__default.writeFileSync(swaggerPatchedFilePath, JSON.stringify(newDocument, null, 2));
337
504
  }
@@ -349,6 +516,67 @@ class ParserUtils {
349
516
  ${content}`;
350
517
  };
351
518
  }
519
+ const mappedMethod = (httpMethod, isForm) => {
520
+ if (httpMethod === "get") {
521
+ return "get";
522
+ } else if (httpMethod === "post" && isForm) {
523
+ return "post";
524
+ } else if (httpMethod === "post") {
525
+ return "create";
526
+ } else if (httpMethod === "put") {
527
+ return "update";
528
+ } else if (httpMethod === "patch") {
529
+ return "patch";
530
+ } else if (httpMethod === "delete") {
531
+ return "delete";
532
+ }
533
+ };
534
+ const resolveConflicts = (path2, generatedMethod, existingMethods) => {
535
+ try {
536
+ testConflict(path2, generatedMethod, existingMethods);
537
+ } catch (e) {
538
+ if (path2.indexOf("/namespaces/") >= 0) {
539
+ generatedMethod += "_ByNS";
540
+ }
541
+ }
542
+ try {
543
+ testConflict(path2, generatedMethod, existingMethods);
544
+ } catch (e) {
545
+ if (path2.indexOf("/v4/") >= 0) {
546
+ generatedMethod += "_v4";
547
+ }
548
+ }
549
+ try {
550
+ testConflict(path2, generatedMethod, existingMethods);
551
+ } catch (e) {
552
+ if (path2.indexOf("/v3/") >= 0) {
553
+ generatedMethod += "_v3";
554
+ }
555
+ }
556
+ try {
557
+ testConflict(path2, generatedMethod, existingMethods);
558
+ } catch (e) {
559
+ if (path2.indexOf("/v2/") >= 0) {
560
+ generatedMethod += "_v2";
561
+ }
562
+ }
563
+ try {
564
+ testConflict(path2, generatedMethod, existingMethods);
565
+ } catch (e) {
566
+ if (path2.indexOf("/admin/") >= 0) {
567
+ generatedMethod += "_admin";
568
+ }
569
+ }
570
+ testConflict(path2, generatedMethod, existingMethods);
571
+ return generatedMethod;
572
+ };
573
+ const testConflict = (path2, generatedMethod, existingMethods) => {
574
+ if (existingMethods[generatedMethod]) {
575
+ const conflictingMethod = { path: path2, generatedMethod };
576
+ throw Error(`Duplicate method conflict in ${JSON.stringify(conflictingMethod)},
577
+ existingMethods: ${JSON.stringify(existingMethods, null, 2)}`);
578
+ }
579
+ };
352
580
 
353
581
  const Schema = z.object({
354
582
  $ref: z.string().nullish(),
@@ -440,50 +668,6 @@ z.object({
440
668
  }).nullish()
441
669
  });
442
670
 
443
- const templateJsdocMethod = ({
444
- classMethod,
445
- httpMethod,
446
- path,
447
- pathParams,
448
- bodyParams,
449
- queryParams
450
- }) => {
451
- let jsdoc = "";
452
- let methodSignature = "";
453
- let newPath = path;
454
- for (const p of pathParams) {
455
- methodSignature += p.name + ", ";
456
- newPath = newPath.replace("{" + p.name + "}", `' + ${p.name} + '`);
457
- jsdoc += `
458
- * @pathParam '${p.name}${p.required ? "" : "?"}' - ${p.description}`;
459
- }
460
- let payloads = "";
461
- for (const p of bodyParams) {
462
- methodSignature += p.name + ", ";
463
- payloads += p.name;
464
- jsdoc += `
465
- * @payload '${p.name}${p.required ? "" : "?"}' - ${p.description} ${p?.schema?.properties ? "=> " + JSON.stringify(p.schema.properties) : ""}`;
466
- }
467
- for (const p of queryParams) {
468
- const optionalMark = p.required ? "" : "?";
469
- methodSignature += p.name + optionalMark + ", ";
470
- jsdoc += `
471
- * @queryParam '${p.name}${p.required ? "" : "?"}' - ${p.description}`;
472
- }
473
- let queryString = "";
474
- for (const p of queryParams) {
475
- queryString += `${p.name}=__&`;
476
- }
477
- queryString = queryString.length > 0 ? "?" + queryString : "";
478
- return `
479
- /**
480
- * ${httpMethod.toUpperCase()} '${newPath}${queryString}' ${payloads ? "payload: '" + payloads + "'" : ""}
481
- * ${jsdoc}
482
- */
483
- ${classMethod}(${methodSignature}): Promise
484
- `;
485
- };
486
-
487
671
  const templateMethod = ({
488
672
  classMethod,
489
673
  description,
@@ -495,13 +679,17 @@ const templateMethod = ({
495
679
  isFormUrlEncoded,
496
680
  responseClass
497
681
  }) => {
498
- let methodSignature = "";
682
+ let methodParams = "";
683
+ let methodParamsNoTypes = "";
499
684
  let newPath = `'${path}'`;
500
- let dependencies = [];
685
+ let importStatements = [];
686
+ let snippetString = "";
687
+ let snippetMethodSignature = "";
501
688
  for (const pathParam of pathParams) {
502
689
  const type = ParserUtils.parseType(pathParam);
503
690
  if (pathParam.name !== "namespace") {
504
- methodSignature += pathParam.name + `:${type}, `;
691
+ methodParams += pathParam.name + `:${type}, `;
692
+ methodParamsNoTypes += pathParam.name + ", ";
505
693
  }
506
694
  const pName = pathParam.name === "namespace" ? "this.namespace" : pathParam.name;
507
695
  if (path.match(`{${pathParam.name}}`)) {
@@ -515,8 +703,16 @@ const templateMethod = ({
515
703
  let dataType = null;
516
704
  if (httpMethod !== "get") {
517
705
  dataType = ParserUtils.parseBodyParamsType(bodyParams);
518
- dependencies = ParserUtils.parseBodyParamsImports(bodyParams);
519
- methodSignature += dataType ? `data: ${dataType},` : "";
706
+ importStatements = ParserUtils.parseBodyParamsImports(bodyParams);
707
+ methodParams += dataType ? `data: ${dataType},` : "";
708
+ methodParamsNoTypes += dataType ? `data,` : "";
709
+ const snippetParams = bodyParams?.map((ob) => {
710
+ return ` <span class='sn-purple'>${ob.name}</span>`;
711
+ });
712
+ snippetMethodSignature += snippetParams ? `data: { ${snippetParams} }` : "";
713
+ bodyParams?.map((ob) => {
714
+ return ` <span class='sn-purple'>"${ob.name}": ""</span>`;
715
+ });
520
716
  }
521
717
  const isAnyRequired = ParserUtils.isAnyQueryParamRequired(queryParams);
522
718
  const queryParamsType = queryParams.length ? `queryParams${isAnyRequired ? "" : "?"}: {${ParserUtils.parseQueryParamsType(queryParams)}}` : "";
@@ -526,7 +722,7 @@ const templateMethod = ({
526
722
  let dataPayload = "{params}";
527
723
  const descriptionText = description ? `
528
724
  /**
529
- * ${(description || "").replace(/\n/g, "\n * ")}
725
+ * ${description.replace(/\n/g, "\n * ")}
530
726
  */` : "";
531
727
  let formPayloadString = "";
532
728
  if (isFormUrlEncoded) {
@@ -538,47 +734,57 @@ const templateMethod = ({
538
734
  } else if (isDelete) {
539
735
  dataPayload = dataType ? `{data, params}` : "{params}";
540
736
  }
541
- const isFileUpload = methodSignature.indexOf("data: {file") > -1;
737
+ const isFileUpload = methodParams.indexOf("data: {file") > -1;
542
738
  const resolvedResponseClass = responseClass || "unknown";
543
739
  const resolvedResponseClassValidated = responseClass || "z.unknown()";
544
- const parameters = (queryParamsType ? `${methodSignature} ${queryParamsType}` : methodSignature).replace(/,\s*$/, "");
740
+ methodParams = (queryParamsType ? `${methodParams} ${queryParamsType}` : methodParams).replace(/,\s*$/, "");
741
+ methodParamsNoTypes = queryParamsType ? `${methodParamsNoTypes} queryParams` : methodParamsNoTypes;
545
742
  let methodImpl = "";
546
743
  const isCacheFetch = ["get"].includes(httpMethod) && resolvedResponseClass !== "unknown";
547
- const cachedFetchMethod = classMethod.replace("get", "fetch");
744
+ const cachedFetchMethod = classMethod;
548
745
  const deprecateTag = isCacheFetch ? `/**
549
- * @deprecated Use "${cachedFetchMethod}()" instead.
746
+ * @deprecated Use "${classMethod}()" instead.
550
747
  */` : "";
551
748
  const isGuardInvoked = ["get", "post", "put", "patch", "delete"].includes(httpMethod);
552
749
  const methodName = httpMethod === "get" ? cachedFetchMethod : ["post", "put", "patch", "delete"].includes(httpMethod) ? classMethod : "";
553
- const methodGenerics = resolvedResponseClass !== "unknown" ? `<T = ${resolvedResponseClass}>` : "";
554
- const responseType = resolvedResponseClass !== "unknown" ? `T` : "unknown";
750
+ const responseType = resolvedResponseClass !== "unknown" ? `${resolvedResponseClass}` : "unknown";
751
+ const generateMethodName = () => `${methodName}(${methodParams}): Promise<${responseSyncType}<${responseType}>>`;
752
+ const generateSnippetMethodName = () => `${methodName}(${snippetMethodSignature})`;
753
+ snippetString += `${generateSnippetMethodName()}`;
555
754
  const responseSyncType = httpMethod === "get" ? "IResponseWithSync" : ["post", "put", "patch", "delete"].includes(httpMethod) ? "IResponse" : "";
556
755
  methodImpl = `${descriptionText}
557
- ${methodName}${methodGenerics}(${parameters}): Promise<${responseSyncType}<${responseType}>> {
756
+ ${generateMethodName()} {
558
757
  ${queryParamsDefault}
559
758
  const url = ${newPath} ${formPayloadString} ${isFileUpload ? "\n// TODO file upload not implemented" : ""}
560
759
  const resultPromise = this.axiosInstance.${httpMethod}(url, ${dataPayload})
561
760
 
562
- ${httpMethod === "get" ? `const res = () => Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})
761
+ ${httpMethod === "get" ? ` const res = () => Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})
563
762
 
564
763
  if (!this.cache) {
565
764
  return SdkCache.withoutCache(res)
566
765
  }
567
766
  const cacheKey = url + CodeGenUtil.hashCode(JSON.stringify({ params }))
568
- return SdkCache.withCache(cacheKey, res)` : ""}${["post", "put", "patch", "delete"].includes(httpMethod) ? `return Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})` : ""}
767
+ return SdkCache.withCache(cacheKey, res)` : ""}${["post", "put", "patch", "delete"].includes(httpMethod) ? ` return Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})` : ""}
569
768
  }
570
769
  `;
571
770
  if (!isGuardInvoked) {
572
771
  methodImpl = `${descriptionText}
573
772
  ${deprecateTag}
574
- TODO_${classMethod}<T = ${resolvedResponseClass}>(${parameters}): Promise<AxiosResponse<T>> {
773
+ TODO_${classMethod}(${methodParams}): Promise<AxiosResponse<${resolvedResponseClass}>> {
575
774
  ${queryParamsDefault}
576
775
  const url = ${newPath} ${formPayloadString} ${isFileUpload ? "\n// TODO file upload not implemented" : ""}
577
776
  return this.axiosInstance.${httpMethod}(url, ${dataPayload})
578
777
  }
579
778
  `;
580
779
  }
581
- return [methodImpl, dependencies];
780
+ const res = {
781
+ methodImpl,
782
+ methodParams,
783
+ methodParamsNoTypes,
784
+ importStatements,
785
+ snippetString
786
+ };
787
+ return res;
582
788
  };
583
789
 
584
790
  class TemplateZod {
@@ -594,32 +800,32 @@ class TemplateZod {
594
800
  }
595
801
  let imports = "";
596
802
  for (const cl of Array.from(this.importClasses).sort()) {
597
- imports += ` import { ${cl} } from './${cl}'
803
+ imports += `import { ${cl} } from './${cl}'
598
804
  `;
599
805
  }
600
806
  let exportedVariableString;
601
807
  let exportedTypeString;
602
808
  if (containsRecursiveType) {
603
809
  exportedVariableString = `
604
- export const ${fileName}: z.ZodType<${fileName}> = z.lazy(() =>
810
+ export const ${fileName}: z.ZodType<${fileName}> = z.lazy(() =>
605
811
  ${content.schemaString}
606
812
  )
607
813
  `;
608
814
  exportedTypeString = `
609
- export type ${fileName} = {
815
+ export interface ${fileName} {
610
816
  ${content.typeString}
611
- }
612
- `;
817
+ }
818
+ `;
613
819
  } else {
614
820
  exportedVariableString = `export const ${fileName} = ${content.schemaString}`;
615
- exportedTypeString = `export type ${fileName} = z.TypeOf<typeof ${fileName}>`;
821
+ exportedTypeString = `export interface ${fileName} extends z.TypeOf<typeof ${fileName}> {}`;
616
822
  }
617
823
  const template = `import { z } from 'zod'
618
- ${imports}
824
+ ${imports}
619
825
 
620
- ${exportedVariableString}
826
+ ${exportedVariableString}
621
827
 
622
- ${exportedTypeString}
828
+ ${exportedTypeString}
623
829
  `;
624
830
  return { buffer: template, duplicateFound: this.duplicateFound };
625
831
  };
@@ -704,7 +910,25 @@ class TemplateZod {
704
910
  typeString: refType
705
911
  };
706
912
  } else if (items) {
707
- model2 = this.parseEnumItems(items);
913
+ if (items.type === "array") {
914
+ const ref3 = items.items?.$ref;
915
+ if (ref3) {
916
+ const refType = ParserUtils.parseRefType(ref3);
917
+ this.importClasses.add(refType);
918
+ model2 = {
919
+ schemaString: refType,
920
+ typeString: refType
921
+ };
922
+ } else if (items.items) {
923
+ model2 = this.parseEnumItems(items.items);
924
+ }
925
+ return {
926
+ schemaString: `${schemaAttribute} z.array(z.array(${model2.schemaString}))${schemaRequired}`,
927
+ typeString: `${typeAttribute} ${model2.typeString}[]${typeNullishability}`
928
+ };
929
+ } else {
930
+ model2 = this.parseEnumItems(items);
931
+ }
708
932
  } else {
709
933
  return {
710
934
  schemaString: `${schemaAttribute} z.array(z.any())${schemaRequired}`,
@@ -776,11 +1000,11 @@ class TemplateZodArray {
776
1000
  render = (name) => {
777
1001
  const cls = name.replace("Array", "");
778
1002
  const template = `import { z } from 'zod'
779
- import { ${cls} } from './${cls}'
1003
+ import { ${cls} } from './${cls}'
780
1004
 
781
- export const ${name} = z.array(${cls})
1005
+ export const ${name} = z.array(${cls})
782
1006
 
783
- export type ${name} = z.TypeOf<typeof ${name}>
1007
+ export interface ${name} extends z.TypeOf<typeof ${name}> {}
784
1008
  `;
785
1009
  return template;
786
1010
  };
@@ -815,24 +1039,147 @@ const extractEnumObject = (type, isRequired, enumArr) => {
815
1039
  };
816
1040
  };
817
1041
 
1042
+ const templateApiMethod = ({
1043
+ classMethod,
1044
+ description,
1045
+ httpMethod,
1046
+ path,
1047
+ pathParams,
1048
+ bodyParams,
1049
+ queryParams,
1050
+ responseClass,
1051
+ classGenName,
1052
+ methodParams,
1053
+ methodParamsNoTypes
1054
+ }) => {
1055
+ let methodSignature = "";
1056
+ let newPath = `'${path}'`;
1057
+ let dependencies = [];
1058
+ let snippetSdk = "";
1059
+ let snippetShell = "";
1060
+ for (const pathParam of pathParams) {
1061
+ const type = ParserUtils.parseType(pathParam);
1062
+ if (pathParam.name !== "namespace") {
1063
+ methodSignature += pathParam.name + `:${type}, `;
1064
+ }
1065
+ const pName = pathParam.name === "namespace" ? "this.namespace" : pathParam.name;
1066
+ if (path.match(`{${pathParam.name}}`)) {
1067
+ if (type === "string") {
1068
+ newPath = `${newPath}.replace('{${pathParam.name}}', ${pName})`;
1069
+ } else {
1070
+ newPath = `${newPath}.replace('{${pathParam.name}}', String(${pName}))`;
1071
+ }
1072
+ }
1073
+ }
1074
+ 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'`;
1075
+ let dataType = null;
1076
+ if (httpMethod !== "get") {
1077
+ dataType = ParserUtils.parseBodyParamsType(bodyParams);
1078
+ dependencies = ParserUtils.parseBodyParamsImports(bodyParams);
1079
+ methodSignature += dataType ? `data: ${dataType},` : "";
1080
+ bodyParams?.map((ob) => {
1081
+ return ` <span class='sn-purple'>${ob.name}</span>`;
1082
+ });
1083
+ const curlParams = bodyParams?.map((ob) => {
1084
+ return ` <span class='sn-purple'>"${ob.name}": ""</span>`;
1085
+ });
1086
+ snippetShell += ` --data-raw { ${curlParams}}`;
1087
+ }
1088
+ const isAnyRequired = ParserUtils.isAnyQueryParamRequired(queryParams);
1089
+ queryParams.length ? `queryParams${isAnyRequired ? "" : "?"}: {${ParserUtils.parseQueryParamsType(queryParams)}}` : "";
1090
+ queryParams.length ? `const params = {${ParserUtils.parseQueryParamsDefault(queryParams)} ...queryParams} as SDKRequestConfig` : "const params = {} as SDKRequestConfig";
1091
+ description ? `
1092
+ /**
1093
+ * ${description.replace(/\n/g, "\n * ")}
1094
+ */` : "";
1095
+ const resolvedResponseClass = responseClass || "unknown";
1096
+ const responseType = resolvedResponseClass !== "unknown" ? `${resolvedResponseClass}` : "unknown";
1097
+ const methodImpl = `
1098
+ async function ${classMethod}(${methodParams}): Promise<${responseType}> {
1099
+ const $ = new ${classGenName}(Network.create(requestConfig), namespace, cache)
1100
+ const resp = await $.${classMethod}(${methodParamsNoTypes})
1101
+ if (resp.error) throw resp.error
1102
+ return resp.response.data
1103
+ }
1104
+ `;
1105
+ snippetSdk += `${classMethod}(${methodParams}) // return Promise<${responseType}>`;
1106
+ return [methodImpl, dependencies, snippetSdk, snippetShell];
1107
+ };
1108
+
1109
+ const templateApiIndex = (serviceName, serviceNameTitle, apiList) => {
1110
+ let imports = "";
1111
+ let returnStatement = "";
1112
+ for (const cl of apiList) {
1113
+ imports += `
1114
+ import { ${cl} } from './${serviceName}/${cl}'`;
1115
+ returnStatement += `
1116
+ ${cl}, `;
1117
+ }
1118
+ return `/**
1119
+ * AUTO GENERATED
1120
+ */
1121
+ ${imports}
1122
+
1123
+ const apis = {
1124
+ ${returnStatement}
1125
+ }
1126
+
1127
+ export const ${ParserUtils.convertDashesToTitleCase(serviceNameTitle)} = apis
1128
+ `;
1129
+ };
1130
+
1131
+ const templateSdkSnippet = (serviceNameTitle, apiName, methodSnippet) => {
1132
+ const sdkSnippet = `
1133
+ import { Accelbyte } from '@accelbyte/sdk'
1134
+ import { ${serviceNameTitle} } from '@accelbyte/sdk-${serviceNameTitle.toLowerCase()}'
1135
+
1136
+ const SDK_CONFIG = {
1137
+ baseURL: 'https://demo.accelbyte.io',
1138
+ clientId: '77f88506b6174c3ea4d925f5b4096ce8',
1139
+ namespace: 'accelbyte',
1140
+ redirectURI: 'http://localhost:3030'
1141
+ }
1142
+
1143
+ const sdk = Accelbyte.SDK({
1144
+ options: SDK_CONFIG
1145
+ })
1146
+
1147
+ ${serviceNameTitle}.${apiName}(sdk).${methodSnippet}
1148
+ `;
1149
+ return sdkSnippet;
1150
+ };
1151
+
818
1152
  class CodeGenerator {
819
1153
  static getPatchedDir = () => path__default.join(CliParser.getSwaggersOutputPath(), "patched");
820
1154
  static getGeneratedPublicFolder = () => `${CliParser.getOutputPath()}/generated-public`;
821
1155
  static getGeneratedAdminFolder = () => `${CliParser.getOutputPath()}/generated-admin`;
822
- static iterateApi = async (api) => {
1156
+ static getGeneratedSnippetsFolder = () => `${CliParser.getSnippetOutputPath()}/generated-snippets`;
1157
+ static getServicePrefix = (servicePaths) => servicePaths[servicePaths.length - 1].split("/")[1];
1158
+ static iterateApi = async (api, serviceName) => {
823
1159
  const apiBufferByTag = {};
824
- const jsDocApiBufferByTag = {};
1160
+ const apiArgumentsByTag = {};
1161
+ const classBufferByTag = {};
825
1162
  const dependenciesByTag = {};
826
1163
  const classImports = {};
827
1164
  let arrayDefinitions = [];
828
- for (const path2 in api.paths) {
1165
+ const snippetMap = {};
1166
+ const mapClassMethods = {};
1167
+ const sortedPathsByLength = new Map(Object.entries(api.paths).sort((a, b) => {
1168
+ if (a[0].length === b[0].length) {
1169
+ return a[0].localeCompare(b[0]);
1170
+ } else {
1171
+ return a[0].length - b[0].length;
1172
+ }
1173
+ }));
1174
+ const sortedKeys = Array.from(sortedPathsByLength.keys());
1175
+ const servicePrefix = CodeGenerator.getServicePrefix(sortedKeys);
1176
+ for (const [path2, operation] of sortedPathsByLength) {
829
1177
  const isAdminEndpoint = path2.indexOf("/admin/") > -1;
830
1178
  if (CliParser.isAdmin() && !isAdminEndpoint) {
831
1179
  continue;
832
1180
  } else if (!CliParser.isAdmin() && isAdminEndpoint) {
833
1181
  continue;
834
1182
  }
835
- const operation = api.paths[path2];
836
1183
  const httpMethods = Object.keys(operation);
837
1184
  for (const httpMethod of httpMethods) {
838
1185
  const endpoint = await Endpoint.parseAsync(operation[httpMethod]).catch((error) => {
@@ -842,59 +1189,98 @@ class CodeGenerator {
842
1189
  if (!endpoint.tags) {
843
1190
  continue;
844
1191
  }
1192
+ if (endpoint.deprecated) {
1193
+ continue;
1194
+ }
845
1195
  const [tag] = endpoint.tags;
846
- const description = endpoint.description;
847
- const isDeprecated = endpoint.deprecated;
1196
+ mapClassMethods[tag] = mapClassMethods[tag] ? mapClassMethods[tag] : {};
1197
+ const isForm = endpoint.consumes && endpoint.consumes[0] === "application/x-www-form-urlencoded";
1198
+ const classMethod = ParserUtils.generateNaturalLangMethod({
1199
+ servicePrefix,
1200
+ path: path2,
1201
+ httpMethod,
1202
+ isForm,
1203
+ existingMethods: mapClassMethods[tag]
1204
+ });
1205
+ mapClassMethods[tag][classMethod] = `${path2} ${httpMethod}`;
1206
+ snippetMap[path2] = snippetMap[path2] ? snippetMap[path2] : {};
1207
+ let description = endpoint.description;
1208
+ description = description || "";
1209
+ description = description.replace(/\s+/g, " ");
848
1210
  const responseClass = ParserUtils.get2xxResponse(endpoint.responses);
849
- const className = _.upperFirst(_.camelCase(tag));
1211
+ const { className, classGenName } = ParserUtils.generateClassName(tag);
850
1212
  classImports[className] = classImports[className] ? classImports[className] : {};
851
- if (!isDeprecated) {
852
- if (responseClass) {
853
- const importTypeClass = ParserUtils.parseRefType(responseClass);
854
- classImports[className][importTypeClass] = `import { ${importTypeClass} } from './definitions/${importTypeClass}'`;
855
- }
856
- if (responseClass && responseClass.endsWith("Array")) {
857
- arrayDefinitions.push(responseClass);
858
- }
859
- const isFormUrlEncoded = ParserUtils.isFormUrlEncoded(httpMethod, endpoint.consumes);
860
- const queryParams = ParserUtils.filterQueryParameters(endpoint.parameters);
861
- const pathParams = ParserUtils.filterPathParams(endpoint.parameters);
862
- let bodyParams = ParserUtils.filterBodyParams(endpoint.parameters);
863
- const classMethod = ParserUtils.generateClassMethod({
864
- path: path2,
865
- endpoint,
866
- httpMethod,
867
- className
868
- });
869
- if (endpoint.requestBody) {
870
- bodyParams = [
871
- {
872
- name: "body",
873
- in: "body",
874
- schema: endpoint.requestBody.content["application/json"].schema
875
- }
876
- ];
877
- }
878
- const pathWithBase = `${api.basePath ?? ""}${path2}`;
879
- const [generatedMethodString, importStatements] = templateMethod({
880
- classMethod,
881
- description,
882
- httpMethod,
883
- path: pathWithBase,
884
- pathParams,
885
- bodyParams,
886
- queryParams,
887
- isFormUrlEncoded,
888
- responseClass
889
- });
890
- apiBufferByTag[tag] = (apiBufferByTag[tag] || "") + generatedMethodString;
891
- jsDocApiBufferByTag[tag] = (jsDocApiBufferByTag[tag] || "") + templateJsdocMethod({ classMethod, httpMethod, path: path2, pathParams, bodyParams, queryParams });
892
- dependenciesByTag[tag] = dependenciesByTag[tag] ? [.../* @__PURE__ */ new Set([...importStatements, ...dependenciesByTag[tag]])] : [...new Set(importStatements)];
1213
+ if (responseClass) {
1214
+ const importTypeClass = ParserUtils.parseRefType(responseClass);
1215
+ classImports[className][importTypeClass] = `import { ${importTypeClass} } from '../definitions/${importTypeClass}'`;
1216
+ }
1217
+ if (responseClass && responseClass.endsWith("Array")) {
1218
+ arrayDefinitions.push(responseClass);
1219
+ }
1220
+ const queryParams = ParserUtils.filterQueryParameters(endpoint.parameters);
1221
+ const pathWithBase = `${api.basePath ?? ""}${path2}`;
1222
+ const isFormUrlEncoded = ParserUtils.isFormUrlEncoded(httpMethod, endpoint.consumes);
1223
+ const pathParams = ParserUtils.filterPathParams(endpoint.parameters);
1224
+ let bodyParams = ParserUtils.filterBodyParams(endpoint.parameters);
1225
+ if (endpoint.requestBody) {
1226
+ bodyParams = [
1227
+ {
1228
+ name: "body",
1229
+ in: "body",
1230
+ schema: endpoint.requestBody.content["application/json"].schema
1231
+ }
1232
+ ];
893
1233
  }
1234
+ const { methodImpl, methodParams, methodParamsNoTypes, importStatements, snippetString } = templateMethod({
1235
+ classMethod,
1236
+ description,
1237
+ httpMethod,
1238
+ path: pathWithBase,
1239
+ pathParams,
1240
+ bodyParams,
1241
+ queryParams,
1242
+ isFormUrlEncoded,
1243
+ responseClass
1244
+ });
1245
+ classBufferByTag[tag] = (classBufferByTag[tag] || "") + methodImpl;
1246
+ const [generatedMethodString1, importStatements1, snippetMethod, snippetShell] = templateApiMethod({
1247
+ classMethod,
1248
+ description,
1249
+ httpMethod,
1250
+ path: pathWithBase,
1251
+ pathParams,
1252
+ bodyParams,
1253
+ queryParams,
1254
+ responseClass,
1255
+ classGenName,
1256
+ methodParams,
1257
+ methodParamsNoTypes
1258
+ });
1259
+ apiBufferByTag[tag] = (apiBufferByTag[tag] || "") + generatedMethodString1;
1260
+ apiArgumentsByTag[tag] = (apiArgumentsByTag[tag] || "") + classMethod + ",";
1261
+ dependenciesByTag[tag] = dependenciesByTag[tag] ? [.../* @__PURE__ */ new Set([...importStatements, ...dependenciesByTag[tag]])] : [...new Set(importStatements)];
1262
+ const serviceNameTitle = ParserUtils.convertDashesToTitleCase(serviceName);
1263
+ const { apiGenName } = ParserUtils.generateApiName(tag);
1264
+ let resultSnippet = templateSdkSnippet(serviceNameTitle, apiGenName, snippetMethod);
1265
+ resultSnippet = ParserUtils.replaceAll(resultSnippet, "<", "&lt;");
1266
+ resultSnippet = ParserUtils.replaceAll(resultSnippet, ">", "&gt;");
1267
+ resultSnippet = ParserUtils.replaceAll(resultSnippet, "\n", "<br/>");
1268
+ snippetMap[path2][httpMethod] = {
1269
+ web: resultSnippet,
1270
+ shell: snippetShell
1271
+ };
894
1272
  }
895
1273
  }
896
1274
  arrayDefinitions = [...new Set(arrayDefinitions)];
897
- return { apiBufferByTag, jsDocApiBufferByTag, dependenciesByTag, classImports, arrayDefinitions };
1275
+ return {
1276
+ apiArgumentsByTag,
1277
+ apiBufferByTag,
1278
+ classBufferByTag,
1279
+ dependenciesByTag,
1280
+ classImports,
1281
+ arrayDefinitions,
1282
+ snippetMap
1283
+ };
898
1284
  };
899
1285
  static main = async (nameArray) => {
900
1286
  const serviceName = nameArray[0];
@@ -902,7 +1288,7 @@ class CodeGenerator {
902
1288
  const parser = new SwaggerParser();
903
1289
  const generatedFolder = CliParser.isAdmin() ? CodeGenerator.getGeneratedAdminFolder() : CodeGenerator.getGeneratedPublicFolder();
904
1290
  const DIST_DIR = `${generatedFolder}/${serviceName}`;
905
- const DIST_DOCS_DIR = path__default.join(DIST_DIR, "docs");
1291
+ const DIST_ENDPOINTS_DIR = path__default.join(DIST_DIR, "endpoints");
906
1292
  const DIST_DEFINITION_DIR = path__default.join(DIST_DIR, "definitions");
907
1293
  const swaggerFilePath = `${CliParser.getSwaggersOutputPath()}/${swaggerFile}`;
908
1294
  const swaggerPatchedFilePath = `${CodeGenerator.getPatchedDir()}/${swaggerFile}`;
@@ -910,20 +1296,35 @@ class CodeGenerator {
910
1296
  ParserUtils.applyPatchIfExists(swaggerFilePath, swaggerPatchFilePath, swaggerPatchedFilePath, CodeGenerator.getPatchedDir());
911
1297
  const api = await parser.parse(swaggerPatchedFilePath);
912
1298
  const indexImportsSet = /* @__PURE__ */ new Set();
913
- console.log("\n----------\n API name: %s, Version: %s schemes %s", api, api.info.title, api.info.version, api.schemes);
1299
+ console.log("----------\nGenerating API:", { title: api.info.title, version: api.info.version });
914
1300
  ParserUtils.mkdirIfNotExist(DIST_DIR);
915
- ParserUtils.mkdirIfNotExist(DIST_DOCS_DIR);
916
1301
  ParserUtils.mkdirIfNotExist(DIST_DEFINITION_DIR);
917
- const { apiBufferByTag, dependenciesByTag, classImports, arrayDefinitions } = await CodeGenerator.iterateApi(api);
1302
+ ParserUtils.mkdirIfNotExist(DIST_ENDPOINTS_DIR);
1303
+ ParserUtils.syncPackageVersion(api.info, CliParser.isAdmin());
1304
+ const { apiArgumentsByTag, apiBufferByTag, classBufferByTag, dependenciesByTag, classImports, arrayDefinitions, snippetMap } = await CodeGenerator.iterateApi(api, serviceName);
1305
+ if (CliParser.getSnippetOutputPath()) {
1306
+ ParserUtils.writeSnippetFile(CodeGenerator.getGeneratedSnippetsFolder(), api.info.title, JSON.stringify(snippetMap, null, 2));
1307
+ }
918
1308
  const targetSrcFolder = `${CliParser.getOutputPath()}/`;
919
- for (const tag in apiBufferByTag) {
920
- const className = _.upperFirst(_.camelCase(tag));
921
- const apiBuffer = apiBufferByTag[tag];
1309
+ const apiList = [];
1310
+ for (const tag in classBufferByTag) {
1311
+ const { className, classGenName } = ParserUtils.generateClassName(tag);
1312
+ const classBuffer = classBufferByTag[tag];
922
1313
  const imports = [.../* @__PURE__ */ new Set([...dependenciesByTag[tag], ...Object.values(classImports[className])])];
923
- const classGenName = CliParser.isAdmin() ? className + "Admin$" : className + "$";
924
- ParserUtils.writeClassFile(DIST_DIR, classGenName, apiBuffer, imports);
925
- indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path__default.join(DIST_DIR, `${classGenName}`), targetSrcFolder));
1314
+ const apiImports = [.../* @__PURE__ */ new Set([...dependenciesByTag[tag], ...Object.values(classImports[className])])];
1315
+ apiImports.push(`import { ${classGenName} } from './endpoints/${classGenName}'`);
1316
+ ParserUtils.writeClassFile(DIST_ENDPOINTS_DIR, classGenName, classBuffer, imports);
1317
+ const apiBuffer = apiBufferByTag[tag];
1318
+ const { apiGenName } = ParserUtils.generateApiName(tag);
1319
+ ParserUtils.writeApiFile(DIST_DIR, apiGenName, apiBuffer, apiImports, apiArgumentsByTag[tag]);
1320
+ apiList.push(apiGenName);
1321
+ indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path__default.join(DIST_ENDPOINTS_DIR, `${classGenName}`), targetSrcFolder));
1322
+ indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path__default.join(DIST_DIR, `${apiGenName}`), targetSrcFolder));
926
1323
  }
1324
+ const serviceNameTitle = ParserUtils.convertDashesToTitleCase(serviceName);
1325
+ const apiIndexBuff = templateApiIndex(serviceName, serviceNameTitle, apiList);
1326
+ ParserUtils.writeApiMainFile(generatedFolder, serviceNameTitle, apiIndexBuff);
1327
+ indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path__default.join(generatedFolder, serviceNameTitle), targetSrcFolder));
927
1328
  const duplicates = /* @__PURE__ */ new Map();
928
1329
  const definitions = api?.components?.schemas || api.definitions;
929
1330
  for (const ref in definitions) {
@@ -952,7 +1353,7 @@ class CodeGenerator {
952
1353
  ParserUtils.writeDefinitionFile(DIST_DEFINITION_DIR, arrayClass, buffer);
953
1354
  indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path__default.join(DIST_DEFINITION_DIR, arrayClass), targetSrcFolder));
954
1355
  }
955
- console.log("\n----------\nCOMPLETED.\n----------\n\n");
1356
+ console.log("\nCOMPLETED\n----------\n\n");
956
1357
  return indexImportsSet;
957
1358
  };
958
1359
  }
@@ -979,7 +1380,7 @@ class SwaggerDownloader {
979
1380
  });
980
1381
  });
981
1382
  request.on("error", (err) => {
982
- console.log(`SwaggerDownloader dl failed for "${targetFileName}" and "${url}"`, err);
1383
+ console.log(`SwaggerDownloader failed for "${targetFileName}" and "${url}"`, err);
983
1384
  });
984
1385
  };
985
1386
  static main = () => {