@currentjs/gen 0.2.2 → 0.3.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +256 -0
  3. package/dist/cli.js +26 -0
  4. package/dist/commands/createApp.js +2 -0
  5. package/dist/commands/generateAll.js +153 -29
  6. package/dist/commands/migrateCommit.d.ts +1 -0
  7. package/dist/commands/migrateCommit.js +201 -0
  8. package/dist/generators/controllerGenerator.d.ts +7 -0
  9. package/dist/generators/controllerGenerator.js +60 -29
  10. package/dist/generators/domainModelGenerator.d.ts +7 -0
  11. package/dist/generators/domainModelGenerator.js +57 -3
  12. package/dist/generators/serviceGenerator.d.ts +16 -1
  13. package/dist/generators/serviceGenerator.js +125 -12
  14. package/dist/generators/storeGenerator.d.ts +8 -0
  15. package/dist/generators/storeGenerator.js +133 -7
  16. package/dist/generators/templateGenerator.d.ts +19 -0
  17. package/dist/generators/templateGenerator.js +216 -11
  18. package/dist/generators/templates/appTemplates.d.ts +8 -7
  19. package/dist/generators/templates/appTemplates.js +11 -1572
  20. package/dist/generators/templates/data/appTsTemplate +39 -0
  21. package/dist/generators/templates/data/appYamlTemplate +4 -0
  22. package/dist/generators/templates/data/cursorRulesTemplate +671 -0
  23. package/dist/generators/templates/data/errorTemplate +28 -0
  24. package/dist/generators/templates/data/frontendScriptTemplate +739 -0
  25. package/dist/generators/templates/data/mainViewTemplate +16 -0
  26. package/dist/generators/templates/data/translationsTemplate +68 -0
  27. package/dist/generators/templates/data/tsConfigTemplate +19 -0
  28. package/dist/generators/templates/viewTemplates.d.ts +10 -1
  29. package/dist/generators/templates/viewTemplates.js +138 -6
  30. package/dist/generators/validationGenerator.d.ts +5 -0
  31. package/dist/generators/validationGenerator.js +51 -0
  32. package/dist/utils/constants.d.ts +3 -0
  33. package/dist/utils/constants.js +5 -2
  34. package/dist/utils/migrationUtils.d.ts +49 -0
  35. package/dist/utils/migrationUtils.js +291 -0
  36. package/howto.md +157 -65
  37. package/package.json +3 -2
@@ -0,0 +1,291 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.mapYamlTypeToSql = mapYamlTypeToSql;
37
+ exports.getTableName = getTableName;
38
+ exports.getForeignKeyFieldName = getForeignKeyFieldName;
39
+ exports.isRelationshipField = isRelationshipField;
40
+ exports.generateCreateTableSQL = generateCreateTableSQL;
41
+ exports.generateDropTableSQL = generateDropTableSQL;
42
+ exports.generateAddColumnSQL = generateAddColumnSQL;
43
+ exports.generateDropColumnSQL = generateDropColumnSQL;
44
+ exports.generateModifyColumnSQL = generateModifyColumnSQL;
45
+ exports.loadSchemaState = loadSchemaState;
46
+ exports.saveSchemaState = saveSchemaState;
47
+ exports.loadMigrationLog = loadMigrationLog;
48
+ exports.saveMigrationLog = saveMigrationLog;
49
+ exports.compareSchemas = compareSchemas;
50
+ exports.generateTimestamp = generateTimestamp;
51
+ exports.getMigrationFileName = getMigrationFileName;
52
+ const fs = __importStar(require("fs"));
53
+ const path = __importStar(require("path"));
54
+ const yaml_1 = require("yaml");
55
+ const TYPE_MAPPING = {
56
+ string: 'VARCHAR(255)',
57
+ number: 'INT',
58
+ boolean: 'TINYINT(1)',
59
+ datetime: 'DATETIME',
60
+ json: 'JSON',
61
+ array: 'JSON',
62
+ object: 'JSON'
63
+ };
64
+ function mapYamlTypeToSql(yamlType, availableModels) {
65
+ // Check if this is a relationship (foreign key)
66
+ if (availableModels.has(yamlType)) {
67
+ return 'INT'; // Foreign keys are INT
68
+ }
69
+ return TYPE_MAPPING[yamlType] || 'VARCHAR(255)';
70
+ }
71
+ function getTableName(modelName) {
72
+ return modelName.toLowerCase() + 's';
73
+ }
74
+ function getForeignKeyFieldName(fieldName) {
75
+ return fieldName + 'Id';
76
+ }
77
+ function isRelationshipField(fieldType, availableModels) {
78
+ return availableModels.has(fieldType);
79
+ }
80
+ function generateCreateTableSQL(model, availableModels) {
81
+ const tableName = getTableName(model.name);
82
+ const columns = [];
83
+ const indexes = [];
84
+ const foreignKeys = [];
85
+ // Add id column
86
+ columns.push(' id INT AUTO_INCREMENT PRIMARY KEY');
87
+ // Add model fields
88
+ model.fields.forEach(field => {
89
+ if (isRelationshipField(field.type, availableModels)) {
90
+ // Foreign key field
91
+ const foreignKeyName = getForeignKeyFieldName(field.name);
92
+ const nullable = field.required === false ? 'NULL DEFAULT NULL' : 'NOT NULL';
93
+ columns.push(` ${foreignKeyName} INT ${nullable}`);
94
+ // Add index for foreign key
95
+ indexes.push(` INDEX idx_${tableName}_${foreignKeyName} (${foreignKeyName})`);
96
+ // Add foreign key constraint
97
+ const refTableName = getTableName(field.type);
98
+ foreignKeys.push(` CONSTRAINT fk_${tableName}_${foreignKeyName} \n` +
99
+ ` FOREIGN KEY (${foreignKeyName}) \n` +
100
+ ` REFERENCES ${refTableName}(id) \n` +
101
+ ` ON DELETE RESTRICT \n` +
102
+ ` ON UPDATE CASCADE`);
103
+ }
104
+ else {
105
+ // Regular field
106
+ const sqlType = mapYamlTypeToSql(field.type, availableModels);
107
+ const nullable = field.required === false ? 'NULL DEFAULT NULL' : 'NOT NULL';
108
+ columns.push(` ${field.name} ${sqlType} ${nullable}`);
109
+ // Add index for filterable fields
110
+ if (['string', 'number'].includes(field.type)) {
111
+ indexes.push(` INDEX idx_${tableName}_${field.name} (${field.name})`);
112
+ }
113
+ }
114
+ });
115
+ // Add standard timestamp columns
116
+ columns.push(' created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP');
117
+ columns.push(' updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
118
+ columns.push(' deleted_at DATETIME NULL DEFAULT NULL');
119
+ // Add standard indexes
120
+ indexes.push(` INDEX idx_${tableName}_deleted_at (deleted_at)`);
121
+ indexes.push(` INDEX idx_${tableName}_created_at (created_at)`);
122
+ // Combine all parts
123
+ const allParts = [...columns, ...indexes, ...foreignKeys];
124
+ const sql = `CREATE TABLE IF NOT EXISTS ${tableName} (\n${allParts.join(',\n')}\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`;
125
+ return sql;
126
+ }
127
+ function generateDropTableSQL(tableName) {
128
+ return `DROP TABLE IF EXISTS ${tableName};`;
129
+ }
130
+ function generateAddColumnSQL(tableName, field, availableModels) {
131
+ if (isRelationshipField(field.type, availableModels)) {
132
+ const foreignKeyName = getForeignKeyFieldName(field.name);
133
+ const nullable = field.required === false ? 'NULL DEFAULT NULL' : 'NOT NULL';
134
+ const sqlType = 'INT';
135
+ return `ALTER TABLE ${tableName} ADD COLUMN ${foreignKeyName} ${sqlType} ${nullable};`;
136
+ }
137
+ else {
138
+ const sqlType = mapYamlTypeToSql(field.type, availableModels);
139
+ const nullable = field.required === false ? 'NULL DEFAULT NULL' : 'NOT NULL';
140
+ return `ALTER TABLE ${tableName} ADD COLUMN ${field.name} ${sqlType} ${nullable};`;
141
+ }
142
+ }
143
+ function generateDropColumnSQL(tableName, columnName) {
144
+ return `ALTER TABLE ${tableName} DROP COLUMN ${columnName};`;
145
+ }
146
+ function generateModifyColumnSQL(tableName, field, availableModels) {
147
+ if (isRelationshipField(field.type, availableModels)) {
148
+ const foreignKeyName = getForeignKeyFieldName(field.name);
149
+ const nullable = field.required === false ? 'NULL DEFAULT NULL' : 'NOT NULL';
150
+ const sqlType = 'INT';
151
+ return `ALTER TABLE ${tableName} MODIFY COLUMN ${foreignKeyName} ${sqlType} ${nullable};`;
152
+ }
153
+ else {
154
+ const sqlType = mapYamlTypeToSql(field.type, availableModels);
155
+ const nullable = field.required === false ? 'NULL DEFAULT NULL' : 'NOT NULL';
156
+ return `ALTER TABLE ${tableName} MODIFY COLUMN ${field.name} ${sqlType} ${nullable};`;
157
+ }
158
+ }
159
+ function loadSchemaState(stateFilePath) {
160
+ if (!fs.existsSync(stateFilePath)) {
161
+ return null;
162
+ }
163
+ const content = fs.readFileSync(stateFilePath, 'utf8');
164
+ return (0, yaml_1.parse)(content);
165
+ }
166
+ function saveSchemaState(stateFilePath, state) {
167
+ fs.mkdirSync(path.dirname(stateFilePath), { recursive: true });
168
+ fs.writeFileSync(stateFilePath, (0, yaml_1.stringify)(state));
169
+ }
170
+ function loadMigrationLog(logFilePath) {
171
+ if (!fs.existsSync(logFilePath)) {
172
+ return { appliedMigrations: [] };
173
+ }
174
+ const content = fs.readFileSync(logFilePath, 'utf8');
175
+ return JSON.parse(content);
176
+ }
177
+ function saveMigrationLog(logFilePath, log) {
178
+ fs.mkdirSync(path.dirname(logFilePath), { recursive: true });
179
+ fs.writeFileSync(logFilePath, JSON.stringify(log, null, 2));
180
+ }
181
+ /**
182
+ * Sort models by dependencies so tables are created in the right order
183
+ * (tables with no foreign keys first, then tables that depend on them)
184
+ */
185
+ function sortModelsByDependencies(models, availableModels) {
186
+ const sorted = [];
187
+ const processed = new Set();
188
+ const addModel = (model) => {
189
+ if (processed.has(model.name))
190
+ return;
191
+ // Find foreign key dependencies
192
+ const dependencies = [];
193
+ model.fields.forEach(field => {
194
+ if (isRelationshipField(field.type, availableModels)) {
195
+ dependencies.push(field.type);
196
+ }
197
+ });
198
+ // Add dependencies first
199
+ dependencies.forEach(depName => {
200
+ const depModel = models.find(m => m.name === depName);
201
+ if (depModel && !processed.has(depName)) {
202
+ addModel(depModel);
203
+ }
204
+ });
205
+ // Then add this model
206
+ sorted.push(model);
207
+ processed.add(model.name);
208
+ };
209
+ models.forEach(model => addModel(model));
210
+ return sorted;
211
+ }
212
+ function compareSchemas(oldState, newModels) {
213
+ const sqlStatements = [];
214
+ const availableModels = new Set(newModels.map(m => m.name));
215
+ if (!oldState || oldState.models.length === 0) {
216
+ // No previous state - generate all CREATE TABLE statements in dependency order
217
+ const sortedModels = sortModelsByDependencies(newModels, availableModels);
218
+ sortedModels.forEach(model => {
219
+ sqlStatements.push(`-- Create ${model.name.toLowerCase()}s table`);
220
+ sqlStatements.push(generateCreateTableSQL(model, availableModels));
221
+ sqlStatements.push('');
222
+ });
223
+ return sqlStatements;
224
+ }
225
+ // Create maps for easy lookup
226
+ const oldModelsMap = new Map(oldState.models.map(m => [m.name, m]));
227
+ const newModelsMap = new Map(newModels.map(m => [m.name, m]));
228
+ // Find dropped tables
229
+ oldState.models.forEach(oldModel => {
230
+ if (!newModelsMap.has(oldModel.name)) {
231
+ const tableName = getTableName(oldModel.name);
232
+ sqlStatements.push(`-- Drop ${tableName} table`);
233
+ sqlStatements.push(generateDropTableSQL(tableName));
234
+ sqlStatements.push('');
235
+ }
236
+ });
237
+ // Find new tables and modified tables
238
+ newModels.forEach(newModel => {
239
+ const oldModel = oldModelsMap.get(newModel.name);
240
+ const tableName = getTableName(newModel.name);
241
+ if (!oldModel) {
242
+ // New table
243
+ sqlStatements.push(`-- Create ${tableName} table`);
244
+ sqlStatements.push(generateCreateTableSQL(newModel, availableModels));
245
+ sqlStatements.push('');
246
+ }
247
+ else {
248
+ // Table exists - check for column changes
249
+ const oldFieldsMap = new Map(oldModel.fields.map(f => [f.name, f]));
250
+ const newFieldsMap = new Map(newModel.fields.map(f => [f.name, f]));
251
+ // Find dropped columns
252
+ oldModel.fields.forEach(oldField => {
253
+ if (!newFieldsMap.has(oldField.name)) {
254
+ const columnName = isRelationshipField(oldField.type, availableModels)
255
+ ? getForeignKeyFieldName(oldField.name)
256
+ : oldField.name;
257
+ sqlStatements.push(`-- Drop column ${columnName} from ${tableName}`);
258
+ sqlStatements.push(generateDropColumnSQL(tableName, columnName));
259
+ sqlStatements.push('');
260
+ }
261
+ });
262
+ // Find new columns and modified columns
263
+ newModel.fields.forEach(newField => {
264
+ const oldField = oldFieldsMap.get(newField.name);
265
+ if (!oldField) {
266
+ // New column
267
+ sqlStatements.push(`-- Add column ${newField.name} to ${tableName}`);
268
+ sqlStatements.push(generateAddColumnSQL(tableName, newField, availableModels));
269
+ sqlStatements.push('');
270
+ }
271
+ else {
272
+ // Check if column definition changed
273
+ const typeChanged = oldField.type !== newField.type;
274
+ const requiredChanged = oldField.required !== newField.required;
275
+ if (typeChanged || requiredChanged) {
276
+ sqlStatements.push(`-- Modify column ${newField.name} in ${tableName}`);
277
+ sqlStatements.push(generateModifyColumnSQL(tableName, newField, availableModels));
278
+ sqlStatements.push('');
279
+ }
280
+ }
281
+ });
282
+ }
283
+ });
284
+ return sqlStatements;
285
+ }
286
+ function generateTimestamp() {
287
+ return new Date().toISOString().replace(/[:.]/g, '-').replace('T', '_').slice(0, -5);
288
+ }
289
+ function getMigrationFileName(timestamp) {
290
+ return `${timestamp}.sql`;
291
+ }
package/howto.md CHANGED
@@ -153,6 +153,110 @@ permissions: # Role-based access control
153
153
  - `number` - Numeric data (integer or float)
154
154
  - `boolean` - True/false values
155
155
  - `datetime` - Date and time values
156
+ - `ModelName` - Relationship to another model (e.g., `Owner`, `User`, `Post`)
157
+
158
+ **Multi-Model Endpoint Configuration:**
159
+
160
+ When working with multiple models in a single module, you have flexible options:
161
+
162
+ **Option 1: Per-Endpoint Model Override**
163
+
164
+ Specify `model` on individual endpoints to override the section default:
165
+
166
+ ```yaml
167
+ models:
168
+ - name: Cat
169
+ - name: Person
170
+ # do not forget to describe fields of each model
171
+
172
+ routes:
173
+ prefix: /cat
174
+ model: Cat # Default for this section
175
+ endpoints:
176
+ - path: /create
177
+ view: catCreate
178
+ # Uses Cat model
179
+
180
+ - path: /createOwner
181
+ view: ownerCreate
182
+ model: Person # Override for this endpoint
183
+ ```
184
+
185
+ **Option 2: Multiple API/Routes Sections**
186
+
187
+ Use arrays to organize endpoints by model:
188
+
189
+ ```yaml
190
+ routes:
191
+ - prefix: /cat
192
+ model: Cat
193
+ endpoints: [...]
194
+
195
+ - prefix: /person
196
+ model: Person
197
+ endpoints: [...]
198
+
199
+ # Same works for api sections
200
+ api:
201
+ - prefix: /api/cat
202
+ model: Cat
203
+ endpoints: [...]
204
+
205
+ - prefix: /api/person
206
+ model: Person
207
+ endpoints: [...]
208
+ ```
209
+
210
+ **Model Resolution Priority:**
211
+ 1. `endpoint.model` (explicit override)
212
+ 2. Inferred from action handler (e.g., `Person:default:create`)
213
+ 3. `api.model` or `routes.model` (section default)
214
+ 4. First model in `models[]` array (fallback)
215
+
216
+ **🔗 Model Relationships:**
217
+
218
+ Define relationships by using another model's name as the field type:
219
+
220
+ ```yaml
221
+ models:
222
+ - name: Owner
223
+ fields:
224
+ - name: name
225
+ type: string
226
+ required: true
227
+
228
+ - name: Cat
229
+ fields:
230
+ - name: name
231
+ type: string
232
+ required: true
233
+ - name: owner
234
+ type: Owner # Creates relationship with Owner model
235
+ required: true # Auto-generates foreign key: ownerId
236
+ displayFields: [name] # Optional: fields to show in dropdowns
237
+ ```
238
+
239
+ **Generated Behavior:**
240
+ - **Domain Model**: Rich object with `owner: Owner` (full object, not just ID)
241
+ - **DTOs**: Use `ownerId: number` for API requests
242
+ - **Database**: Stores `ownerId` foreign key column (references `Owner.id`)
243
+ - **Store**: Automatically loads the related Owner object when fetching a Cat
244
+ - **HTML Forms**: Auto-generates select dropdown with "Create New" button
245
+ - **TypeScript**: Full type safety with proper imports
246
+
247
+ **Naming Convention:**
248
+ Foreign keys are automatically generated following the pattern `fieldName + 'Id'`:
249
+ - `owner` → `ownerId`
250
+ - `author` → `authorId`
251
+ - `parentComment` → `parentCommentId`
252
+
253
+ **Optional Configuration:**
254
+ ```yaml
255
+ - name: author
256
+ type: User
257
+ required: true
258
+ displayFields: [name, email] # Show in dropdown (optional)
259
+ ```
156
260
 
157
261
  **🔄 Handler vs Action Architecture:**
158
262
  - **Handler**: Creates a separate service method (one handler = one service method)
@@ -374,13 +478,13 @@ try {
374
478
  **Basic Usage:**
375
479
  ```javascript
376
480
  // Translate strings
377
- t('Hello World') // Returns translated version or original
378
- t('Save changes')
481
+ App.lang.t('Hello World') // Returns translated version or original
482
+ App.lang.t('Save changes')
379
483
 
380
484
  // Set language
381
- setLang('pl') // Switch to Polish
382
- setLang('en') // Switch to English
383
- getCurrentLanguage() // Get current language code
485
+ App.lang.set('pl') // Switch to Polish
486
+ App.lang.set('en') // Switch to English
487
+ App.lang.get() // Get current language code
384
488
  ```
385
489
 
386
490
  **Translation File (web/translations.json):**
@@ -402,43 +506,35 @@ getCurrentLanguage() // Get current language code
402
506
 
403
507
  **Toast Notifications:**
404
508
  ```javascript
405
- showToast('Success message', 'success') // Green toast
406
- showToast('Error occurred', 'error') // Red toast
407
- showToast('Information', 'info') // Blue toast
408
- showToast('Warning', 'warning') // Yellow toast
509
+ App.ui.showToast('Success message', 'success') // Green toast
510
+ App.ui.showToast('Error occurred', 'error') // Red toast
511
+ App.ui.showToast('Information', 'info') // Blue toast
512
+ App.ui.showToast('Warning', 'warning') // Yellow toast
409
513
  ```
410
514
 
411
515
  **Inline Messages:**
412
516
  ```javascript
413
- showMessage('messageId', 'Success!', 'success')
414
- showMessage('errorContainer', 'Validation failed', 'error')
517
+ App.ui.showMessage('messageId', 'Success!', 'success')
518
+ App.ui.showMessage('errorContainer', 'Validation failed', 'error')
415
519
  ```
416
520
 
417
521
  **Modal Dialogs:**
418
522
  ```javascript
419
- showModal('confirmModal', 'Item saved successfully', 'success')
420
- showModal('errorModal', 'Operation failed', 'error')
523
+ App.ui.showModal('confirmModal', 'Item saved successfully', 'success')
524
+ App.ui.showModal('errorModal', 'Operation failed', 'error')
421
525
  ```
422
526
 
423
527
  ### Navigation & Page Actions
424
528
 
425
529
  **Navigation Functions:**
426
530
  ```javascript
427
- navigateBack() // Go back in history or to home
428
- redirectTo('/posts') // Safe redirect with validation
429
- reloadPage() // Reload with loading indicator
430
- refreshSection('#content') // Refresh specific section
431
-
432
531
  // SPA-style navigation
433
- navigateToPage('/posts/123') // Loads via AJAX, updates #main
434
- ```
532
+ App.nav.go('/posts/123') // Loads via AJAX, updates #main
435
533
 
436
- **Content Management:**
437
- ```javascript
438
- updateContent('#results', newHtml, 'replace') // Replace content
439
- updateContent('#list', itemHtml, 'append') // Add to end
440
- updateContent('#list', itemHtml, 'prepend') // Add to beginning
441
- removeElement('#item-123') // Animate and remove
534
+ // Or use native browser APIs directly
535
+ window.history.back() // Go back in history
536
+ window.location.href = '/posts' // Full page redirect
537
+ window.location.reload() // Reload page
442
538
  ```
443
539
 
444
540
  ### Form Handling & Strategy System
@@ -468,52 +564,26 @@ removeElement('#item-123') // Animate and remove
468
564
  **Manual Form Submission:**
469
565
  ```javascript
470
566
  const form = document.querySelector('#myForm');
471
- submitForm(form, ['toast', 'back'], {
567
+ App.nav.submit(form, ['toast', 'back'], {
472
568
  entityName: 'Post',
473
569
  basePath: '/posts',
474
570
  messageId: 'form-message'
475
571
  });
476
572
  ```
477
573
 
478
- **Success Handling:**
479
- ```javascript
480
- handleFormSuccess(response, ['toast', 'back'], {
481
- entityName: 'Post',
482
- basePath: '/posts',
483
- messageId: 'success-msg',
484
- modalId: 'success-modal'
485
- });
486
- ```
487
-
488
- ### Form Validation & Type Conversion
489
-
490
- **Client-side Validation Setup:**
491
- ```javascript
492
- setupFormValidation('#createForm'); // Adds required field validation
493
- ```
494
-
495
- **Field Type Conversion:**
496
- ```javascript
497
- // Automatic conversion based on data-field-types
498
- convertFieldValue('123', 'number') // Returns 123 (number)
499
- convertFieldValue('true', 'boolean') // Returns true (boolean)
500
- convertFieldValue('text', 'string') // Returns 'text' (string)
501
- ```
502
-
503
574
  ### Loading States & Utilities
504
575
 
505
576
  **Loading Indicators:**
506
577
  ```javascript
507
- showLoading('#form') // Show spinner on form
508
- hideLoading('#form') // Hide spinner
509
- showLoading('#main') // Show spinner on main content
578
+ App.ui.showLoading('#form') // Show spinner on form
579
+ App.ui.hideLoading('#form') // Hide spinner
580
+ App.ui.showLoading('#main') // Show spinner on main content
510
581
  ```
511
582
 
512
583
  **Utility Functions:**
513
584
  ```javascript
514
- debounce(searchFunction, 300) // Debounce for search inputs
515
- getElementSafely('#selector') // Safe element selection
516
- clearForm('#myForm') // Reset form and clear validation
585
+ App.utils.debounce(searchFunction, 300) // Debounce for search inputs
586
+ App.utils.$('#selector') // Safe element selection
517
587
  ```
518
588
 
519
589
  ### Event Handling & SPA Integration
@@ -531,12 +601,12 @@ clearForm('#myForm') // Reset form and clear validation
531
601
  **Custom Event Listeners:**
532
602
  ```javascript
533
603
  // Re-initialize after dynamic content loading
534
- initializeEventListeners();
604
+ App.utils.initializeEventListeners();
535
605
 
536
606
  // Handle specific link navigation
537
607
  document.querySelector('#myLink').addEventListener('click', (e) => {
538
608
  e.preventDefault();
539
- navigateToPage('/custom/path');
609
+ App.nav.go('/custom/path');
540
610
  });
541
611
  ```
542
612
 
@@ -545,11 +615,33 @@ document.querySelector('#myLink').addEventListener('click', (e) => {
545
615
  **Accessing Functions:**
546
616
  ```javascript
547
617
  // All functions available under window.App
548
- App.showToast('Message', 'success');
549
- App.navigateBack();
550
- App.t('Translate this');
551
- App.setLang('pl');
552
- App.showLoading('#content');
618
+ // Organized by category for better discoverability
619
+
620
+ // UI Functions
621
+ App.ui.showToast('Message', 'success');
622
+ App.ui.showMessage('elementId', 'Success!', 'success');
623
+ App.ui.showModal('modalId', 'Done!', 'success');
624
+ App.ui.showLoading('#form');
625
+ App.ui.hideLoading('#form');
626
+
627
+ // Navigation Functions
628
+ App.nav.go('/posts/123'); // SPA-style navigation with AJAX
629
+ App.nav.submit(formElement, ['toast', 'back'], options); // Submit form via AJAX
630
+
631
+ // Translation Functions
632
+ App.lang.t('Translate this'); // Translate string
633
+ App.lang.set('pl'); // Set language
634
+ App.lang.get(); // Get current language
635
+
636
+ // Utility Functions
637
+ App.utils.$('#selector'); // Safe element selection
638
+ App.utils.debounce(fn, 300); // Debounce function
639
+ App.utils.initializeEventListeners(); // Re-initialize after dynamic content
640
+
641
+ // Authentication Functions (JWT)
642
+ App.auth.setAuthToken(token); // Store JWT token
643
+ App.auth.clearAuthToken(); // Remove JWT token
644
+ App.auth.buildAuthHeaders(additionalHeaders); // Build headers with auth token
553
645
  ```
554
646
 
555
647
  ### Template Data Binding
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@currentjs/gen",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "CLI code generator",
5
5
  "license": "LGPL-3.0",
6
6
  "author": "Konstantin Zavalny",
@@ -27,7 +27,8 @@
27
27
  "CHANGELOG.md"
28
28
  ],
29
29
  "scripts": {
30
- "build": "tsc -p tsconfig.json",
30
+ "build": "tsc -p tsconfig.json && npm run copy-templates",
31
+ "copy-templates": "mkdir -p dist/generators/templates/data && cp src/generators/templates/data/* dist/generators/templates/data/",
31
32
  "clean": "rm -rf dist",
32
33
  "prepack": "npm run build"
33
34
  },