@asad_dev/leo-generator 1.6.3 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  All notable changes to Leo Generate will be documented in this file.
4
4
 
5
+ ## [1.7.0] - 2025-12-28
6
+
7
+ ### 🌟 New: Smart Postman Documentation
8
+ - **Intelligent Endpoint Merging**: Manually added endpoints in Postman are now preserved during `update-docs` or new module generation.
9
+ - **Improved Sync Logic**: The generator now merges endpoints instead of overwriting the entire module folder, ensuring manual customizations are never lost.
10
+ - **Cloud & Local Support**: Merge logic works perfectly for both local `.postman_collection.json` files and automated Postman Cloud sync.
11
+
12
+ ### 🚀 New: Postman Export Feature
13
+ - **Pull Collection Command**: Added `pull-postman` (aliases: `pull`, `export`) to fetch your entire collection from Postman Cloud and save it as a local JSON file.
14
+ - **Backup & Portability**: Easily backup your cloud collection or export it for use in other tools with a single command.
15
+
16
+ ### 🛡️ Core Improvements
17
+ - **Console Feedback**: Added detailed logging during the merge process to show which manual endpoints were preserved.
18
+ - **Enhanced Reliability**: Improved error handling for Postman API interactions to prevent data loss.
19
+
5
20
  ## [1.6.0] - 2025-12-28
6
21
 
7
22
  ### 🌟 New: Asynchronous File Cleanup
@@ -134,6 +134,12 @@ leo-generate --version
134
134
  | `--postman-dir <path>` | Custom Postman output directory | `postman` | `--postman-dir collections` |
135
135
  | `--swagger-file <path>` | Custom Swagger file path | `swagger.json` | `--swagger-file api-spec.json` |
136
136
 
137
+ ### **Pull-Postman Command Options**
138
+
139
+ | Option | Description | Default | Example |
140
+ |--------|-------------|---------|---------|
141
+ | `-o, --output <path>` | Custom output file path | `postman/full_collection...` | `-o ./my_collection.json` |
142
+
137
143
  ## 🏗️ **Field Type Syntax**
138
144
 
139
145
  ### **Basic Types**
@@ -382,6 +388,9 @@ leo-generate generate User name:string --no-postman --no-swagger
382
388
  | `<name> [fields...]` | Legacy syntax | Same as generate |
383
389
  | `update-docs` | Update all module documentation | Updated Postman + Swagger |
384
390
  | `docs` | Alias for update-docs | Same as update-docs |
391
+ | `pull-postman` | Export collection from cloud | Full collection JSON file |
392
+ | `pull` | Alias for pull-postman | Same as pull-postman |
393
+ | `export` | Alias for pull-postman | Same as pull-postman |
385
394
  | `--help` | Show help information | Help text |
386
395
  | `--version` | Show version information | Version number |
387
396
 
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Leo Generate - Enhanced Module Generator (v1.6.0)
1
+ # Leo Generate - Enhanced Module Generator (v1.7.0)
2
2
 
3
3
  A powerful, production-ready module generator for Express.js applications with Mongoose models, automated Postman Cloud sync, and Swagger documentation.
4
4
 
@@ -70,6 +70,7 @@ leo-generate User name:string email:string age:number
70
70
  | `g <name> [fields...]` | Short alias for generate | `leo-generate g Product name:string price:number` |
71
71
  | `update-docs` | Update all documentation | `leo-generate update-docs` |
72
72
  | `docs` | Short alias for update-docs | `leo-generate docs` |
73
+ | `pull-postman` | Fetch collection from cloud | `leo-generate pull-postman` |
73
74
  | `<name> [fields...]` | Legacy syntax | `leo-generate User name:string email:string` |
74
75
 
75
76
  ### **Common Options**
@@ -364,6 +365,19 @@ leo-generate generate User name:string \
364
365
  --swagger-file api-docs.json
365
366
  ```
366
367
 
368
+ ### Export Full Postman Collection
369
+ ```bash
370
+ # Fetches the entire collection from Postman Cloud and saves it locally
371
+ leo-generate pull-postman
372
+
373
+ # Aliases:
374
+ leo-generate pull
375
+ leo-generate export
376
+
377
+ # Options:
378
+ -o, --output <path> # Custom output file path (default: postman/full_collection.postman_collection.json)
379
+ ```
380
+
367
381
  ## 🧠 Enhanced Documentation Intelligence (v1.2.0)
368
382
 
369
383
  ### **Code-Aware Documentation Updates**
package/dist/index.js CHANGED
@@ -51,6 +51,8 @@ const fieldParser_1 = require("./utils/fieldParser");
51
51
  const interfaceGenerator_1 = require("./utils/interfaceGenerator");
52
52
  const documentationUpdater_1 = require("./utils/documentationUpdater");
53
53
  const helperGenerator_1 = require("./utils/helperGenerator");
54
+ const postmanApi_1 = require("./utils/postmanApi");
55
+ const postmanGenerator_1 = require("./utils/postmanGenerator");
54
56
  // Import template generators
55
57
  const route_template_1 = require("./templates/route.template");
56
58
  const constants_template_1 = require("./templates/constants.template");
@@ -550,6 +552,28 @@ function main() {
550
552
  postmanCollectionId: config.postmanCollectionId
551
553
  }, modules);
552
554
  }));
555
+ program
556
+ .command("pull-postman")
557
+ .alias("pull")
558
+ .alias("export")
559
+ .description("Fetch and save the full Postman collection from the cloud")
560
+ .option("-o, --output <path>", "Custom output file path", "postman/full_collection.postman_collection.json")
561
+ .action((options) => __awaiter(this, void 0, void 0, function* () {
562
+ if (!config.postmanApiKey || !config.postmanCollectionId) {
563
+ console.error("❌ Postman API Key and Collection ID are required in .env or package.json");
564
+ return;
565
+ }
566
+ try {
567
+ const collection = yield (0, postmanApi_1.fetchPostmanCollection)({
568
+ apiKey: config.postmanApiKey,
569
+ collectionId: config.postmanCollectionId
570
+ });
571
+ (0, postmanGenerator_1.saveFullPostmanCollection)(collection, options.output);
572
+ }
573
+ catch (error) {
574
+ console.error("❌ Error pulling Postman collection:", error instanceof Error ? error.message : String(error));
575
+ }
576
+ }));
553
577
  // Legacy support - direct module generation (backward compatibility)
554
578
  program
555
579
  .argument("[name]", "Module name (legacy support)")
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.updatePostmanCollectionViaApi = updatePostmanCollectionViaApi;
13
+ exports.fetchPostmanCollection = fetchPostmanCollection;
13
14
  /**
14
15
  * Updates an existing Postman collection via the Postman API.
15
16
  * It fetches the existing collection, finds the folder for the current module,
@@ -70,10 +71,6 @@ function updatePostmanCollectionViaApi(moduleName, newCollectionData, config) {
70
71
  // 2. Prepare the new folder for this module
71
72
  // The generator creates a collection where 'item' is the list of requests
72
73
  const moduleFolderName = `${moduleName} API`;
73
- const newModuleFolder = {
74
- name: moduleFolderName,
75
- item: newCollectionData.item
76
- };
77
74
  // 3. Update or Add the folder in the existing collection
78
75
  if (!collection.item) {
79
76
  collection.item = [];
@@ -81,11 +78,27 @@ function updatePostmanCollectionViaApi(moduleName, newCollectionData, config) {
81
78
  const existingFolderIndex = collection.item.findIndex((item) => item.name === moduleFolderName);
82
79
  if (existingFolderIndex !== -1) {
83
80
  console.log(`📝 Updating existing folder: ${moduleFolderName} in Postman collection`);
84
- collection.item[existingFolderIndex] = newModuleFolder;
81
+ // Merge logic: keep existing items that aren't being updated
82
+ const existingFolder = collection.item[existingFolderIndex];
83
+ const existingItems = existingFolder.item || [];
84
+ const newItems = newCollectionData.item;
85
+ // Create a map of new items by name for quick lookup
86
+ const newItemsMap = new Map(newItems.map((item) => [item.name, item]));
87
+ // Updated items list:
88
+ // 1. All new items (will overwrite existing ones with the same name)
89
+ // 2. Existing items that ARE NOT in the new list (manual endpoints)
90
+ const manualItems = existingItems.filter((existingItem) => !newItemsMap.has(existingItem.name));
91
+ if (manualItems.length > 0) {
92
+ console.log(` ✨ Preserving ${manualItems.length} manual endpoints: ${manualItems.map((m) => m.name).join(', ')}`);
93
+ }
94
+ collection.item[existingFolderIndex] = Object.assign(Object.assign({}, existingFolder), { item: [...newItems, ...manualItems] });
85
95
  }
86
96
  else {
87
97
  console.log(`➕ Adding new folder: ${moduleFolderName} to Postman collection`);
88
- collection.item.push(newModuleFolder);
98
+ collection.item.push({
99
+ name: moduleFolderName,
100
+ item: newCollectionData.item
101
+ });
89
102
  }
90
103
  // Debug: Log a snippet of what we are sending
91
104
  console.log(`📤 Sending PUT request to Postman API...`);
@@ -111,3 +124,30 @@ function updatePostmanCollectionViaApi(moduleName, newCollectionData, config) {
111
124
  }
112
125
  });
113
126
  }
127
+ /**
128
+ * Fetches the complete Postman collection from the API.
129
+ */
130
+ function fetchPostmanCollection(config) {
131
+ return __awaiter(this, void 0, void 0, function* () {
132
+ const { apiKey, collectionId } = config;
133
+ const url = `https://api.getpostman.com/collections/${collectionId}`;
134
+ try {
135
+ console.log(`🚀 Fetching collection from Postman API: ${collectionId}...`);
136
+ const response = yield fetch(url, {
137
+ headers: {
138
+ "X-Api-Key": apiKey
139
+ }
140
+ });
141
+ if (!response.ok) {
142
+ const errorData = yield response.json();
143
+ throw new Error(`Failed to fetch collection: ${JSON.stringify(errorData)}`);
144
+ }
145
+ const data = yield response.json();
146
+ return data.collection;
147
+ }
148
+ catch (error) {
149
+ console.error("❌ Error fetching Postman collection:", error instanceof Error ? error.message : String(error));
150
+ throw error;
151
+ }
152
+ });
153
+ }
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.generatePostmanCollection = generatePostmanCollection;
37
37
  exports.savePostmanCollection = savePostmanCollection;
38
+ exports.saveFullPostmanCollection = saveFullPostmanCollection;
38
39
  const fs = __importStar(require("fs"));
39
40
  const path = __importStar(require("path"));
40
41
  // Helper to generate Pre-request Script
@@ -220,8 +221,33 @@ function savePostmanCollection(moduleName, collection, outputDir = "postman") {
220
221
  fs.mkdirSync(postmanDir, { recursive: true });
221
222
  }
222
223
  const filePath = path.join(postmanDir, `${camelCaseName.toLowerCase()}.postman_collection.json`);
223
- fs.writeFileSync(filePath, JSON.stringify(collection, null, 2));
224
- console.log(`✅ Postman collection created: ${filePath}`);
224
+ let finalCollection = collection;
225
+ // Check if the file already exists to merge endpoints
226
+ if (fs.existsSync(filePath)) {
227
+ try {
228
+ const existingContent = fs.readFileSync(filePath, "utf-8");
229
+ const existingCollection = JSON.parse(existingContent);
230
+ if (existingCollection.item && Array.isArray(existingCollection.item)) {
231
+ console.log(`🔄 Merging with existing local collection: ${filePath}`);
232
+ const existingItems = existingCollection.item;
233
+ const newItems = collection.item;
234
+ // Create a map of new items by name
235
+ const newItemsMap = new Map(newItems.map((item) => [item.name, item]));
236
+ // Keep manual items (those not in the generated set)
237
+ const manualItems = existingItems.filter((existingItem) => !newItemsMap.has(existingItem.name));
238
+ if (manualItems.length > 0) {
239
+ console.log(` ✨ Preserving ${manualItems.length} manual endpoints in local file: ${manualItems.map((m) => m.name).join(', ')}`);
240
+ }
241
+ // Final items = new generated items + manual items
242
+ finalCollection = Object.assign(Object.assign({}, collection), { item: [...newItems, ...manualItems] });
243
+ }
244
+ }
245
+ catch (error) {
246
+ console.warn(`⚠️ Could not parse existing Postman collection at ${filePath}, overwriting instead.`);
247
+ }
248
+ }
249
+ fs.writeFileSync(filePath, JSON.stringify(finalCollection, null, 2));
250
+ console.log(`✅ Postman collection ${fs.existsSync(filePath) ? 'updated' : 'created'}: ${filePath}`);
225
251
  }
226
252
  function generateSampleData(fields, prefix = "") {
227
253
  const sampleData = {};
@@ -281,3 +307,23 @@ function generateUpdateSampleData(fields) {
281
307
  function toCamelCase(str) {
282
308
  return str.charAt(0).toUpperCase() + str.slice(1);
283
309
  }
310
+ /**
311
+ * Saves a full Postman collection to a specific file.
312
+ */
313
+ function saveFullPostmanCollection(collection, outputFilePath = "postman/full_collection.postman_collection.json") {
314
+ try {
315
+ const fullPath = path.isAbsolute(outputFilePath)
316
+ ? outputFilePath
317
+ : path.join(process.cwd(), outputFilePath);
318
+ const dir = path.dirname(fullPath);
319
+ if (!fs.existsSync(dir)) {
320
+ fs.mkdirSync(dir, { recursive: true });
321
+ }
322
+ fs.writeFileSync(fullPath, JSON.stringify({ collection }, null, 2));
323
+ console.log(`✅ Full Postman collection exported to: ${fullPath}`);
324
+ }
325
+ catch (error) {
326
+ console.error("❌ Error saving full Postman collection:", error instanceof Error ? error.message : String(error));
327
+ throw error;
328
+ }
329
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asad_dev/leo-generator",
3
- "version": "1.6.3",
3
+ "version": "1.7.0",
4
4
  "description": "Enhanced Express module generator with Mongoose models, automated Postman Cloud sync, and Swagger documentation",
5
5
  "main": "dist/index.js",
6
6
  "bin": {