@currentjs/gen 0.5.0 → 0.5.2

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 (70) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +374 -996
  3. package/dist/cli.js +28 -10
  4. package/dist/commands/createModel.d.ts +1 -0
  5. package/dist/commands/createModel.js +764 -0
  6. package/dist/commands/createModule.js +13 -0
  7. package/dist/commands/generateAll.d.ts +1 -0
  8. package/dist/commands/generateAll.js +1 -1
  9. package/dist/commands/init.d.ts +1 -0
  10. package/dist/commands/{createApp.js → init.js} +2 -2
  11. package/dist/commands/migrateCommit.js +33 -68
  12. package/dist/generators/controllerGenerator.d.ts +7 -0
  13. package/dist/generators/controllerGenerator.js +56 -17
  14. package/dist/generators/domainLayerGenerator.js +51 -8
  15. package/dist/generators/dtoGenerator.js +13 -8
  16. package/dist/generators/serviceGenerator.d.ts +6 -0
  17. package/dist/generators/serviceGenerator.js +219 -23
  18. package/dist/generators/storeGenerator.d.ts +4 -0
  19. package/dist/generators/storeGenerator.js +116 -9
  20. package/dist/generators/templateGenerator.d.ts +1 -0
  21. package/dist/generators/templateGenerator.js +8 -2
  22. package/dist/generators/templates/appTemplates.js +1 -1
  23. package/dist/generators/templates/data/cursorRulesTemplate +11 -755
  24. package/dist/generators/templates/data/frontendScriptTemplate +11 -4
  25. package/dist/generators/templates/data/mainViewTemplate +1 -0
  26. package/dist/generators/templates/storeTemplates.d.ts +1 -1
  27. package/dist/generators/templates/storeTemplates.js +3 -26
  28. package/dist/generators/useCaseGenerator.js +6 -3
  29. package/dist/types/configTypes.d.ts +6 -0
  30. package/dist/utils/migrationUtils.d.ts +9 -19
  31. package/dist/utils/migrationUtils.js +80 -110
  32. package/dist/utils/promptUtils.d.ts +37 -0
  33. package/dist/utils/promptUtils.js +149 -0
  34. package/dist/utils/typeUtils.d.ts +4 -0
  35. package/dist/utils/typeUtils.js +7 -0
  36. package/package.json +1 -1
  37. package/dist/commands/createApp.d.ts +0 -1
  38. package/dist/commands/migratePush.d.ts +0 -1
  39. package/dist/commands/migratePush.js +0 -135
  40. package/dist/commands/migrateUpdate.d.ts +0 -1
  41. package/dist/commands/migrateUpdate.js +0 -147
  42. package/dist/commands/newGenerateAll.d.ts +0 -4
  43. package/dist/commands/newGenerateAll.js +0 -336
  44. package/dist/generators/domainModelGenerator.d.ts +0 -41
  45. package/dist/generators/domainModelGenerator.js +0 -242
  46. package/dist/generators/newControllerGenerator.d.ts +0 -55
  47. package/dist/generators/newControllerGenerator.js +0 -644
  48. package/dist/generators/newServiceGenerator.d.ts +0 -19
  49. package/dist/generators/newServiceGenerator.js +0 -266
  50. package/dist/generators/newStoreGenerator.d.ts +0 -39
  51. package/dist/generators/newStoreGenerator.js +0 -408
  52. package/dist/generators/newTemplateGenerator.d.ts +0 -29
  53. package/dist/generators/newTemplateGenerator.js +0 -510
  54. package/dist/generators/storeGeneratorV2.d.ts +0 -31
  55. package/dist/generators/storeGeneratorV2.js +0 -190
  56. package/dist/generators/templates/controllerTemplates.d.ts +0 -43
  57. package/dist/generators/templates/controllerTemplates.js +0 -82
  58. package/dist/generators/templates/newStoreTemplates.d.ts +0 -5
  59. package/dist/generators/templates/newStoreTemplates.js +0 -141
  60. package/dist/generators/templates/serviceTemplates.d.ts +0 -16
  61. package/dist/generators/templates/serviceTemplates.js +0 -59
  62. package/dist/generators/templates/validationTemplates.d.ts +0 -25
  63. package/dist/generators/templates/validationTemplates.js +0 -66
  64. package/dist/generators/templates/viewTemplates.d.ts +0 -25
  65. package/dist/generators/templates/viewTemplates.js +0 -491
  66. package/dist/generators/validationGenerator.d.ts +0 -29
  67. package/dist/generators/validationGenerator.js +0 -250
  68. package/dist/utils/new_parts_of_migrationUtils.d.ts +0 -0
  69. package/dist/utils/new_parts_of_migrationUtils.js +0 -164
  70. package/howto.md +0 -667
@@ -1,491 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.toFileNameFromTemplateName = toFileNameFromTemplateName;
4
- exports.setAvailableModels = setAvailableModels;
5
- exports.setRelationshipContext = setRelationshipContext;
6
- exports.isRelationshipField = isRelationshipField;
7
- exports.getForeignKeyFieldName = getForeignKeyFieldName;
8
- exports.renderListTemplate = renderListTemplate;
9
- exports.renderDetailTemplate = renderDetailTemplate;
10
- exports.renderCreateTemplate = renderCreateTemplate;
11
- exports.renderUpdateTemplate = renderUpdateTemplate;
12
- exports.renderDeleteTemplate = renderDeleteTemplate;
13
- exports.renderLayoutTemplate = renderLayoutTemplate;
14
- // Helper to check if a field type is a known model (relationship)
15
- let availableModels = new Set();
16
- let relationshipContext = {
17
- routePaths: new Map(),
18
- apiPaths: new Map()
19
- };
20
- function setAvailableModels(models) {
21
- availableModels = new Set(models);
22
- }
23
- function setRelationshipContext(context) {
24
- relationshipContext = context;
25
- }
26
- function isRelationshipField(field) {
27
- return availableModels.has(field.type);
28
- }
29
- function getForeignKeyFieldName(field) {
30
- // Convention: fieldName + 'Id' (e.g., owner -> ownerId)
31
- return field.name + 'Id';
32
- }
33
- function toFileNameFromTemplateName(name) {
34
- const last = name.split('/').pop() || 'template';
35
- let base = last
36
- .replace(/\.tpl\.html$/i, '')
37
- .replace(/\.(html|htm|tpl)$/i, '');
38
- base = base.toLowerCase().replace(/[^a-z0-9._-]+/g, '-');
39
- return `${base}.html`;
40
- }
41
- function generateFormInput(field) {
42
- const requiredAttr = field.required ? ' required' : '';
43
- const fieldId = field.name;
44
- const fieldName = field.name.charAt(0).toUpperCase() + field.name.slice(1).replace(/_/g, ' ');
45
- // Skip auto fields (like auto-incrementing IDs)
46
- if (field.auto) {
47
- return '';
48
- }
49
- // Handle relationship fields with a select dropdown
50
- if (isRelationshipField(field)) {
51
- const foreignKeyName = getForeignKeyFieldName(field);
52
- const relatedModel = field.type;
53
- const relatedModelLower = relatedModel.toLowerCase();
54
- const displayField = (field.displayFields && field.displayFields.length > 0) ? field.displayFields[0] : 'name';
55
- // Get actual paths from context, or fallback to defaults
56
- const routePath = relationshipContext.routePaths.get(relatedModel) || `/${relatedModelLower}/create`;
57
- const apiPath = relationshipContext.apiPaths.get(relatedModel) || `/api/${relatedModelLower}`;
58
- return ` <div class="mb-3">
59
- <label for="${foreignKeyName}" class="form-label">${fieldName}</label>
60
- <div class="input-group">
61
- <select id="${foreignKeyName}" name="${foreignKeyName}" class="form-select"${requiredAttr} data-relationship="${relatedModel}">
62
- <option value="">-- Select ${fieldName} --</option>
63
- <!-- Options will be populated via JavaScript from ${apiPath} -->
64
- </select>
65
- <button type="button" class="btn btn-outline-secondary" onclick="window.open('${routePath}', '${relatedModel}Create', 'width=600,height=400')">
66
- <i class="bi bi-plus-circle"></i> New
67
- </button>
68
- </div>
69
- <small class="form-text text-muted">Select an existing ${fieldName} or create a new one</small>
70
- </div>
71
- <script>
72
- // Load ${relatedModel} options
73
- (async () => {
74
- try {
75
- const response = await fetch('${apiPath}', {
76
- headers: App.auth.buildAuthHeaders()
77
- });
78
- const data = await response.json();
79
- const select = document.getElementById('${foreignKeyName}');
80
- if (Array.isArray(data)) {
81
- data.forEach(item => {
82
- const option = document.createElement('option');
83
- option.value = item.id;
84
- option.textContent = item.${displayField} || item.id;
85
- select.appendChild(option);
86
- });
87
- }
88
- } catch (error) {
89
- console.error('Failed to load ${relatedModel} options:', error);
90
- }
91
- })();
92
- </script>`;
93
- }
94
- switch (field.type.toLowerCase()) {
95
- case 'number':
96
- case 'int':
97
- case 'integer':
98
- case 'float':
99
- case 'decimal':
100
- return ` <div class="mb-3">
101
- <label for="${fieldId}" class="form-label">${fieldName}</label>
102
- <input id="${fieldId}" name="${field.name}" type="number" class="form-control"${requiredAttr} />
103
- </div>`;
104
- case 'boolean':
105
- case 'bool':
106
- return ` <div class="mb-3">
107
- <label class="form-label">${fieldName}</label>
108
- <div class="form-check">
109
- <input id="${fieldId}_true" name="${field.name}" type="radio" value="true" class="form-check-input"${requiredAttr} />
110
- <label for="${fieldId}_true" class="form-check-label">Yes</label>
111
- </div>
112
- <div class="form-check">
113
- <input id="${fieldId}_false" name="${field.name}" type="radio" value="false" class="form-check-input" />
114
- <label for="${fieldId}_false" class="form-check-label">No</label>
115
- </div>
116
- </div>`;
117
- case 'enum':
118
- if (field.enum && field.enum.length > 0) {
119
- const options = field.enum.map(opt => ` <option value="${opt}">${opt.charAt(0).toUpperCase() + opt.slice(1)}</option>`).join('\n');
120
- return ` <div class="mb-3">
121
- <label for="${fieldId}" class="form-label">${fieldName}</label>
122
- <select id="${fieldId}" name="${field.name}" class="form-select"${requiredAttr}>
123
- <option value="">-- Select ${fieldName} --</option>
124
- ${options}
125
- </select>
126
- </div>`;
127
- }
128
- // Fallback to text if no enum values
129
- return ` <div class="mb-3">
130
- <label for="${fieldId}" class="form-label">${fieldName}</label>
131
- <input id="${fieldId}" name="${field.name}" type="text" class="form-control"${requiredAttr} />
132
- </div>`;
133
- default:
134
- // Text fields (string, text, etc.)
135
- return ` <div class="mb-3">
136
- <label for="${fieldId}" class="form-label">${fieldName}</label>
137
- <input id="${fieldId}" name="${field.name}" type="text" class="form-control"${requiredAttr} />
138
- </div>`;
139
- }
140
- }
141
- function generateUpdateFormInput(field) {
142
- const requiredAttr = field.required ? ' required' : '';
143
- const fieldId = field.name;
144
- const fieldName = field.name.charAt(0).toUpperCase() + field.name.slice(1).replace(/_/g, ' ');
145
- const fieldValue = `{{ $root.${field.name} }}`;
146
- // Skip auto fields (like auto-incrementing IDs)
147
- if (field.auto) {
148
- return '';
149
- }
150
- // Handle relationship fields with a select dropdown
151
- if (isRelationshipField(field)) {
152
- const foreignKeyName = getForeignKeyFieldName(field);
153
- const relatedModel = field.type;
154
- const relatedModelLower = relatedModel.toLowerCase();
155
- const displayField = (field.displayFields && field.displayFields.length > 0) ? field.displayFields[0] : 'name';
156
- // Get actual paths from context, or fallback to defaults
157
- const routePath = relationshipContext.routePaths.get(relatedModel) || `/${relatedModelLower}/create`;
158
- const apiPath = relationshipContext.apiPaths.get(relatedModel) || `/api/${relatedModelLower}`;
159
- return ` <div class="mb-3">
160
- <label for="${foreignKeyName}" class="form-label">${fieldName}</label>
161
- <div class="input-group">
162
- <select id="${foreignKeyName}" name="${foreignKeyName}" class="form-select"${requiredAttr} data-relationship="${relatedModel}" data-current-value="{{ $root.${foreignKeyName} }}">
163
- <option value="">-- Select ${fieldName} --</option>
164
- <!-- Options will be populated via JavaScript from ${apiPath} -->
165
- </select>
166
- <button type="button" class="btn btn-outline-secondary" onclick="window.open('${routePath}', '${relatedModel}Create', 'width=600,height=400')">
167
- <i class="bi bi-plus-circle"></i> New
168
- </button>
169
- </div>
170
- <small class="form-text text-muted">Select an existing ${fieldName} or create a new one</small>
171
- </div>
172
- <script>
173
- // Load ${relatedModel} options
174
- (async () => {
175
- try {
176
- const response = await fetch('${apiPath}', {
177
- headers: App.auth.buildAuthHeaders()
178
- });
179
- const data = await response.json();
180
- const select = document.getElementById('${foreignKeyName}');
181
- const currentValue = select.getAttribute('data-current-value');
182
- if (Array.isArray(data)) {
183
- data.forEach(item => {
184
- const option = document.createElement('option');
185
- option.value = item.id;
186
- option.textContent = item.${displayField} || item.id;
187
- if (currentValue && item.id == currentValue) {
188
- option.selected = true;
189
- }
190
- select.appendChild(option);
191
- });
192
- }
193
- } catch (error) {
194
- console.error('Failed to load ${relatedModel} options:', error);
195
- }
196
- })();
197
- </script>`;
198
- }
199
- switch (field.type.toLowerCase()) {
200
- case 'number':
201
- case 'int':
202
- case 'integer':
203
- case 'float':
204
- case 'decimal':
205
- return ` <div class="mb-3">
206
- <label for="${fieldId}" class="form-label">${fieldName}</label>
207
- <input id="${fieldId}" name="${field.name}" type="number" class="form-control" value="${fieldValue}"${requiredAttr} />
208
- </div>`;
209
- case 'boolean':
210
- case 'bool':
211
- return ` <div class="mb-3">
212
- <label class="form-label">${fieldName}</label>
213
- <div class="form-check">
214
- <input id="${fieldId}_true" name="${field.name}" type="radio" value="true" class="form-check-input" {{ $root.${field.name} ? 'checked' : '' }}${requiredAttr} />
215
- <label for="${fieldId}_true" class="form-check-label">Yes</label>
216
- </div>
217
- <div class="form-check">
218
- <input id="${fieldId}_false" name="${field.name}" type="radio" value="false" class="form-check-input" {{ $root.${field.name} ? '' : 'checked' }} />
219
- <label for="${fieldId}_false" class="form-check-label">No</label>
220
- </div>
221
- </div>`;
222
- case 'enum':
223
- if (field.enum && field.enum.length > 0) {
224
- const options = field.enum.map(opt => ` <option value="${opt}" {{ $root.${field.name} === '${opt}' ? 'selected' : '' }}>${opt.charAt(0).toUpperCase() + opt.slice(1)}</option>`).join('\n');
225
- return ` <div class="mb-3">
226
- <label for="${fieldId}" class="form-label">${fieldName}</label>
227
- <select id="${fieldId}" name="${field.name}" class="form-select"${requiredAttr}>
228
- <option value="">-- Select ${fieldName} --</option>
229
- ${options}
230
- </select>
231
- </div>`;
232
- }
233
- // Fallback to text if no enum values
234
- return ` <div class="mb-3">
235
- <label for="${fieldId}" class="form-label">${fieldName}</label>
236
- <input id="${fieldId}" name="${field.name}" type="text" class="form-control" value="${fieldValue}"${requiredAttr} />
237
- </div>`;
238
- default:
239
- // Text fields (string, text, etc.)
240
- return ` <div class="mb-3">
241
- <label for="${fieldId}" class="form-label">${fieldName}</label>
242
- <input id="${fieldId}" name="${field.name}" type="text" class="form-control" value="${fieldValue}"${requiredAttr} />
243
- </div>`;
244
- }
245
- }
246
- function renderListTemplate(entityName, templateName, basePath, fields, apiBase) {
247
- const safeFields = fields.length > 0 ? fields : [{ name: 'id', type: 'number' }, { name: 'name', type: 'string' }];
248
- const headers = ['ID', ...safeFields.filter(f => f.name !== 'id').map(f => f.name.charAt(0).toUpperCase() + f.name.slice(1).replace(/_/g, ' '))].map(f => `<th scope="col">${f}</th>`).join('');
249
- // Generate cells with relationship field handling
250
- const cells = [
251
- '<td>{{ row.id }}</td>',
252
- ...safeFields.filter(f => f.name !== 'id').map(f => {
253
- // For relationship fields, show a specific field from the related object
254
- if (isRelationshipField(f)) {
255
- const displayField = (f.displayFields && f.displayFields.length > 0) ? f.displayFields[0] : 'name';
256
- return `<td>{{ row.${f.name}.${displayField} }}</td>`;
257
- }
258
- return `<td>{{ row.${f.name} }}</td>`;
259
- })
260
- ].join('\n ');
261
- const deleteAction = apiBase ? `${apiBase}/{{ row.id }}` : `${basePath}/api/{{ row.id }}`;
262
- return `<!-- @template name="${templateName}" -->
263
- <div class="container-fluid py-4">
264
- <div class="row">
265
- <div class="col">
266
- <div class="d-flex justify-content-between align-items-center mb-4">
267
- <h1 class="h2">${entityName} List</h1>
268
- <a href="${basePath}/create" class="btn btn-primary">
269
- <i class="bi bi-plus-circle me-1"></i>Create New ${entityName}
270
- </a>
271
- </div>
272
-
273
- <div class="card">
274
- <div class="card-body">
275
- <div class="table-responsive">
276
- <table class="table table-hover">
277
- <thead class="table-light">
278
- <tr>
279
- ${headers}
280
- <th scope="col" class="text-end">Actions</th>
281
- </tr>
282
- </thead>
283
- <tbody x-for="$root" x-row="row">
284
- <tr id="row-{{ row.id }}">
285
- ${cells}
286
- <td class="text-end">
287
- <div class="btn-group" role="group">
288
- <a href="${basePath}/{{ row.id }}" class="btn btn-sm btn-outline-primary">View</a>
289
- <a href="${basePath}/{{ row.id }}/edit" class="btn btn-sm btn-outline-secondary">Edit</a>
290
- <form style="display: inline;"
291
- data-action="${deleteAction}"
292
- data-method="DELETE"
293
- data-strategy='["toast", "remove"]'
294
- data-entity-name="${entityName}"
295
- data-confirm-message="Are you sure you want to delete this ${entityName.toLowerCase()}? This action cannot be undone."
296
- data-target-selector="#row-{{ row.id }}">
297
- <button type="submit" class="btn btn-sm btn-outline-danger">
298
- <i class="bi bi-trash me-1"></i>Delete
299
- </button>
300
- </form>
301
- </div>
302
- </td>
303
- </tr>
304
- </tbody>
305
- </table>
306
- </div>
307
- </div>
308
- </div>
309
- </div>
310
- </div>
311
- </div>
312
- `;
313
- }
314
- function renderDetailTemplate(entityName, templateName, fields) {
315
- const safeFields = fields.length > 0 ? fields : [{ name: 'id', type: 'number' }, { name: 'name', type: 'string' }];
316
- const rows = safeFields.map(f => {
317
- const fieldName = f.name.charAt(0).toUpperCase() + f.name.slice(1).replace(/_/g, ' ');
318
- // For relationship fields, show specific fields from the related object
319
- if (isRelationshipField(f)) {
320
- const displayFields = (f.displayFields && f.displayFields.length > 0) ? f.displayFields : ['name'];
321
- // Show all displayFields separated by comma
322
- const fieldAccess = displayFields.map(df => `{{ $root.${f.name}.${df} }}`).join(', ');
323
- return ` <tr><th scope="row" class="w-25">${fieldName}</th><td>${fieldAccess}</td></tr>`;
324
- }
325
- return ` <tr><th scope="row" class="w-25">${fieldName}</th><td>{{ $root.${f.name} }}</td></tr>`;
326
- }).join('\n');
327
- return `<!-- @template name="${templateName}" -->
328
- <div class="container-fluid py-4">
329
- <div class="row justify-content-center">
330
- <div class="col-lg-8">
331
- <div class="d-flex justify-content-between align-items-center mb-4">
332
- <h1 class="h2">${entityName} Details</h1>
333
- <div class="btn-group" role="group">
334
- <a href="{{ basePath }}/{{ $root.id }}/edit" class="btn btn-outline-primary">Edit</a>
335
- <button onclick="window.history.back()" class="btn btn-outline-secondary">Back</button>
336
- </div>
337
- </div>
338
-
339
- <div class="card">
340
- <div class="card-body">
341
- <div class="table-responsive">
342
- <table class="table table-borderless">
343
- <tbody>
344
- ${rows}
345
- </tbody>
346
- </table>
347
- </div>
348
- </div>
349
- </div>
350
- </div>
351
- </div>
352
- </div>
353
- `;
354
- }
355
- function renderCreateTemplate(entityName, templateName, apiBase, fields, strategy = ['back', 'toast'], basePath) {
356
- const safeFields = fields.filter(f => f.name !== 'id' && !f.auto);
357
- const inputs = safeFields.map(f => generateFormInput(f)).filter(input => input.trim() !== '').join('\n');
358
- const tplId = templateName.replace(/[^a-z0-9._-]+/gi, '-').toLowerCase();
359
- const targetId = `${tplId}-result`;
360
- const messageId = `${tplId}-message`;
361
- const modalId = `${tplId}-modal`;
362
- const errorsId = `${tplId}-errors`;
363
- const fieldTypes = JSON.stringify(safeFields.reduce((acc, f) => {
364
- acc[f.name] = f.type;
365
- return acc;
366
- }, {}));
367
- const inlineErrorsBlock = strategy.includes('inline') ? `\n <div id="${errorsId}" class="alert alert-danger d-none"></div>` : '';
368
- const messageBlock = strategy.includes('message') ? `\n <div id="${messageId}" class="alert d-none" role="status"></div>` : '';
369
- const modalBlock = strategy.includes('modal')
370
- ? `\n<div class="modal fade" id="${modalId}" tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-body"></div></div></div></div>`
371
- : '';
372
- return `<!-- @template name="${templateName}" -->
373
- <div class="container-fluid py-4">
374
- <div class="row justify-content-center">
375
- <div class="col-lg-6">
376
- <div class="d-flex justify-content-between align-items-center mb-4">
377
- <h1 class="h2">Create ${entityName}</h1>
378
- <button onclick="window.history.back()" class="btn btn-outline-secondary">Cancel</button>
379
- </div>
380
-
381
- <div class="card">
382
- <div class="card-body">
383
- <form data-template="${tplId}" data-action="${apiBase}" data-method="POST" data-strategy='${JSON.stringify(strategy)}' data-base-path="${basePath || ''}" data-entity-name="${entityName}" data-message-id="${messageId}" data-modal-id="${modalId}" data-field-types='${fieldTypes}'>
384
- ${inputs}
385
- <div class="d-flex gap-2 justify-content-end">
386
- <button type="button" onclick="window.history.back()" class="btn btn-outline-secondary">Cancel</button>
387
- <button type="submit" class="btn btn-primary">Create ${entityName}</button>
388
- </div>
389
- </form>
390
- <div id="${targetId}"></div>${inlineErrorsBlock}${messageBlock}
391
- </div>
392
- </div>
393
- </div>
394
- </div>
395
- </div>${modalBlock}
396
- `;
397
- }
398
- function renderUpdateTemplate(entityName, templateName, apiBase, fields, strategy = ['back', 'toast'], basePath) {
399
- const safeFields = fields.filter(f => f.name !== 'id' && !f.auto);
400
- const inputs = safeFields.map(f => generateUpdateFormInput(f)).filter(input => input.trim() !== '').join('\n');
401
- const tplId = templateName.replace(/[^a-z0-9._-]+/gi, '-').toLowerCase();
402
- const targetId = `${tplId}-result`;
403
- const messageId = `${tplId}-message`;
404
- const modalId = `${tplId}-modal`;
405
- const errorsId = `${tplId}-errors`;
406
- const fieldTypes = JSON.stringify(safeFields.reduce((acc, f) => {
407
- acc[f.name] = f.type;
408
- return acc;
409
- }, {}));
410
- const inlineErrorsBlock = strategy.includes('inline') ? `\n <div id="${errorsId}" class="alert alert-danger d-none"></div>` : '';
411
- const messageBlock = strategy.includes('message') ? `\n <div id="${messageId}" class="alert d-none" role="status"></div>` : '';
412
- const modalBlock = strategy.includes('modal')
413
- ? `\n<div class="modal fade" id="${modalId}" tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-body"></div></div></div></div>`
414
- : '';
415
- return `<!-- @template name="${templateName}" -->
416
- <div class="container-fluid py-4">
417
- <div class="row justify-content-center">
418
- <div class="col-lg-6">
419
- <div class="d-flex justify-content-between align-items-center mb-4">
420
- <h1 class="h2">Edit ${entityName}</h1>
421
- <button onclick="window.history.back()" class="btn btn-outline-secondary">Cancel</button>
422
- </div>
423
-
424
- <div class="card">
425
- <div class="card-body">
426
- <form data-template="${tplId}" data-action="${apiBase}/{{ $root.id }}" data-method="PUT" data-strategy='${JSON.stringify(strategy)}' data-base-path="${basePath || ''}" data-entity-name="${entityName}" data-message-id="${messageId}" data-modal-id="${modalId}" data-field-types='${fieldTypes}'>
427
- ${inputs}
428
- <div class="d-flex gap-2 justify-content-end">
429
- <button type="button" onclick="window.history.back()" class="btn btn-outline-secondary">Cancel</button>
430
- <button type="submit" class="btn btn-primary">Save Changes</button>
431
- </div>
432
- </form>
433
- <div id="${targetId}"></div>${inlineErrorsBlock}${messageBlock}
434
- </div>
435
- </div>
436
- </div>
437
- </div>
438
- </div>${modalBlock}
439
- `;
440
- }
441
- function renderDeleteTemplate(entityName, templateName, apiBase, strategy = ['back', 'toast'], basePath) {
442
- const tplId = templateName.replace(/[^a-z0-9._-]+/gi, '-').toLowerCase();
443
- const messageId = `${tplId}-message`;
444
- const modalId = `${tplId}-modal`;
445
- const messageBlock = strategy.includes('message') ? `\n <div id="${messageId}" class="alert d-none" role="status"></div>` : '';
446
- const modalBlock = strategy.includes('modal')
447
- ? `\n<div class="modal fade" id="${modalId}" tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-body"></div></div></div></div>`
448
- : '';
449
- return `<!-- @template name="${templateName}" -->
450
- <div class="container-fluid py-4">
451
- <div class="row justify-content-center">
452
- <div class="col-lg-6">
453
- <div class="d-flex justify-content-between align-items-center mb-4">
454
- <h1 class="h2">Delete ${entityName}</h1>
455
- <button onclick="window.history.back()" class="btn btn-outline-secondary">Cancel</button>
456
- </div>
457
-
458
- <div class="card border-danger">
459
- <div class="card-body">
460
- <div class="alert alert-warning" role="alert">
461
- <h5 class="alert-heading">⚠️ Confirm Deletion</h5>
462
- <p class="mb-0">Are you sure you want to delete this ${entityName.toLowerCase()}? This action cannot be undone.</p>
463
- </div>
464
-
465
- <form data-template="${tplId}" data-action="${apiBase}/{{ $root.id }}" data-method="DELETE" data-strategy='${JSON.stringify(strategy)}' data-base-path="${basePath || ''}" data-entity-name="${entityName}" data-message-id="${messageId}" data-modal-id="${modalId}">
466
- <div class="d-flex gap-2 justify-content-end">
467
- <button type="button" onclick="window.history.back()" class="btn btn-outline-secondary">Cancel</button>
468
- <button type="submit" class="btn btn-danger">Delete ${entityName}</button>
469
- </div>
470
- </form>${messageBlock}
471
- </div>
472
- </div>
473
- </div>
474
- </div>
475
- </div>${modalBlock}
476
- `;
477
- }
478
- function renderLayoutTemplate(layoutName) {
479
- return `<!-- @template name="${layoutName}" -->
480
- <!doctype html>
481
- <html>
482
- <head>
483
- <meta charset="utf-8" />
484
- <title>${layoutName}</title>
485
- </head>
486
- <body>
487
- {{{ content }}}
488
- </body>
489
- </html>
490
- `;
491
- }
@@ -1,29 +0,0 @@
1
- interface FieldConfig {
2
- name: string;
3
- type: string;
4
- required?: boolean;
5
- auto?: boolean;
6
- unique?: boolean;
7
- displayFields?: string[];
8
- }
9
- export declare class ValidationGenerator {
10
- private availableModels;
11
- private setAvailableModels;
12
- private isRelationshipField;
13
- private getForeignKeyFieldName;
14
- private replaceTemplateVars;
15
- private getTypeScriptType;
16
- private generateInterfaceField;
17
- private generateValidationLogic;
18
- private generateDtoField;
19
- private generateDtoInterface;
20
- private generateInputInterface;
21
- private generateValidationFunction;
22
- generateValidation(entityName: string, fields: FieldConfig[]): string;
23
- generateFromYamlFile(yamlFilePath: string): Record<string, string>;
24
- generateAndSaveFiles(yamlFilePath?: string, outputDir?: string, opts?: {
25
- force?: boolean;
26
- skipOnConflict?: boolean;
27
- }): Promise<void>;
28
- }
29
- export {};