@currentjs/gen 0.3.1 → 0.5.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 +8 -289
- package/README.md +623 -427
- package/dist/cli.js +2 -1
- package/dist/commands/commit.js +25 -42
- package/dist/commands/createApp.js +1 -0
- package/dist/commands/createModule.js +151 -45
- package/dist/commands/diff.js +27 -40
- package/dist/commands/generateAll.js +141 -291
- package/dist/commands/migrateCommit.js +6 -18
- package/dist/commands/migratePush.d.ts +1 -0
- package/dist/commands/migratePush.js +135 -0
- package/dist/commands/migrateUpdate.d.ts +1 -0
- package/dist/commands/migrateUpdate.js +147 -0
- package/dist/commands/newGenerateAll.d.ts +4 -0
- package/dist/commands/newGenerateAll.js +336 -0
- package/dist/generators/controllerGenerator.d.ts +43 -19
- package/dist/generators/controllerGenerator.js +547 -329
- package/dist/generators/domainLayerGenerator.d.ts +21 -0
- package/dist/generators/domainLayerGenerator.js +276 -0
- package/dist/generators/dtoGenerator.d.ts +21 -0
- package/dist/generators/dtoGenerator.js +518 -0
- package/dist/generators/newControllerGenerator.d.ts +55 -0
- package/dist/generators/newControllerGenerator.js +644 -0
- package/dist/generators/newServiceGenerator.d.ts +19 -0
- package/dist/generators/newServiceGenerator.js +266 -0
- package/dist/generators/newStoreGenerator.d.ts +39 -0
- package/dist/generators/newStoreGenerator.js +408 -0
- package/dist/generators/newTemplateGenerator.d.ts +29 -0
- package/dist/generators/newTemplateGenerator.js +510 -0
- package/dist/generators/serviceGenerator.d.ts +16 -51
- package/dist/generators/serviceGenerator.js +167 -586
- package/dist/generators/storeGenerator.d.ts +35 -32
- package/dist/generators/storeGenerator.js +291 -238
- package/dist/generators/storeGeneratorV2.d.ts +31 -0
- package/dist/generators/storeGeneratorV2.js +190 -0
- package/dist/generators/templateGenerator.d.ts +21 -21
- package/dist/generators/templateGenerator.js +393 -268
- package/dist/generators/templates/appTemplates.d.ts +3 -1
- package/dist/generators/templates/appTemplates.js +15 -10
- package/dist/generators/templates/data/appYamlTemplate +5 -2
- package/dist/generators/templates/data/cursorRulesTemplate +315 -221
- package/dist/generators/templates/data/frontendScriptTemplate +76 -47
- package/dist/generators/templates/data/mainViewTemplate +1 -1
- package/dist/generators/templates/data/systemTsTemplate +5 -0
- package/dist/generators/templates/index.d.ts +0 -3
- package/dist/generators/templates/index.js +0 -3
- package/dist/generators/templates/newStoreTemplates.d.ts +5 -0
- package/dist/generators/templates/newStoreTemplates.js +141 -0
- package/dist/generators/templates/storeTemplates.d.ts +1 -5
- package/dist/generators/templates/storeTemplates.js +102 -219
- package/dist/generators/templates/viewTemplates.js +1 -1
- package/dist/generators/useCaseGenerator.d.ts +13 -0
- package/dist/generators/useCaseGenerator.js +188 -0
- package/dist/types/configTypes.d.ts +148 -0
- package/dist/types/configTypes.js +10 -0
- package/dist/utils/childEntityUtils.d.ts +18 -0
- package/dist/utils/childEntityUtils.js +78 -0
- package/dist/utils/commandUtils.d.ts +43 -0
- package/dist/utils/commandUtils.js +124 -0
- package/dist/utils/commitUtils.d.ts +4 -1
- package/dist/utils/constants.d.ts +10 -0
- package/dist/utils/constants.js +13 -1
- package/dist/utils/diResolver.d.ts +32 -0
- package/dist/utils/diResolver.js +204 -0
- package/dist/utils/new_parts_of_migrationUtils.d.ts +0 -0
- package/dist/utils/new_parts_of_migrationUtils.js +164 -0
- package/dist/utils/typeUtils.d.ts +19 -0
- package/dist/utils/typeUtils.js +70 -0
- package/package.json +7 -3
|
@@ -394,8 +394,7 @@ window.AppConfig = {
|
|
|
394
394
|
target.innerHTML = html;
|
|
395
395
|
// Update browser history
|
|
396
396
|
window.history.pushState({}, '', url);
|
|
397
|
-
//
|
|
398
|
-
initializeEventListeners();
|
|
397
|
+
// No need to re-initialize - delegation handles new content automatically
|
|
399
398
|
})
|
|
400
399
|
.catch(error => {
|
|
401
400
|
console.error('Navigation failed:', error);
|
|
@@ -415,7 +414,7 @@ window.AppConfig = {
|
|
|
415
414
|
* @returns {any} Converted value
|
|
416
415
|
*/
|
|
417
416
|
function convertFieldValue(value, fieldType) {
|
|
418
|
-
if (
|
|
417
|
+
if (value === undefined || value === null || value === '') {
|
|
419
418
|
return null;
|
|
420
419
|
}
|
|
421
420
|
|
|
@@ -423,6 +422,7 @@ window.AppConfig = {
|
|
|
423
422
|
case 'number':
|
|
424
423
|
case 'int':
|
|
425
424
|
case 'integer':
|
|
425
|
+
case 'id':
|
|
426
426
|
const intVal = parseInt(value, 10);
|
|
427
427
|
return isNaN(intVal) ? null : intVal;
|
|
428
428
|
|
|
@@ -433,10 +433,15 @@ window.AppConfig = {
|
|
|
433
433
|
|
|
434
434
|
case 'boolean':
|
|
435
435
|
case 'bool':
|
|
436
|
-
if (value === 'true') return true;
|
|
437
|
-
if (value === 'false') return false;
|
|
436
|
+
if (value === 'true' || value === 'on' || value === '1') return true;
|
|
437
|
+
if (value === 'false' || value === 'off' || value === '0') return false;
|
|
438
438
|
return Boolean(value);
|
|
439
|
+
|
|
440
|
+
case 'json':
|
|
441
|
+
try { return JSON.parse(value); } catch { return value; }
|
|
439
442
|
|
|
443
|
+
case 'datetime':
|
|
444
|
+
case 'date':
|
|
440
445
|
case 'enum':
|
|
441
446
|
case 'string':
|
|
442
447
|
case 'text':
|
|
@@ -475,6 +480,30 @@ window.AppConfig = {
|
|
|
475
480
|
jsonData[key] = convertedValue;
|
|
476
481
|
}
|
|
477
482
|
}
|
|
483
|
+
|
|
484
|
+
// Unchecked checkboxes are not included in FormData — default to false
|
|
485
|
+
for (const [fieldName, fieldType] of Object.entries(fieldTypes)) {
|
|
486
|
+
const ft = fieldType.toLowerCase();
|
|
487
|
+
if ((ft === 'boolean' || ft === 'bool') && !(fieldName in jsonData)) {
|
|
488
|
+
jsonData[fieldName] = false;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Unflatten dot-notation keys (e.g., "amount.currency") into nested objects
|
|
493
|
+
const finalData = {};
|
|
494
|
+
for (const [key, value] of Object.entries(jsonData)) {
|
|
495
|
+
if (key.includes('.')) {
|
|
496
|
+
const parts = key.split('.');
|
|
497
|
+
let current = finalData;
|
|
498
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
499
|
+
if (!(parts[i] in current)) current[parts[i]] = {};
|
|
500
|
+
current = current[parts[i]];
|
|
501
|
+
}
|
|
502
|
+
current[parts[parts.length - 1]] = value;
|
|
503
|
+
} else {
|
|
504
|
+
finalData[key] = value;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
478
507
|
|
|
479
508
|
const url = form.getAttribute('data-action') || form.action;
|
|
480
509
|
const method = (form.getAttribute('data-method') || form.method || 'POST').toUpperCase();
|
|
@@ -487,16 +516,20 @@ window.AppConfig = {
|
|
|
487
516
|
'Content-Type': 'application/json',
|
|
488
517
|
'Accept': 'application/json'
|
|
489
518
|
}),
|
|
490
|
-
body: JSON.stringify(
|
|
519
|
+
body: JSON.stringify(finalData)
|
|
491
520
|
})
|
|
492
|
-
.then(response =>
|
|
493
|
-
|
|
494
|
-
|
|
521
|
+
.then(response =>
|
|
522
|
+
response.json()
|
|
523
|
+
.catch(() => ({}))
|
|
524
|
+
.then(body => ({ ok: response.ok, status: response.status, statusText: response.statusText, body }))
|
|
525
|
+
)
|
|
526
|
+
.then(({ ok, status, statusText, body }) => {
|
|
527
|
+
if (!ok) {
|
|
528
|
+
const message = body.message || body.error || `HTTP ${status}: ${statusText}`;
|
|
529
|
+
handleFormError({ message }, options);
|
|
530
|
+
return;
|
|
495
531
|
}
|
|
496
|
-
|
|
497
|
-
})
|
|
498
|
-
.then(data => {
|
|
499
|
-
handleFormSuccess(data, strategy, options);
|
|
532
|
+
handleFormSuccess(body, strategy, options);
|
|
500
533
|
})
|
|
501
534
|
.catch(error => {
|
|
502
535
|
console.error('Form submission failed:', error);
|
|
@@ -616,56 +649,52 @@ window.AppConfig = {
|
|
|
616
649
|
// ===== INITIALIZATION =====
|
|
617
650
|
|
|
618
651
|
/**
|
|
619
|
-
* Initialize event
|
|
652
|
+
* Initialize event delegation for links and forms
|
|
653
|
+
* Uses document-level listeners that work for all current and future elements
|
|
620
654
|
*/
|
|
621
655
|
function initializeEventListeners() {
|
|
622
|
-
//
|
|
623
|
-
|
|
656
|
+
// Only initialize once
|
|
657
|
+
if (window._appEventListenersInitialized) return;
|
|
658
|
+
window._appEventListenersInitialized = true;
|
|
659
|
+
|
|
660
|
+
// Handle all link clicks via delegation
|
|
661
|
+
document.addEventListener('click', function(event) {
|
|
662
|
+
const link = event.target.closest('a[href]');
|
|
663
|
+
if (!link) return;
|
|
664
|
+
|
|
624
665
|
const href = link.getAttribute('href');
|
|
625
|
-
|
|
666
|
+
|
|
626
667
|
// Skip external links, anchors, and special protocols
|
|
627
|
-
if (!href ||
|
|
628
|
-
href.startsWith('http://') ||
|
|
629
|
-
href.startsWith('https://') ||
|
|
630
|
-
href.startsWith('mailto:') ||
|
|
631
|
-
href.startsWith('tel:') ||
|
|
668
|
+
if (!href ||
|
|
669
|
+
href.startsWith('http://') ||
|
|
670
|
+
href.startsWith('https://') ||
|
|
671
|
+
href.startsWith('mailto:') ||
|
|
672
|
+
href.startsWith('tel:') ||
|
|
632
673
|
href.startsWith('#') ||
|
|
633
674
|
href.startsWith('javascript:')) {
|
|
634
675
|
return;
|
|
635
676
|
}
|
|
636
|
-
|
|
637
|
-
// Remove any existing event listeners and add new one
|
|
638
|
-
link.removeEventListener('click', handleLinkClick);
|
|
639
|
-
link.addEventListener('click', handleLinkClick);
|
|
640
|
-
});
|
|
641
|
-
|
|
642
|
-
// Handle forms with strategy
|
|
643
|
-
document.querySelectorAll('form[data-strategy]').forEach(form => {
|
|
644
|
-
// Remove any existing event listeners and add new one
|
|
645
|
-
form.removeEventListener('submit', handleFormSubmit);
|
|
646
|
-
form.addEventListener('submit', handleFormSubmit);
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
677
|
|
|
650
|
-
|
|
651
|
-
* Handle link click for internal navigation
|
|
652
|
-
* @param {Event} event - Click event
|
|
653
|
-
*/
|
|
654
|
-
function handleLinkClick(event) {
|
|
655
|
-
event.preventDefault();
|
|
656
|
-
const href = event.currentTarget.getAttribute('href');
|
|
657
|
-
if (href) {
|
|
678
|
+
event.preventDefault();
|
|
658
679
|
navigateToPage(href);
|
|
659
|
-
}
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
// Handle all form submissions via delegation
|
|
683
|
+
document.addEventListener('submit', function(event) {
|
|
684
|
+
const form = event.target.closest('form[data-strategy]');
|
|
685
|
+
if (!form) return;
|
|
686
|
+
|
|
687
|
+
event.preventDefault();
|
|
688
|
+
handleFormSubmit(event, form);
|
|
689
|
+
});
|
|
660
690
|
}
|
|
661
691
|
|
|
662
692
|
/**
|
|
663
693
|
* Handle form submission
|
|
664
694
|
* @param {Event} event - Submit event
|
|
665
695
|
*/
|
|
666
|
-
function handleFormSubmit(event) {
|
|
667
|
-
event.
|
|
668
|
-
const form = event.target;
|
|
696
|
+
function handleFormSubmit(event, form) {
|
|
697
|
+
//const form = event.target.closest ? event.target.closest('form') : event.target;
|
|
669
698
|
|
|
670
699
|
// Check for confirmation message
|
|
671
700
|
const confirmMessage = form.getAttribute('data-confirm-message');
|
|
@@ -14,8 +14,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./serviceTemplates"), exports);
|
|
18
|
-
__exportStar(require("./controllerTemplates"), exports);
|
|
19
17
|
__exportStar(require("./storeTemplates"), exports);
|
|
20
|
-
__exportStar(require("./validationTemplates"), exports);
|
|
21
18
|
__exportStar(require("./appTemplates"), exports);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const newStoreTemplates: {
|
|
2
|
+
rowInterface: string;
|
|
3
|
+
storeClass: string;
|
|
4
|
+
};
|
|
5
|
+
export declare const newStoreFileTemplate = "import { {{ENTITY_NAME}} } from '../../domain/entities/{{ENTITY_NAME}}';\nimport type { ISqlProvider } from '@currentjs/provider-mysql';{{VALUE_OBJECT_IMPORTS}}\n\n{{ROW_INTERFACE}}\n\n{{STORE_CLASS}}\n";
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.newStoreFileTemplate = exports.newStoreTemplates = void 0;
|
|
4
|
+
exports.newStoreTemplates = {
|
|
5
|
+
rowInterface: `export interface {{ENTITY_NAME}}Row {
|
|
6
|
+
id: number;
|
|
7
|
+
{{ROW_FIELDS}}
|
|
8
|
+
created_at: string;
|
|
9
|
+
updated_at: string;
|
|
10
|
+
deleted_at?: string;
|
|
11
|
+
}`,
|
|
12
|
+
storeClass: `/**
|
|
13
|
+
* Data access layer for {{ENTITY_NAME}}
|
|
14
|
+
*/
|
|
15
|
+
export class {{ENTITY_NAME}}Store {
|
|
16
|
+
private tableName = '{{TABLE_NAME}}';
|
|
17
|
+
|
|
18
|
+
constructor(private db: ISqlProvider) {}
|
|
19
|
+
|
|
20
|
+
private toMySQLDatetime(date: Date): string {
|
|
21
|
+
return date.toISOString().slice(0, 19).replace('T', ' ');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private rowToModel(row: {{ENTITY_NAME}}Row): {{ENTITY_NAME}} {
|
|
25
|
+
return new {{ENTITY_NAME}}(
|
|
26
|
+
row.id,
|
|
27
|
+
{{ROW_TO_MODEL_MAPPING}}
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async getAll(page: number = 1, limit: number = 20): Promise<{{ENTITY_NAME}}[]> {
|
|
32
|
+
const offset = (page - 1) * limit;
|
|
33
|
+
const result = await this.db.query(
|
|
34
|
+
\`SELECT {{FIELD_NAMES}} FROM \\\`\${this.tableName}\\\` WHERE deleted_at IS NULL LIMIT :limit OFFSET :offset\`,
|
|
35
|
+
{ limit: String(limit), offset: String(offset) }
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (result.success && result.data) {
|
|
39
|
+
return result.data.map((row: {{ENTITY_NAME}}Row) => this.rowToModel(row));
|
|
40
|
+
}
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async count(): Promise<number> {
|
|
45
|
+
const result = await this.db.query(
|
|
46
|
+
\`SELECT COUNT(*) as count FROM \\\`\${this.tableName}\\\` WHERE deleted_at IS NULL\`,
|
|
47
|
+
{}
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
if (result.success && result.data && result.data.length > 0) {
|
|
51
|
+
return parseInt(result.data[0].count, 10);
|
|
52
|
+
}
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async getById(id: number): Promise<{{ENTITY_NAME}} | null> {
|
|
57
|
+
const result = await this.db.query(
|
|
58
|
+
\`SELECT {{FIELD_NAMES}} FROM \\\`\${this.tableName}\\\` WHERE id = :id AND deleted_at IS NULL\`,
|
|
59
|
+
{ id }
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
if (result.success && result.data && result.data.length > 0) {
|
|
63
|
+
return this.rowToModel(result.data[0] as {{ENTITY_NAME}}Row);
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async insert(entity: {{ENTITY_NAME}}): Promise<{{ENTITY_NAME}}> {
|
|
69
|
+
const now = new Date();
|
|
70
|
+
const data: Partial<{{ENTITY_NAME}}Row> = {
|
|
71
|
+
{{INSERT_DATA_MAPPING}},
|
|
72
|
+
created_at: this.toMySQLDatetime(now),
|
|
73
|
+
updated_at: this.toMySQLDatetime(now)
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const fieldsList = Object.keys(data).map(f => \`\\\`\${f}\\\`\`).join(', ');
|
|
77
|
+
const placeholders = Object.keys(data).map(f => \`:\${f}\`).join(', ');
|
|
78
|
+
|
|
79
|
+
const result = await this.db.query(
|
|
80
|
+
\`INSERT INTO \\\`\${this.tableName}\\\` (\${fieldsList}) VALUES (\${placeholders})\`,
|
|
81
|
+
data
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
if (result.success && result.insertId) {
|
|
85
|
+
const newId = typeof result.insertId === 'string' ? parseInt(result.insertId, 10) : result.insertId;
|
|
86
|
+
return this.getById(newId) as Promise<{{ENTITY_NAME}}>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
throw new Error('Failed to insert {{ENTITY_NAME}}');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async update(id: number, entity: {{ENTITY_NAME}}): Promise<{{ENTITY_NAME}}> {
|
|
93
|
+
const now = new Date();
|
|
94
|
+
const data: Partial<{{ENTITY_NAME}}Row> & { id: number } = {
|
|
95
|
+
{{UPDATE_DATA_MAPPING}},
|
|
96
|
+
updated_at: this.toMySQLDatetime(now),
|
|
97
|
+
id
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const updateFields = {{UPDATE_FIELDS_ARRAY}}.map(f => \`\\\`\${f}\\\` = :\${f}\`).join(', ');
|
|
101
|
+
|
|
102
|
+
const result = await this.db.query(
|
|
103
|
+
\`UPDATE \\\`\${this.tableName}\\\` SET \${updateFields}, updated_at = :updated_at WHERE id = :id\`,
|
|
104
|
+
data
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
if (result.success) {
|
|
108
|
+
return this.getById(id) as Promise<{{ENTITY_NAME}}>;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
throw new Error('Failed to update {{ENTITY_NAME}}');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async softDelete(id: number): Promise<boolean> {
|
|
115
|
+
const now = new Date();
|
|
116
|
+
const result = await this.db.query(
|
|
117
|
+
\`UPDATE \\\`\${this.tableName}\\\` SET deleted_at = :deleted_at WHERE id = :id\`,
|
|
118
|
+
{ deleted_at: this.toMySQLDatetime(now), id }
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
return result.success;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async hardDelete(id: number): Promise<boolean> {
|
|
125
|
+
const result = await this.db.query(
|
|
126
|
+
\`DELETE FROM \\\`\${this.tableName}\\\` WHERE id = :id\`,
|
|
127
|
+
{ id }
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
return result.success;
|
|
131
|
+
}
|
|
132
|
+
{{GET_BY_PARENT_ID_METHOD}}
|
|
133
|
+
{{GET_RESOURCE_OWNER_METHOD}}}`
|
|
134
|
+
};
|
|
135
|
+
exports.newStoreFileTemplate = `import { {{ENTITY_NAME}} } from '../../domain/entities/{{ENTITY_NAME}}';
|
|
136
|
+
import type { ISqlProvider } from '@currentjs/provider-mysql';{{VALUE_OBJECT_IMPORTS}}
|
|
137
|
+
|
|
138
|
+
{{ROW_INTERFACE}}
|
|
139
|
+
|
|
140
|
+
{{STORE_CLASS}}
|
|
141
|
+
`;
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
export declare const storeTemplates: {
|
|
2
2
|
rowInterface: string;
|
|
3
3
|
storeClass: string;
|
|
4
|
-
conversionMethods: string;
|
|
5
|
-
};
|
|
6
|
-
export declare const fileTemplates: {
|
|
7
|
-
storeFile: string;
|
|
8
|
-
storeInterface: string;
|
|
9
4
|
};
|
|
5
|
+
export declare const storeFileTemplate = "import { Injectable } from '../../../../system';\nimport { {{ENTITY_NAME}} } from '../../domain/entities/{{ENTITY_NAME}}';\nimport type { ISqlProvider } from '@currentjs/provider-mysql';{{VALUE_OBJECT_IMPORTS}}\n\n{{ROW_INTERFACE}}\n\n{{STORE_CLASS}}\n";
|