@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.
- package/CHANGELOG.md +19 -0
- package/README.md +374 -996
- package/dist/cli.js +28 -10
- package/dist/commands/createModel.d.ts +1 -0
- package/dist/commands/createModel.js +764 -0
- package/dist/commands/createModule.js +13 -0
- package/dist/commands/generateAll.d.ts +1 -0
- package/dist/commands/generateAll.js +1 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/{createApp.js โ init.js} +2 -2
- package/dist/commands/migrateCommit.js +33 -68
- package/dist/generators/controllerGenerator.d.ts +7 -0
- package/dist/generators/controllerGenerator.js +56 -17
- package/dist/generators/domainLayerGenerator.js +51 -8
- package/dist/generators/dtoGenerator.js +13 -8
- package/dist/generators/serviceGenerator.d.ts +6 -0
- package/dist/generators/serviceGenerator.js +219 -23
- package/dist/generators/storeGenerator.d.ts +4 -0
- package/dist/generators/storeGenerator.js +116 -9
- package/dist/generators/templateGenerator.d.ts +1 -0
- package/dist/generators/templateGenerator.js +8 -2
- package/dist/generators/templates/appTemplates.js +1 -1
- package/dist/generators/templates/data/cursorRulesTemplate +11 -755
- package/dist/generators/templates/data/frontendScriptTemplate +11 -4
- package/dist/generators/templates/data/mainViewTemplate +1 -0
- package/dist/generators/templates/storeTemplates.d.ts +1 -1
- package/dist/generators/templates/storeTemplates.js +3 -26
- package/dist/generators/useCaseGenerator.js +6 -3
- package/dist/types/configTypes.d.ts +6 -0
- package/dist/utils/migrationUtils.d.ts +9 -19
- package/dist/utils/migrationUtils.js +80 -110
- package/dist/utils/promptUtils.d.ts +37 -0
- package/dist/utils/promptUtils.js +149 -0
- package/dist/utils/typeUtils.d.ts +4 -0
- package/dist/utils/typeUtils.js +7 -0
- package/package.json +1 -1
- package/dist/commands/createApp.d.ts +0 -1
- package/dist/commands/migratePush.d.ts +0 -1
- package/dist/commands/migratePush.js +0 -135
- package/dist/commands/migrateUpdate.d.ts +0 -1
- package/dist/commands/migrateUpdate.js +0 -147
- package/dist/commands/newGenerateAll.d.ts +0 -4
- package/dist/commands/newGenerateAll.js +0 -336
- package/dist/generators/domainModelGenerator.d.ts +0 -41
- package/dist/generators/domainModelGenerator.js +0 -242
- package/dist/generators/newControllerGenerator.d.ts +0 -55
- package/dist/generators/newControllerGenerator.js +0 -644
- package/dist/generators/newServiceGenerator.d.ts +0 -19
- package/dist/generators/newServiceGenerator.js +0 -266
- package/dist/generators/newStoreGenerator.d.ts +0 -39
- package/dist/generators/newStoreGenerator.js +0 -408
- package/dist/generators/newTemplateGenerator.d.ts +0 -29
- package/dist/generators/newTemplateGenerator.js +0 -510
- package/dist/generators/storeGeneratorV2.d.ts +0 -31
- package/dist/generators/storeGeneratorV2.js +0 -190
- package/dist/generators/templates/controllerTemplates.d.ts +0 -43
- package/dist/generators/templates/controllerTemplates.js +0 -82
- package/dist/generators/templates/newStoreTemplates.d.ts +0 -5
- package/dist/generators/templates/newStoreTemplates.js +0 -141
- package/dist/generators/templates/serviceTemplates.d.ts +0 -16
- package/dist/generators/templates/serviceTemplates.js +0 -59
- package/dist/generators/templates/validationTemplates.d.ts +0 -25
- package/dist/generators/templates/validationTemplates.js +0 -66
- package/dist/generators/templates/viewTemplates.d.ts +0 -25
- package/dist/generators/templates/viewTemplates.js +0 -491
- package/dist/generators/validationGenerator.d.ts +0 -29
- package/dist/generators/validationGenerator.js +0 -250
- package/dist/utils/new_parts_of_migrationUtils.d.ts +0 -0
- package/dist/utils/new_parts_of_migrationUtils.js +0 -164
- package/howto.md +0 -667
package/README.md
CHANGED
|
@@ -1,10 +1,27 @@
|
|
|
1
|
-
# @currentjs/gen
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
# @currentjs/gen
|
|
2
|
+
|
|
3
|
+
A CLI code generator that transforms YAML specifications into fully functional TypeScript applications following clean architecture principles.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Quick Start](#quick-start)
|
|
9
|
+
- [Development Flow](#development-flow)
|
|
10
|
+
- [TL;DR](#tldr)
|
|
11
|
+
- [Reference](#reference)
|
|
12
|
+
- [Module Configuration Overview](#module-configuration-overview)
|
|
13
|
+
- [Domain Layer](#domain-layer-domain)
|
|
14
|
+
- [Use Cases Layer](#use-cases-layer-usecases)
|
|
15
|
+
- [API Layer](#api-layer-api)
|
|
16
|
+
- [Web Layer](#web-layer-web)
|
|
17
|
+
- [Generated Source Code](#generated-source-code)
|
|
18
|
+
- [Change Tracking: diff and commit](#change-tracking-diff-and-commit)
|
|
19
|
+
- [Database Migrations](#database-migrations)
|
|
20
|
+
- [Template System](#template-system)
|
|
21
|
+
- [Authorship & Contribution](#authorship--contribution)
|
|
22
|
+
- [License](#license)
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
8
25
|
|
|
9
26
|
```bash
|
|
10
27
|
npm install -g @currentjs/gen
|
|
@@ -12,356 +29,233 @@ npm install -g @currentjs/gen
|
|
|
12
29
|
npx @currentjs/gen
|
|
13
30
|
```
|
|
14
31
|
|
|
15
|
-
## Quick Start
|
|
32
|
+
## Quick Start
|
|
16
33
|
|
|
17
|
-
|
|
18
|
-
# Show help
|
|
19
|
-
currentjs --help
|
|
34
|
+
Building an application from scratch:
|
|
20
35
|
|
|
21
|
-
|
|
22
|
-
currentjs create app
|
|
23
|
-
|
|
24
|
-
# Create a new app inside a folder
|
|
25
|
-
currentjs create app my-app
|
|
26
|
-
|
|
27
|
-
# Create a module folder under src/modules
|
|
28
|
-
currentjs create module Blog
|
|
36
|
+
1. Initialize a new project:
|
|
29
37
|
|
|
30
|
-
|
|
31
|
-
currentjs
|
|
32
|
-
|
|
33
|
-
# Generate specific module
|
|
34
|
-
currentjs generate Blog --yaml app.yaml
|
|
38
|
+
```
|
|
39
|
+
currentjs init myapp
|
|
40
|
+
cd myapp
|
|
35
41
|
```
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
### Basic Development Flow
|
|
40
|
-
|
|
41
|
-
1. **Create an empty app**
|
|
42
|
-
```bash
|
|
43
|
-
currentjs create app # will create an app inside the current directory
|
|
44
|
-
# or:
|
|
45
|
-
currentjs create app my-project # will create a directory "my-project" and create an app there
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
2. **Create a new module**
|
|
49
|
-
```bash
|
|
50
|
-
currentjs create module Blog
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
3. **Define your module's configuration** in `src/modules/Blog/blog.yaml`:
|
|
54
|
-
- Define your domain (aggregates and value objects)
|
|
55
|
-
- Configure use cases (CRUD is already configured)
|
|
56
|
-
- Set up API and web endpoints with auth
|
|
57
|
-
|
|
58
|
-
4. **Generate TypeScript files**
|
|
59
|
-
```bash
|
|
60
|
-
currentjs generate Blog
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
5. **Make custom changes** (if needed) to:
|
|
64
|
-
- Business logic in models
|
|
65
|
-
- Custom actions in services
|
|
66
|
-
- HTML templates
|
|
43
|
+
2. Create a module:
|
|
67
44
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
```
|
|
45
|
+
```
|
|
46
|
+
currentjs create module Blog
|
|
47
|
+
```
|
|
72
48
|
|
|
73
|
-
|
|
49
|
+
3. Run an interactive command:
|
|
74
50
|
|
|
75
|
-
|
|
51
|
+
```
|
|
52
|
+
currentjs create model Blog:Post
|
|
53
|
+
```
|
|
76
54
|
|
|
77
|
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
- **๐จ HTML templates** using @currentjs/templating
|
|
83
|
-
- **๐ Change tracking** so you can modify generated code safely
|
|
55
|
+
It will:
|
|
56
|
+
- ask everything it needs,
|
|
57
|
+
- generate yaml config,
|
|
58
|
+
- generate a TypeScript source code,
|
|
59
|
+
- and build it.
|
|
84
60
|
|
|
85
|
-
|
|
61
|
+
Alternatively, you can:
|
|
62
|
+
- edit the generated module YAML at `src/modules/Blog/blog.yaml`. Define the domain model fields, use cases, API endpoints, and web routes.
|
|
63
|
+
- Generate TypeScript files from the YAML configuration: `currentjs generate Blog`
|
|
64
|
+
- If needed, make manual changes to generated files (domain entities, views, services).
|
|
65
|
+
- Commit those manual changes so they survive regeneration: `currentjs commit`
|
|
86
66
|
|
|
87
|
-
|
|
67
|
+
To add custom (non-CRUD) behavior: define a method in the service, reference it in the module YAML as a handler, regenerate, and commit.
|
|
88
68
|
|
|
89
|
-
### App & Module Creation
|
|
90
|
-
```bash
|
|
91
|
-
currentjs create app [name] # Create new application
|
|
92
|
-
currentjs create module <name> # Create new module in existing app
|
|
93
69
|
```
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
```bash
|
|
97
|
-
currentjs generate [module] # Generate code from YAML specs
|
|
98
|
-
--yaml app.yaml # Specify config file (default: ./app.yaml)
|
|
99
|
-
--force # Overwrite files without prompting
|
|
100
|
-
--skip # Skip conflicts, never overwrite
|
|
70
|
+
currentjs generate Blog
|
|
71
|
+
currentjs commit
|
|
101
72
|
```
|
|
102
73
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
The generator includes a sophisticated change tracking system that **revolutionizes how you work with generated code**:
|
|
74
|
+
## Development Flow
|
|
106
75
|
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
currentjs
|
|
110
|
-
|
|
111
|
-
|
|
76
|
+
```
|
|
77
|
+
โโโโโโโโโโโโโโโโโโโโ
|
|
78
|
+
โ currentjs init โ
|
|
79
|
+
โโโโโโโโโโฌโโโโโโโโโโ
|
|
80
|
+
โ
|
|
81
|
+
โผ
|
|
82
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
83
|
+
โ currentjs create moduleโ
|
|
84
|
+
โโโโโโโโโโโโโโฌโโโโโโโโโโโโ
|
|
85
|
+
โ
|
|
86
|
+
โโโโโโโโโโโโโโดโโโโโโโโโโโโโ
|
|
87
|
+
โ โ
|
|
88
|
+
โผ โผ
|
|
89
|
+
โโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
90
|
+
โ Edit module YAML โ โ currentjs create model โ
|
|
91
|
+
โ (define structure) โ โ (interactive wizard) โ
|
|
92
|
+
โโโโโโโโโโโฌโโโโโโโโโโโ โโโโโโโโโโโโโโฌโโโโโโโโโโโโ
|
|
93
|
+
โ โ
|
|
94
|
+
โผ โ
|
|
95
|
+
โโโโโโโโโโโโโโโโโโโโโ โ
|
|
96
|
+
โ currentjs generateโ โ
|
|
97
|
+
โโโโโโโโโโโฌโโโโโโโโโโ โ
|
|
98
|
+
โ โ
|
|
99
|
+
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโ
|
|
100
|
+
โ
|
|
101
|
+
โผ
|
|
102
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
103
|
+
โ Modify generated filesโ
|
|
104
|
+
โ (entities, views, etc)โ
|
|
105
|
+
โ (optional step) โ
|
|
106
|
+
โโโโโโโโโโโโโฌโโโโโโโโโโโโ
|
|
107
|
+
โ
|
|
108
|
+
โผ
|
|
109
|
+
โโโโโโโโโโโโโโโโโโโโโ
|
|
110
|
+
โ currentjs commit โ
|
|
111
|
+
โโโโโโโโโโโโโโโโโโโโโ
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
There are two paths after creating a module:
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
- **Interactive wizard** (`currentjs create model Blog:Post`) โ prompts for fields, use cases, routes, and permissions, then generates everything automatically.
|
|
117
|
+
- **Manual editing** โ edit the module YAML by hand, then run `currentjs generate`. This gives full control over every configuration option.
|
|
117
118
|
|
|
118
|
-
|
|
119
|
-
- **Your YAML files** (the source of truth)
|
|
120
|
-
- **`registry.json`** (change tracking metadata)
|
|
121
|
-
- **Your custom modifications** (stored as reusable "patches")
|
|
119
|
+
Both paths converge at the same point: once files are generated, you can optionally customize the generated code (business logic, templates, etc.) and run `currentjs commit` to preserve those changes across future regenerations.
|
|
122
120
|
|
|
123
|
-
|
|
124
|
-
```
|
|
125
|
-
git/
|
|
126
|
-
โโโ src/modules/Blog/Blog.yaml # Source specification
|
|
127
|
-
โโโ src/modules/Blog/domain/entities/Post.ts # Generated + modified
|
|
128
|
-
โโโ src/modules/Blog/services/PostService.ts # Generated + modified
|
|
129
|
-
โโโ ... (hundreds of generated files with custom changes)
|
|
130
|
-
```
|
|
121
|
+
When you need behavior beyond standard CRUD:
|
|
131
122
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
โโโ registry.json # Change tracking metadata
|
|
137
|
-
โโโ .currentjs/commits/ # Your custom modifications as patches
|
|
138
|
-
โโโ commit-2024-01-15.json
|
|
139
|
-
โโโ commit-2024-01-20.json
|
|
140
|
-
```
|
|
123
|
+
1. Implement the custom method in the generated service class.
|
|
124
|
+
2. Reference it in the module YAML as a handler (e.g., `service:myMethod`).
|
|
125
|
+
3. Optionally add API/web endpoints for the new action.
|
|
126
|
+
4. Regenerate and commit.
|
|
141
127
|
|
|
142
|
-
####
|
|
128
|
+
#### TLDR
|
|
143
129
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
currentjs generate
|
|
147
|
-
|
|
148
|
-
|
|
130
|
+
- module's YAML configurations (plus "commits") are the source of truth.
|
|
131
|
+
- `currentjs create module` creates module's structure (empty folders, YAML configuration with one model without any fields)
|
|
132
|
+
- `currentjs create model`: You > YAML > `generate`
|
|
133
|
+
- `currentjs generate`: YAML + commits > TypeScript > JS
|
|
134
|
+
- since YAML is the main source of truth, it's also the best place to make changes
|
|
135
|
+
- if changes are beyond configuration (require some coding), the best place is (in descending order): model, service
|
|
136
|
+
- in order to preserve changes in TypeScript files, use `currentjs commit`
|
|
137
|
+
- templates can be changed freely, they are not regenerated by default.
|
|
149
138
|
|
|
150
|
-
**2. Make Your Custom Changes**
|
|
151
|
-
```typescript
|
|
152
|
-
// Edit generated service to add custom logic
|
|
153
|
-
export class PostService extends GeneratedPostService {
|
|
154
|
-
async publishPost(id: number): Promise<void> {
|
|
155
|
-
// Your custom business logic here
|
|
156
|
-
const post = await this.getById(id);
|
|
157
|
-
post.publishedAt = new Date();
|
|
158
|
-
await this.update(id, post);
|
|
159
|
-
await this.sendNotificationEmail(post);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
```
|
|
163
139
|
|
|
164
|
-
|
|
165
|
-
```bash
|
|
166
|
-
currentjs commit src/modules/Blog/services/PostService.ts
|
|
167
|
-
# Saves your modifications as reusable "hunks" (like git patches)
|
|
168
|
-
```
|
|
140
|
+
## Reference
|
|
169
141
|
|
|
170
|
-
|
|
171
|
-
```bash
|
|
172
|
-
# Change your YAML specification
|
|
173
|
-
currentjs generate --force
|
|
174
|
-
# Your custom changes are automatically reapplied to the new generated code!
|
|
175
|
-
```
|
|
142
|
+
For detailed documentation of all CLI commands, YAML configuration options, and field types, see the [Reference](REFERENCE.md).
|
|
176
143
|
|
|
177
|
-
|
|
144
|
+
## Module Configuration Overview
|
|
178
145
|
|
|
179
|
-
|
|
180
|
-
# See what's different from generated baseline
|
|
181
|
-
currentjs diff Blog
|
|
182
|
-
```
|
|
146
|
+
Each module is configured through a single YAML file located at `src/modules/<Name>/<name>.yaml`. The configuration follows a layered structure inspired by Clean Architecture. Each layer in the YAML maps to a set of generated TypeScript files.
|
|
183
147
|
|
|
184
|
-
|
|
185
|
-
```diff
|
|
186
|
-
[modified] src/modules/Blog/services/PostService.ts
|
|
187
|
-
|
|
188
|
-
@@ -15,0 +16,8 @@
|
|
189
|
-
+
|
|
190
|
-
+ async publishPost(id: number): Promise<void> {
|
|
191
|
-
+ const post = await this.getById(id);
|
|
192
|
-
+ post.publishedAt = new Date();
|
|
193
|
-
+ await this.update(id, post);
|
|
194
|
-
+ await this.sendNotificationEmail(post);
|
|
195
|
-
+ }
|
|
196
|
-
```
|
|
148
|
+
### Layers at a Glance
|
|
197
149
|
|
|
198
|
-
|
|
150
|
+
| YAML Section | Purpose | Generated Files |
|
|
151
|
+
|---|---|---|
|
|
152
|
+
| `domain` | Define your data models (aggregates, value objects) | Entity classes, value object classes |
|
|
153
|
+
| `useCases` | Define business operations, input/output shapes, handler chains | Use case orchestrators, services, DTOs |
|
|
154
|
+
| `api` | Define REST API endpoints | API controller |
|
|
155
|
+
| `web` | Define server-rendered pages and forms | Web controller, HTML templates |
|
|
199
156
|
|
|
200
|
-
|
|
201
|
-
- Cleaner repositories (no generated code noise)
|
|
202
|
-
- Fearless regeneration (your changes are always preserved)
|
|
203
|
-
- Clear separation between specifications and implementations
|
|
157
|
+
A minimal module YAML needs at least `domain` and `useCases`. The `api` and `web` sections are optional.
|
|
204
158
|
|
|
205
|
-
|
|
206
|
-
- Merge conflicts only happen in YAML files (much simpler)
|
|
207
|
-
- Team members can have different generated code locally
|
|
208
|
-
- Changes to business logic are tracked separately from schema changes
|
|
209
|
-
- New team members just run `currentjs generate` to get up and running
|
|
159
|
+
โ Reference: [Module Configuration](REFERENCE.md#module-configuration-module-yaml)
|
|
210
160
|
|
|
211
|
-
|
|
212
|
-
```bash
|
|
213
|
-
# In your deployment pipeline
|
|
214
|
-
git clone your-repo
|
|
215
|
-
currentjs generate # Recreates all source code from YAML + patches
|
|
216
|
-
npm run deploy
|
|
217
|
-
```
|
|
161
|
+
---
|
|
218
162
|
|
|
219
|
-
|
|
220
|
-
```bash
|
|
221
|
-
# Export your modifications
|
|
222
|
-
git add registry.json .currentjs/
|
|
223
|
-
git commit -m "Add custom publish functionality"
|
|
224
|
-
git push
|
|
225
|
-
|
|
226
|
-
# Teammates get your changes
|
|
227
|
-
git pull
|
|
228
|
-
currentjs generate # Their code automatically includes your customizations
|
|
229
|
-
```
|
|
163
|
+
### Domain Layer (`domain`)
|
|
230
164
|
|
|
231
|
-
|
|
165
|
+
The domain layer defines your data models. There are two kinds: **aggregates** (entities) and **value objects**.
|
|
232
166
|
|
|
233
|
-
####
|
|
167
|
+
#### Aggregates
|
|
234
168
|
|
|
235
|
-
|
|
169
|
+
Aggregates are the main entities. One aggregate should be marked as the root (`root: true`), which enables ownership tracking (auto-generated `ownerId` field).
|
|
236
170
|
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
171
|
+
```yaml
|
|
172
|
+
domain:
|
|
173
|
+
aggregates:
|
|
174
|
+
Post:
|
|
175
|
+
root: true
|
|
176
|
+
fields:
|
|
177
|
+
title: { type: string, required: true }
|
|
178
|
+
content: { type: string, required: true }
|
|
179
|
+
status: { type: enum, values: [draft, published, archived] }
|
|
180
|
+
publishedAt: { type: datetime }
|
|
181
|
+
```
|
|
246
182
|
|
|
247
|
-
|
|
248
|
-
!*.yaml
|
|
249
|
-
!registry.json
|
|
250
|
-
!.currentjs/
|
|
183
|
+
Fields like `id`, `ownerId`, `created_at`, `updated_at`, and `deleted_at` are added automatically โ do not include them.
|
|
251
184
|
|
|
252
|
-
|
|
253
|
-
node_modules/
|
|
254
|
-
build/
|
|
255
|
-
dist/
|
|
256
|
-
*.log
|
|
257
|
-
```
|
|
185
|
+
**Available field types:** `string`, `number`, `integer`, `decimal`, `boolean`, `datetime`, `date`, `id`, `json`, `array`, `object`, `enum`.
|
|
258
186
|
|
|
259
|
-
|
|
187
|
+
For `enum` fields, provide the allowed values with `values: [...]`.
|
|
260
188
|
|
|
261
|
-
|
|
189
|
+
#### Model Relationships
|
|
262
190
|
|
|
263
|
-
|
|
191
|
+
Set the `type` to another aggregate's name to create a foreign key relationship:
|
|
264
192
|
|
|
265
193
|
```yaml
|
|
266
194
|
domain:
|
|
267
195
|
aggregates:
|
|
268
|
-
|
|
196
|
+
Author:
|
|
269
197
|
root: true
|
|
270
198
|
fields:
|
|
271
199
|
name: { type: string, required: true }
|
|
272
|
-
|
|
200
|
+
Post:
|
|
273
201
|
root: true
|
|
274
202
|
fields:
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
useCases:
|
|
279
|
-
Cat:
|
|
280
|
-
list:
|
|
281
|
-
handlers: [default:list]
|
|
282
|
-
create:
|
|
283
|
-
input: { from: Cat }
|
|
284
|
-
output: { from: Cat }
|
|
285
|
-
handlers: [default:create]
|
|
286
|
-
Person:
|
|
287
|
-
list:
|
|
288
|
-
handlers: [default:list]
|
|
289
|
-
create:
|
|
290
|
-
input: { from: Person }
|
|
291
|
-
output: { from: Person }
|
|
292
|
-
handlers: [default:create]
|
|
293
|
-
|
|
294
|
-
api:
|
|
295
|
-
Cat:
|
|
296
|
-
prefix: /api/cat
|
|
297
|
-
endpoints:
|
|
298
|
-
- method: GET
|
|
299
|
-
path: /
|
|
300
|
-
useCase: Cat:list
|
|
301
|
-
auth: all
|
|
302
|
-
Person:
|
|
303
|
-
prefix: /api/person
|
|
304
|
-
endpoints:
|
|
305
|
-
- method: GET
|
|
306
|
-
path: /
|
|
307
|
-
useCase: Person:list
|
|
308
|
-
auth: all
|
|
309
|
-
|
|
310
|
-
web:
|
|
311
|
-
Cat:
|
|
312
|
-
prefix: /cat
|
|
313
|
-
layout: main_view
|
|
314
|
-
pages:
|
|
315
|
-
- path: /
|
|
316
|
-
useCase: Cat:list
|
|
317
|
-
view: catList
|
|
318
|
-
auth: all
|
|
319
|
-
- path: /create
|
|
320
|
-
method: GET
|
|
321
|
-
view: catCreate
|
|
322
|
-
auth: authenticated
|
|
323
|
-
Person:
|
|
324
|
-
prefix: /person
|
|
325
|
-
layout: main_view
|
|
326
|
-
pages:
|
|
327
|
-
- path: /
|
|
328
|
-
useCase: Person:list
|
|
329
|
-
view: personList
|
|
330
|
-
auth: all
|
|
331
|
-
- path: /create
|
|
332
|
-
method: GET
|
|
333
|
-
view: personCreate
|
|
334
|
-
auth: authenticated
|
|
203
|
+
title: { type: string, required: true }
|
|
204
|
+
author: { type: Author, required: true }
|
|
335
205
|
```
|
|
336
206
|
|
|
337
|
-
|
|
207
|
+
The generator automatically:
|
|
208
|
+
- Creates a foreign key column `authorId` in the database.
|
|
209
|
+
- Uses the full `Author` object in the domain model (not just the ID).
|
|
210
|
+
- Uses `authorId: number` in DTOs for API transmission.
|
|
211
|
+
- Generates a `<select>` dropdown with a "Create New" button in HTML forms.
|
|
212
|
+
- Wires the related store as a dependency for loading relationships.
|
|
338
213
|
|
|
339
|
-
|
|
214
|
+
Foreign key naming follows the pattern `fieldName + 'Id'` (e.g., `author` โ `authorId`).
|
|
340
215
|
|
|
341
|
-
|
|
216
|
+
#### Child Entities
|
|
342
217
|
|
|
343
|
-
|
|
344
|
-
```bash
|
|
345
|
-
currentjs create app my-blog
|
|
346
|
-
cd my-blog
|
|
347
|
-
currentjs create module Blog
|
|
348
|
-
```
|
|
218
|
+
An aggregate can have child entities listed in the `entities` field:
|
|
349
219
|
|
|
350
|
-
### 2. Define your data model
|
|
351
220
|
```yaml
|
|
352
|
-
# src/modules/Blog/blog.yaml
|
|
353
|
-
# Only the domain fields need to be customized - the rest is auto-generated!
|
|
354
221
|
domain:
|
|
355
222
|
aggregates:
|
|
356
|
-
|
|
223
|
+
Invoice:
|
|
357
224
|
root: true
|
|
358
225
|
fields:
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
226
|
+
number: { type: string, required: true }
|
|
227
|
+
entities: [InvoiceItem]
|
|
228
|
+
|
|
229
|
+
InvoiceItem:
|
|
230
|
+
fields:
|
|
231
|
+
productName: { type: string, required: true }
|
|
232
|
+
quantity: { type: integer, required: true }
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Child entities get a `getByParentId()` method in their store and `listByParent()` in their service. Use `input.parentId` in child use cases to link them to the parent.
|
|
236
|
+
|
|
237
|
+
#### Value Objects
|
|
363
238
|
|
|
364
|
-
|
|
239
|
+
Value objects are reusable types embedded in aggregates, stored as JSON in the database:
|
|
240
|
+
|
|
241
|
+
```yaml
|
|
242
|
+
domain:
|
|
243
|
+
valueObjects:
|
|
244
|
+
Money:
|
|
245
|
+
fields:
|
|
246
|
+
amount: { type: decimal, constraints: { min: 0 } }
|
|
247
|
+
currency: { type: enum, values: [USD, EUR, PLN] }
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
โ Reference: [aggregates](REFERENCE.md#aggregates) ยท [valueObjects](REFERENCE.md#valueobjects) ยท [Field Types](REFERENCE.md#field-types) ยท [Child Entities](REFERENCE.md#child-entities)
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
### Use Cases Layer (`useCases`)
|
|
255
|
+
|
|
256
|
+
Use cases define the operations available for each model. Each use case specifies its input shape, output shape, and a chain of handlers to execute.
|
|
257
|
+
|
|
258
|
+
```yaml
|
|
365
259
|
useCases:
|
|
366
260
|
Post:
|
|
367
261
|
list:
|
|
@@ -377,15 +271,66 @@ useCases:
|
|
|
377
271
|
input: { from: Post }
|
|
378
272
|
output: { from: Post }
|
|
379
273
|
handlers: [default:create]
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
#### Handlers
|
|
277
|
+
|
|
278
|
+
Handlers are listed in execution order. Each handler becomes a method on the service class.
|
|
279
|
+
|
|
280
|
+
**Built-in handlers:**
|
|
281
|
+
|
|
282
|
+
| Handler | Description |
|
|
283
|
+
|---|---|
|
|
284
|
+
| `default:list` | Paginated list of entities |
|
|
285
|
+
| `default:get` | Fetch by ID |
|
|
286
|
+
| `default:create` | Create with validation |
|
|
287
|
+
| `default:update` | Update by ID |
|
|
288
|
+
| `default:delete` | Soft-delete by ID |
|
|
289
|
+
|
|
290
|
+
**Custom handlers** โ use `methodName` (or `service:methodName`). The generator creates a stub method that receives `(result, input)`:
|
|
291
|
+
|
|
292
|
+
```yaml
|
|
293
|
+
useCases:
|
|
294
|
+
Post:
|
|
295
|
+
publish:
|
|
385
296
|
input: { identifier: id }
|
|
386
|
-
output:
|
|
387
|
-
handlers:
|
|
297
|
+
output: { from: Post }
|
|
298
|
+
handlers:
|
|
299
|
+
- default:get
|
|
300
|
+
- validateForPublish
|
|
301
|
+
- updatePublishStatus
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
This generates three service methods called in sequence. Custom methods get a TODO comment for you to fill in.
|
|
305
|
+
|
|
306
|
+
#### Input Configuration
|
|
307
|
+
|
|
308
|
+
Inputs can derive fields from a model (`from`), pick/omit specific fields, add extra fields, define validation rules, enable pagination, filtering, and sorting. See the [Reference](REFERENCE.md) for the full input specification.
|
|
309
|
+
|
|
310
|
+
#### Displaying Child Entities (`withChild`)
|
|
311
|
+
|
|
312
|
+
When an aggregate root has child entities, you can show them on the root's pages:
|
|
313
|
+
|
|
314
|
+
```yaml
|
|
315
|
+
useCases:
|
|
316
|
+
Invoice:
|
|
317
|
+
list:
|
|
318
|
+
withChild: true # Adds a link column to child entities on the list page
|
|
319
|
+
# ...
|
|
320
|
+
get:
|
|
321
|
+
withChild: true # Shows a child entities table on the detail page
|
|
322
|
+
# ...
|
|
323
|
+
```
|
|
388
324
|
|
|
325
|
+
โ Reference: [useCases](REFERENCE.md#usecases) ยท [handlers](REFERENCE.md#handlers) ยท [input](REFERENCE.md#input) ยท [output](REFERENCE.md#output)
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
### API Layer (`api`)
|
|
330
|
+
|
|
331
|
+
Defines REST API endpoints. Each model gets its own section keyed by name:
|
|
332
|
+
|
|
333
|
+
```yaml
|
|
389
334
|
api:
|
|
390
335
|
Post:
|
|
391
336
|
prefix: /api/posts
|
|
@@ -394,10 +339,6 @@ api:
|
|
|
394
339
|
path: /
|
|
395
340
|
useCase: Post:list
|
|
396
341
|
auth: all
|
|
397
|
-
- method: GET
|
|
398
|
-
path: /:id
|
|
399
|
-
useCase: Post:get
|
|
400
|
-
auth: all
|
|
401
342
|
- method: POST
|
|
402
343
|
path: /
|
|
403
344
|
useCase: Post:create
|
|
@@ -406,11 +347,33 @@ api:
|
|
|
406
347
|
path: /:id
|
|
407
348
|
useCase: Post:update
|
|
408
349
|
auth: [owner, admin]
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
auth: [owner, admin]
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Each endpoint references a use case with the format `ModelName:actionName`.
|
|
413
353
|
|
|
354
|
+
#### Auth / Roles
|
|
355
|
+
|
|
356
|
+
The `auth` field controls access:
|
|
357
|
+
|
|
358
|
+
| Value | Meaning |
|
|
359
|
+
|---|---|
|
|
360
|
+
| `all` | Public, no authentication required |
|
|
361
|
+
| `authenticated` | Any logged-in user (valid JWT) |
|
|
362
|
+
| `owner` | User must own the resource (matched via `ownerId`) |
|
|
363
|
+
| `admin`, `editor`, etc. | User must have this role (from JWT) |
|
|
364
|
+
| `[owner, admin]` | OR logic โ user matches any of the listed roles |
|
|
365
|
+
|
|
366
|
+
When `owner` is combined with privileged roles (e.g., `[owner, admin]`), privileged roles bypass the ownership check.
|
|
367
|
+
|
|
368
|
+
โ Reference: [api](REFERENCE.md#api) ยท [auth](REFERENCE.md#auth)
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
### Web Layer (`web`)
|
|
373
|
+
|
|
374
|
+
Defines server-rendered pages and forms:
|
|
375
|
+
|
|
376
|
+
```yaml
|
|
414
377
|
web:
|
|
415
378
|
Post:
|
|
416
379
|
prefix: /posts
|
|
@@ -420,10 +383,6 @@ web:
|
|
|
420
383
|
useCase: Post:list
|
|
421
384
|
view: postList
|
|
422
385
|
auth: all
|
|
423
|
-
- path: /:id
|
|
424
|
-
useCase: Post:get
|
|
425
|
-
view: postDetail
|
|
426
|
-
auth: all
|
|
427
386
|
- path: /create
|
|
428
387
|
method: GET
|
|
429
388
|
view: postCreate
|
|
@@ -434,761 +393,181 @@ web:
|
|
|
434
393
|
auth: authenticated
|
|
435
394
|
onSuccess:
|
|
436
395
|
redirect: /posts/:id
|
|
437
|
-
toast: "Post created
|
|
396
|
+
toast: "Post created"
|
|
438
397
|
onError:
|
|
439
398
|
stay: true
|
|
440
399
|
toast: error
|
|
441
|
-
- path: /:id/edit
|
|
442
|
-
method: GET
|
|
443
|
-
useCase: Post:get
|
|
444
|
-
view: postEdit
|
|
445
|
-
auth: [owner, admin]
|
|
446
|
-
- path: /:id/edit
|
|
447
|
-
method: POST
|
|
448
|
-
useCase: Post:update
|
|
449
|
-
auth: [owner, admin]
|
|
450
|
-
onSuccess:
|
|
451
|
-
back: true
|
|
452
|
-
toast: "Post updated successfully"
|
|
453
|
-
```
|
|
454
|
-
> **Note**: All CRUD use cases, API endpoints, and web pages are automatically generated when you run `currentjs create module Blog`. The only thing left for you is your domain fields.
|
|
455
|
-
|
|
456
|
-
### 3. Generate everything
|
|
457
|
-
```bash
|
|
458
|
-
currentjs generate
|
|
459
|
-
npm start
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
**Boom!** ๐ฅ You now have a complete blog system with:
|
|
463
|
-
- REST API endpoints at `/api/posts/*`
|
|
464
|
-
- Web interface at `/posts/*`
|
|
465
|
-
- Full CRUD operations
|
|
466
|
-
- HTML templates for all views
|
|
467
|
-
- Database integration ready to go
|
|
468
|
-
|
|
469
|
-
## Generated Project Structure ๐๏ธ
|
|
470
|
-
|
|
471
400
|
```
|
|
472
|
-
my-app/
|
|
473
|
-
โโโ package.json # Dependencies (router, templating, providers)
|
|
474
|
-
โโโ tsconfig.json # TypeScript configuration
|
|
475
|
-
โโโ app.yaml # Main application config
|
|
476
|
-
โโโ src/
|
|
477
|
-
โ โโโ app.ts # Main application entry point (with DI wiring)
|
|
478
|
-
โ โโโ system.ts # @Injectable decorator for DI
|
|
479
|
-
โ โโโ common/ # Shared utilities and templates
|
|
480
|
-
โ โ โโโ services/ # Common services
|
|
481
|
-
โ โ โโโ ui/
|
|
482
|
-
โ โ โโโ templates/
|
|
483
|
-
โ โ โโโ main_view.html # Main layout template
|
|
484
|
-
โ โ โโโ error.html # Error page template
|
|
485
|
-
โ โโโ modules/ # Your business modules
|
|
486
|
-
โ โโโ YourModule/
|
|
487
|
-
โ โโโ yourmodule.yaml # Module specification
|
|
488
|
-
โ โโโ domain/
|
|
489
|
-
โ โ โโโ entities/ # Domain models (aggregates)
|
|
490
|
-
โ โ โโโ valueObjects/ # Value objects
|
|
491
|
-
โ โโโ application/
|
|
492
|
-
โ โ โโโ useCases/ # Use case orchestrators
|
|
493
|
-
โ โ โโโ services/ # Business logic handlers
|
|
494
|
-
โ โ โโโ dto/ # Input/Output DTOs
|
|
495
|
-
โ โโโ infrastructure/
|
|
496
|
-
โ โ โโโ controllers/ # HTTP endpoints (API + Web)
|
|
497
|
-
โ โ โโโ stores/ # Data access
|
|
498
|
-
โ โโโ views/ # HTML templates
|
|
499
|
-
โโโ build/ # Compiled JavaScript
|
|
500
|
-
โโโ web/ # Static assets, served as is
|
|
501
|
-
โโโ app.js # Frontend JavaScript
|
|
502
|
-
โโโ translations.json # i18n support
|
|
503
|
-
```
|
|
504
|
-
|
|
505
401
|
|
|
506
|
-
|
|
402
|
+
Form submission results are handled with `onSuccess` / `onError`:
|
|
507
403
|
|
|
508
|
-
|
|
404
|
+
- `toast: "message"` โ show a toast notification
|
|
405
|
+
- `back: true` โ navigate back in browser history
|
|
406
|
+
- `redirect: /path` โ redirect to a URL (supports `:id` placeholder)
|
|
407
|
+
- `stay: true` โ stay on the current page
|
|
509
408
|
|
|
510
|
-
|
|
409
|
+
โ Reference: [web](REFERENCE.md#web) ยท [auth](REFERENCE.md#auth)
|
|
511
410
|
|
|
512
|
-
|
|
513
|
-
2. **Constructor-based discovery**: The generator scans constructors to determine what each class needs (e.g., `InvoiceService` needs `InvoiceStore`).
|
|
514
|
-
3. **Automatic ordering**: Dependencies are topologically sorted โ stores first, then services, then use cases, then controllers.
|
|
515
|
-
4. **Wiring in `app.ts`**: All imports, instantiations, and the `controllers` array are auto-generated between marker comments.
|
|
411
|
+
---
|
|
516
412
|
|
|
517
|
-
|
|
413
|
+
## Generated Source Code
|
|
518
414
|
|
|
519
|
-
|
|
415
|
+
When you run `currentjs generate`, the following files are produced for each model defined in a module:
|
|
520
416
|
|
|
521
|
-
```typescript
|
|
522
|
-
export function Injectable() {
|
|
523
|
-
return function (target: any) {
|
|
524
|
-
target.__injectable = true;
|
|
525
|
-
};
|
|
526
|
-
}
|
|
527
417
|
```
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
// ...
|
|
544
|
-
}
|
|
545
|
-
}
|
|
418
|
+
src/modules/<ModuleName>/
|
|
419
|
+
domain/
|
|
420
|
+
entities/<EntityName>.ts โ Domain entity class with typed constructor and setters
|
|
421
|
+
valueObjects/<ValueObject>.ts โ Value object class (if defined)
|
|
422
|
+
application/
|
|
423
|
+
dto/<Action>InputDto.ts โ Input DTO with parse() and validation
|
|
424
|
+
dto/<Action>OutputDto.ts โ Output DTO with from() mapper
|
|
425
|
+
useCases/<EntityName>UseCase.ts โ Use case orchestrator (calls service methods in sequence)
|
|
426
|
+
services/<EntityName>Service.ts โ Service with handler implementations (CRUD + custom stubs)
|
|
427
|
+
infrastructure/
|
|
428
|
+
controllers/<EntityName>ApiController.ts โ REST endpoints with auth checks
|
|
429
|
+
controllers/<EntityName>WebController.ts โ Page rendering with form handling
|
|
430
|
+
stores/<EntityName>Store.ts โ Database access (CRUD, row-to-model conversion, relationships)
|
|
431
|
+
views/
|
|
432
|
+
<viewName>.html โ HTML templates (list, detail, create, edit forms)
|
|
546
433
|
```
|
|
547
434
|
|
|
548
|
-
|
|
435
|
+
The generator also updates `src/app.ts` with dependency injection wiring between marker comments (`// currentjs:controllers:start` ... `// currentjs:controllers:end`). This section is fully regenerated each time โ imports, instantiation order (topologically sorted), and the controllers array.
|
|
549
436
|
|
|
550
|
-
|
|
437
|
+
Each generated class is decorated with `@Injectable()` or `@Controller()`, so the DI system discovers and wires them automatically.
|
|
551
438
|
|
|
552
|
-
|
|
439
|
+
โ Reference: [Generated File Structure](REFERENCE.md#generated-file-structure) ยท [generate](REFERENCE.md#generate)
|
|
553
440
|
|
|
554
|
-
|
|
555
|
-
database:
|
|
556
|
-
provider: "@currentjs/provider-mysql" # npm package
|
|
557
|
-
# or:
|
|
558
|
-
provider: "./src/common/SomeProvider" # local file path
|
|
559
|
-
```
|
|
441
|
+
## Change Tracking: `diff` and `commit`
|
|
560
442
|
|
|
561
|
-
|
|
443
|
+
Generated code often needs small adjustments โ custom business logic, template tweaks, validation rules. The generator includes a change tracking system so these adjustments survive regeneration.
|
|
562
444
|
|
|
563
|
-
|
|
564
|
-
# In module's yaml section of app.yaml
|
|
565
|
-
modules:
|
|
566
|
-
- name: Analytics
|
|
567
|
-
database:
|
|
568
|
-
provider: "@currentjs/provider-postgres"
|
|
569
|
-
```
|
|
445
|
+
### How it works
|
|
570
446
|
|
|
571
|
-
|
|
447
|
+
1. **Registry** โ when files are generated, their content hashes are stored in `registry.json`.
|
|
572
448
|
|
|
573
|
-
|
|
449
|
+
2. **`currentjs diff [module]`** โ compares each generated file's current content against what the generator would produce. Reports files as `[clean]`, `[modified]`, or `[missing]`.
|
|
574
450
|
|
|
575
|
-
|
|
451
|
+
3. **`currentjs commit [files...]`** โ records the differences between your current files and the generated baseline. Diffs are saved as JSON files in the `commits/` directory.
|
|
576
452
|
|
|
577
|
-
|
|
578
|
-
// currentjs:controllers:start
|
|
579
|
-
import { InvoiceStore } from './modules/Invoice/infrastructure/stores/InvoiceStore';
|
|
580
|
-
import { InvoiceService } from './modules/Invoice/application/services/InvoiceService';
|
|
581
|
-
import { InvoiceUseCase } from './modules/Invoice/application/useCases/InvoiceUseCase';
|
|
582
|
-
import { InvoiceApiController } from './modules/Invoice/infrastructure/controllers/InvoiceApiController';
|
|
583
|
-
import { InvoiceWebController } from './modules/Invoice/infrastructure/controllers/InvoiceWebController';
|
|
453
|
+
4. **Regeneration** โ on the next `currentjs generate`, committed changes are reapplied to the freshly generated code. If a change cannot be applied cleanly (e.g., the generated code changed in the same area), you are prompted to resolve it (unless `--force` or `--skip` is set).
|
|
584
454
|
|
|
585
|
-
|
|
586
|
-
const invoiceStore = new InvoiceStore(db);
|
|
587
|
-
const invoiceService = new InvoiceService(invoiceStore);
|
|
588
|
-
const invoiceUseCase = new InvoiceUseCase(invoiceService);
|
|
455
|
+
### Practical workflow
|
|
589
456
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
];
|
|
594
|
-
// currentjs:controllers:end
|
|
595
|
-
```
|
|
596
|
-
|
|
597
|
-
This block is fully regenerated on each `currentjs generate` run. You never need to edit it manually.
|
|
457
|
+
```bash
|
|
458
|
+
# Generate code
|
|
459
|
+
currentjs generate
|
|
598
460
|
|
|
599
|
-
|
|
461
|
+
# Make your changes (service logic, templates, etc.)
|
|
462
|
+
# ...
|
|
600
463
|
|
|
601
|
-
|
|
464
|
+
# See what you changed
|
|
465
|
+
currentjs diff Blog
|
|
602
466
|
|
|
603
|
-
|
|
467
|
+
# Save your changes
|
|
468
|
+
currentjs commit
|
|
604
469
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
โโโ domain/
|
|
609
|
-
โ โโโ entities/ # Generated domain models (aggregates)
|
|
610
|
-
โ โโโ valueObjects/ # Generated value objects
|
|
611
|
-
โโโ application/
|
|
612
|
-
โ โโโ useCases/ # Generated use case orchestrators
|
|
613
|
-
โ โโโ services/ # Generated business logic handlers
|
|
614
|
-
โ โโโ dto/ # Generated Input/Output DTOs
|
|
615
|
-
โโโ infrastructure/
|
|
616
|
-
โ โโโ controllers/ # Generated HTTP endpoints (API + Web)
|
|
617
|
-
โ โโโ stores/ # Generated data access
|
|
618
|
-
โโโ views/ # Generated HTML templates
|
|
470
|
+
# Later, after modifying the YAML and regenerating:
|
|
471
|
+
currentjs generate
|
|
472
|
+
# Your committed changes are reapplied automatically
|
|
619
473
|
```
|
|
620
474
|
|
|
621
|
-
###
|
|
475
|
+
### Repository strategy
|
|
622
476
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
```yaml
|
|
626
|
-
domain:
|
|
627
|
-
aggregates:
|
|
628
|
-
Post:
|
|
629
|
-
root: true # Marks as aggregate root
|
|
630
|
-
fields:
|
|
631
|
-
title: { type: string, required: true }
|
|
632
|
-
content: { type: string, required: true }
|
|
633
|
-
authorId: { type: id, required: true }
|
|
634
|
-
publishedAt: { type: datetime }
|
|
635
|
-
status: { type: string, required: true }
|
|
477
|
+
You can choose to either commit generated source code to git normally, or keep your repository lean by only tracking YAML files, `registry.json`, and the `commits/` directory. In the latter case, anyone can recreate the full source by running `currentjs generate`.
|
|
636
478
|
|
|
637
|
-
|
|
638
|
-
Post:
|
|
639
|
-
list:
|
|
640
|
-
input:
|
|
641
|
-
pagination: { type: offset, defaults: { limit: 20, maxLimit: 100 } }
|
|
642
|
-
output: { from: Post, pagination: true }
|
|
643
|
-
handlers: [default:list] # Built-in list handler
|
|
644
|
-
get:
|
|
645
|
-
input: { identifier: id }
|
|
646
|
-
output: { from: Post }
|
|
647
|
-
handlers: [default:get] # Built-in get handler
|
|
648
|
-
create:
|
|
649
|
-
input: { from: Post }
|
|
650
|
-
output: { from: Post }
|
|
651
|
-
handlers: [default:create]
|
|
652
|
-
update:
|
|
653
|
-
input: { identifier: id, from: Post, partial: true }
|
|
654
|
-
output: { from: Post }
|
|
655
|
-
handlers: [default:update]
|
|
656
|
-
delete:
|
|
657
|
-
input: { identifier: id }
|
|
658
|
-
output: void
|
|
659
|
-
handlers: [ # Chain multiple handlers
|
|
660
|
-
checkCanDelete, # Custom โ PostService.checkCanDelete(result, input)
|
|
661
|
-
default:delete # Built-in delete
|
|
662
|
-
]
|
|
663
|
-
publish: # Custom action
|
|
664
|
-
input: { identifier: id }
|
|
665
|
-
output: { from: Post }
|
|
666
|
-
handlers: [
|
|
667
|
-
default:get, # Fetch entity
|
|
668
|
-
validateForPublish, # Custom โ PostService.validateForPublish(result, input)
|
|
669
|
-
updatePublishStatus # Custom โ PostService.updatePublishStatus(result, input)
|
|
670
|
-
]
|
|
671
|
-
|
|
672
|
-
api: # REST API configuration
|
|
673
|
-
Post: # Keyed by model name
|
|
674
|
-
prefix: /api/posts
|
|
675
|
-
endpoints:
|
|
676
|
-
- method: GET
|
|
677
|
-
path: /
|
|
678
|
-
useCase: Post:list # References useCases.Post.list
|
|
679
|
-
auth: all # Public access
|
|
680
|
-
- method: GET
|
|
681
|
-
path: /:id
|
|
682
|
-
useCase: Post:get
|
|
683
|
-
auth: all
|
|
684
|
-
- method: POST
|
|
685
|
-
path: /
|
|
686
|
-
useCase: Post:create
|
|
687
|
-
auth: authenticated # Must be logged in
|
|
688
|
-
- method: PUT
|
|
689
|
-
path: /:id
|
|
690
|
-
useCase: Post:update
|
|
691
|
-
auth: [owner, admin] # Owner OR admin (OR logic)
|
|
692
|
-
- method: DELETE
|
|
693
|
-
path: /:id
|
|
694
|
-
useCase: Post:delete
|
|
695
|
-
auth: [owner, admin]
|
|
696
|
-
- method: POST # Custom endpoint
|
|
697
|
-
path: /:id/publish
|
|
698
|
-
useCase: Post:publish
|
|
699
|
-
auth: [owner, editor, admin]
|
|
479
|
+
โ Reference: [diff](REFERENCE.md#diff) ยท [commit](REFERENCE.md#commit) ยท [Notes โ File Change Tracking](REFERENCE.md#file-change-tracking) ยท [Notes โ Commit Mechanism](REFERENCE.md#commit-mechanism)
|
|
700
480
|
|
|
701
|
-
|
|
702
|
-
Post: # Keyed by model name
|
|
703
|
-
prefix: /posts
|
|
704
|
-
layout: main_view
|
|
705
|
-
pages:
|
|
706
|
-
- path: / # List page
|
|
707
|
-
useCase: Post:list
|
|
708
|
-
view: postList
|
|
709
|
-
auth: all
|
|
710
|
-
- path: /:id # Detail page
|
|
711
|
-
useCase: Post:get
|
|
712
|
-
view: postDetail
|
|
713
|
-
auth: all
|
|
714
|
-
- path: /create # Create form (GET = show form)
|
|
715
|
-
method: GET
|
|
716
|
-
view: postCreate
|
|
717
|
-
auth: authenticated
|
|
718
|
-
- path: /create # Create form (POST = submit)
|
|
719
|
-
method: POST
|
|
720
|
-
useCase: Post:create
|
|
721
|
-
auth: authenticated
|
|
722
|
-
onSuccess:
|
|
723
|
-
redirect: /posts/:id
|
|
724
|
-
toast: "Post created successfully"
|
|
725
|
-
onError:
|
|
726
|
-
stay: true
|
|
727
|
-
toast: error
|
|
728
|
-
- path: /:id/edit # Edit form (GET = show form)
|
|
729
|
-
method: GET
|
|
730
|
-
useCase: Post:get
|
|
731
|
-
view: postUpdate
|
|
732
|
-
auth: [owner, admin]
|
|
733
|
-
- path: /:id/edit # Edit form (POST = submit)
|
|
734
|
-
method: POST
|
|
735
|
-
useCase: Post:update
|
|
736
|
-
auth: [owner, admin]
|
|
737
|
-
onSuccess:
|
|
738
|
-
back: true
|
|
739
|
-
toast: "Post updated successfully"
|
|
740
|
-
```
|
|
481
|
+
## Database Migrations
|
|
741
482
|
|
|
742
|
-
|
|
483
|
+
The generator can produce SQL migration files based on changes to your domain models.
|
|
743
484
|
|
|
744
|
-
|
|
485
|
+
### `migrate commit`
|
|
745
486
|
|
|
746
|
-
|
|
747
|
-
|
|
487
|
+
```bash
|
|
488
|
+
currentjs migrate commit
|
|
489
|
+
```
|
|
748
490
|
|
|
749
|
-
|
|
491
|
+
Collects all aggregate definitions from module YAMLs, compares them against the stored schema state (`migrations/schema_state.yaml`), and generates a `.sql` migration file in the `migrations/` directory.
|
|
750
492
|
|
|
751
|
-
|
|
493
|
+
The migration file contains `CREATE TABLE`, `ALTER TABLE ADD/MODIFY/DROP COLUMN`, and `DROP TABLE` statements as needed. Foreign keys, indexes, and standard timestamp columns (`created_at`, `updated_at`, `deleted_at`) are handled automatically.
|
|
752
494
|
|
|
753
|
-
|
|
754
|
-
useCases:
|
|
755
|
-
Invoice:
|
|
756
|
-
list:
|
|
757
|
-
withChild: true # Adds link column to child entities on list page
|
|
758
|
-
input:
|
|
759
|
-
pagination: { type: offset, defaults: { limit: 20, maxLimit: 100 } }
|
|
760
|
-
output: { from: Invoice, pagination: true }
|
|
761
|
-
handlers: [default:list]
|
|
762
|
-
get:
|
|
763
|
-
withChild: true # Shows child entities table on detail page
|
|
764
|
-
input: { identifier: id }
|
|
765
|
-
output: { from: Invoice }
|
|
766
|
-
handlers: [default:get]
|
|
767
|
-
```
|
|
495
|
+
After generating the file, the schema state is updated so the next `migrate commit` only produces a diff of subsequent changes.
|
|
768
496
|
|
|
769
|
-
###
|
|
770
|
-
|
|
771
|
-
**Available Field Types:**
|
|
772
|
-
- `string` - Text data (VARCHAR in database)
|
|
773
|
-
- `number` - Numeric data (INT/DECIMAL in database)
|
|
774
|
-
- `integer` - Integer data (INT in database)
|
|
775
|
-
- `decimal` - Decimal data (DECIMAL in database)
|
|
776
|
-
- `boolean` - True/false values (BOOLEAN in database)
|
|
777
|
-
- `datetime` - Date and time values (DATETIME in database)
|
|
778
|
-
- `date` - Date values (DATE in database)
|
|
779
|
-
- `id` - Foreign key reference (INT in database)
|
|
780
|
-
- `json` - JSON data
|
|
781
|
-
- `enum` - Enumerated values (use with `values: [...]`)
|
|
782
|
-
|
|
783
|
-
**Important Field Rules:**
|
|
784
|
-
- **Never include `id`/`owner_id`/`created_at`/`updated_at`/`deleted_at` fields** - these are added automatically
|
|
785
|
-
- Use `required: true` for mandatory fields
|
|
786
|
-
- Fields without `required` are optional
|
|
497
|
+
### `migrate push` *(not yet implemented)*
|
|
787
498
|
|
|
788
|
-
|
|
789
|
-
domain:
|
|
790
|
-
aggregates:
|
|
791
|
-
User:
|
|
792
|
-
root: true
|
|
793
|
-
fields:
|
|
794
|
-
email: { type: string, required: true }
|
|
795
|
-
age: { type: number }
|
|
796
|
-
isActive: { type: boolean, required: true }
|
|
797
|
-
lastLoginAt: { type: datetime }
|
|
798
|
-
```
|
|
499
|
+
Will apply pending migration files to the database.
|
|
799
500
|
|
|
800
|
-
###
|
|
501
|
+
### `migrate update` *(not yet implemented)*
|
|
801
502
|
|
|
802
|
-
|
|
503
|
+
Will compare the live database schema against the current model definitions and generate a migration to bring them in sync.
|
|
803
504
|
|
|
804
|
-
|
|
805
|
-
```yaml
|
|
806
|
-
domain:
|
|
807
|
-
aggregates:
|
|
808
|
-
Owner:
|
|
809
|
-
root: true
|
|
810
|
-
fields:
|
|
811
|
-
name: { type: string, required: true }
|
|
812
|
-
email: { type: string, required: true }
|
|
505
|
+
โ Reference: [migrate commit](REFERENCE.md#migrate-commit)
|
|
813
506
|
|
|
814
|
-
|
|
815
|
-
root: true
|
|
816
|
-
fields:
|
|
817
|
-
name: { type: string, required: true }
|
|
818
|
-
breed: { type: string }
|
|
819
|
-
owner: { type: Owner, required: true } # Relationship to Owner model
|
|
820
|
-
```
|
|
507
|
+
## Template System
|
|
821
508
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
The generator uses **Infrastructure-Level Relationship Assembly**:
|
|
825
|
-
|
|
826
|
-
- **Domain Layer**: Works with full objects (no FKs)
|
|
827
|
-
- **Infrastructure (Store)**: Handles FK โ Object conversion
|
|
828
|
-
- **DTOs**: Use FKs for API transmission
|
|
829
|
-
|
|
830
|
-
**What Gets Generated:**
|
|
831
|
-
|
|
832
|
-
1. **Domain Model**: Pure business objects
|
|
833
|
-
```typescript
|
|
834
|
-
import { Owner } from './Owner';
|
|
835
|
-
|
|
836
|
-
export class Cat {
|
|
837
|
-
constructor(
|
|
838
|
-
public id: number,
|
|
839
|
-
public name: string,
|
|
840
|
-
public breed?: string,
|
|
841
|
-
public owner: Owner // โจ Full object, no FK!
|
|
842
|
-
) {}
|
|
843
|
-
}
|
|
844
|
-
```
|
|
845
|
-
|
|
846
|
-
2. **DTOs**: Use foreign keys for API
|
|
847
|
-
```typescript
|
|
848
|
-
export interface CatDTO {
|
|
849
|
-
name: string;
|
|
850
|
-
breed?: string;
|
|
851
|
-
ownerId: number; // โจ FK for over-the-wire
|
|
852
|
-
}
|
|
853
|
-
```
|
|
854
|
-
|
|
855
|
-
3. **Store**: Converts FK โ Object
|
|
856
|
-
```typescript
|
|
857
|
-
export class CatStore {
|
|
858
|
-
constructor(
|
|
859
|
-
private db: ISqlProvider,
|
|
860
|
-
private ownerStore: OwnerStore // โจ Foreign store dependency
|
|
861
|
-
) {}
|
|
862
|
-
|
|
863
|
-
async loadRelationships(entity: Cat, row: CatRow): Promise<Cat> {
|
|
864
|
-
const owner = await this.ownerStore.getById(row.ownerId);
|
|
865
|
-
if (owner) entity.setOwner(owner);
|
|
866
|
-
return entity;
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
async insert(cat: Cat): Promise<Cat> {
|
|
870
|
-
const row = {
|
|
871
|
-
name: cat.name,
|
|
872
|
-
ownerId: cat.owner?.id // โจ Extract FK to save
|
|
873
|
-
};
|
|
874
|
-
// ...
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
```
|
|
878
|
-
|
|
879
|
-
4. **Service**: Loads objects from FKs
|
|
880
|
-
```typescript
|
|
881
|
-
export class CatService {
|
|
882
|
-
constructor(
|
|
883
|
-
private catStore: CatStore,
|
|
884
|
-
private ownerStore: OwnerStore // โจ To load relationships
|
|
885
|
-
) {}
|
|
886
|
-
|
|
887
|
-
async create(catData: CatDTO): Promise<Cat> {
|
|
888
|
-
// โจ Load full owner object from FK
|
|
889
|
-
const owner = await this.ownerStore.getById(catData.ownerId);
|
|
890
|
-
const cat = new Cat(0, catData.name, catData.breed, owner);
|
|
891
|
-
return await this.catStore.insert(cat);
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
```
|
|
895
|
-
|
|
896
|
-
5. **HTML Forms**: Select dropdown with "Create New" button
|
|
897
|
-
```html
|
|
898
|
-
<select id="ownerId" name="ownerId" required>
|
|
899
|
-
<option value="">-- Select Owner --</option>
|
|
900
|
-
<!-- Options loaded from /api/owner -->
|
|
901
|
-
</select>
|
|
902
|
-
<button onclick="window.open('/owner/create')">+ New</button>
|
|
903
|
-
```
|
|
904
|
-
|
|
905
|
-
**Relationship Naming Convention:**
|
|
906
|
-
|
|
907
|
-
The generator automatically creates foreign key fields following this convention:
|
|
908
|
-
- **Field name**: `owner` โ **Foreign key**: `ownerId`
|
|
909
|
-
- **Field name**: `author` โ **Foreign key**: `authorId`
|
|
910
|
-
- **Field name**: `parentComment` โ **Foreign key**: `parentCommentId`
|
|
911
|
-
|
|
912
|
-
The foreign key always references the `id` field of the related model.
|
|
913
|
-
|
|
914
|
-
**Multiple Relationships:**
|
|
915
|
-
```yaml
|
|
916
|
-
domain:
|
|
917
|
-
aggregates:
|
|
918
|
-
Comment:
|
|
919
|
-
root: true
|
|
920
|
-
fields:
|
|
921
|
-
content: { type: string, required: true }
|
|
922
|
-
post: { type: Post, required: true } # Creates foreign key: postId
|
|
923
|
-
author: { type: User, required: true } # Creates foreign key: authorId
|
|
924
|
-
parentComment: { type: Comment } # Self-referential, optional
|
|
925
|
-
```
|
|
509
|
+
Generated HTML templates use the `@currentjs/templating` engine. Templates are placed in the module's `views/` directory and referenced by name in the `web` section of the YAML.
|
|
926
510
|
|
|
927
|
-
|
|
928
|
-
- โ
Always define the foreign model first in the same module
|
|
929
|
-
- โ
Use descriptive field names for relationships (e.g., `author` instead of `user`)
|
|
930
|
-
- โ
Set appropriate `displayFields` to show meaningful data in dropdowns
|
|
931
|
-
- โ
Use `required: false` for optional relationships
|
|
932
|
-
- โ
Foreign keys are auto-generated following the pattern `fieldName + 'Id'`
|
|
933
|
-
- โ Don't manually add foreign key fields (they're auto-generated)
|
|
934
|
-
- โ Don't create circular dependencies between modules
|
|
935
|
-
|
|
936
|
-
### Use Case Handlers Explained
|
|
937
|
-
|
|
938
|
-
**๐ Handler vs Use Case Distinction:**
|
|
939
|
-
- **Handler**: Creates a separate service method (one handler = one method)
|
|
940
|
-
- **Use Case**: Defined under `useCases.ModelName.actionName`, orchestrates handler calls step-by-step
|
|
941
|
-
- **UseCase reference**: Used in `api`/`web` endpoints as `ModelName:actionName` (e.g., `Post:list`)
|
|
942
|
-
|
|
943
|
-
**Built-in Handlers (inside `useCases.*.*.handlers`):**
|
|
944
|
-
- `default:list` - Creates service method with pagination parameters
|
|
945
|
-
- `default:get` - Creates service method named `get` with ID parameter
|
|
946
|
-
- `default:create` - Creates service method with DTO parameter
|
|
947
|
-
- `default:update` - Creates service method with ID and DTO parameters
|
|
948
|
-
- `default:delete` - Creates service method with ID parameter
|
|
949
|
-
|
|
950
|
-
Note: Handlers within `useCases` do NOT need a model prefix because the model is already the key.
|
|
951
|
-
|
|
952
|
-
**Custom Handlers:**
|
|
953
|
-
- `customMethodName` - Creates service method that accepts `(result, input)` parameters
|
|
954
|
-
- `result`: Result from previous handler (or `null` if it's the first handler)
|
|
955
|
-
- `input`: The parsed input DTO
|
|
956
|
-
- User can customize the implementation after generation
|
|
957
|
-
- Each handler generates a separate method in the service
|
|
958
|
-
|
|
959
|
-
**๐ Multiple Handlers per Use Case:**
|
|
960
|
-
When a use case has multiple handlers, each handler generates a separate service method, and the use case orchestrator calls them sequentially:
|
|
511
|
+
### Template header
|
|
961
512
|
|
|
962
|
-
|
|
963
|
-
useCases:
|
|
964
|
-
Invoice:
|
|
965
|
-
get:
|
|
966
|
-
input: { identifier: id }
|
|
967
|
-
output: { from: Invoice }
|
|
968
|
-
handlers:
|
|
969
|
-
- default:get # Creates InvoiceService.get() method
|
|
970
|
-
- enrichData # Creates InvoiceService.enrichData() method
|
|
971
|
-
```
|
|
513
|
+
Each template starts with a comment that declares its name:
|
|
972
514
|
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
// InvoiceService.ts
|
|
976
|
-
async get(id: number): Promise<Invoice> {
|
|
977
|
-
// Standard get implementation
|
|
978
|
-
}
|
|
979
|
-
async enrichData(result: any, input: any): Promise<any> {
|
|
980
|
-
// TODO: Implement custom enrichData method
|
|
981
|
-
// result = result from previous handler (Invoice object in this case)
|
|
982
|
-
// input = parsed input DTO
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
// InvoiceUseCase.ts
|
|
986
|
-
async get(input: InvoiceGetInput): Promise<Invoice> {
|
|
987
|
-
const result0 = await this.invoiceService.get(input.id);
|
|
988
|
-
const result = await this.invoiceService.enrichData(result0, input);
|
|
989
|
-
return result; // Returns result from last handler
|
|
990
|
-
}
|
|
515
|
+
```html
|
|
516
|
+
<!-- @template name="postList" -->
|
|
991
517
|
```
|
|
992
518
|
|
|
993
|
-
|
|
994
|
-
- **Default handlers** (`default:*`): Receive standard parameters (id, pagination, DTO, etc.)
|
|
995
|
-
- **Custom handlers**: Receive `(result, input)` where:
|
|
996
|
-
- `result`: Result from previous handler, or `null` if it's the first handler
|
|
997
|
-
- `input`: Parsed input DTO
|
|
519
|
+
### Variables
|
|
998
520
|
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
list:
|
|
1004
|
-
handlers: [default:list] # Single handler: list(page, limit)
|
|
1005
|
-
get:
|
|
1006
|
-
handlers: [default:get] # Single handler: get(id)
|
|
1007
|
-
complexFlow:
|
|
1008
|
-
handlers: [
|
|
1009
|
-
default:create, # create(input) - standard parameters
|
|
1010
|
-
sendNotification # sendNotification(result, input) - result from create
|
|
1011
|
-
]
|
|
1012
|
-
customFirst:
|
|
1013
|
-
handlers: [
|
|
1014
|
-
validateInput, # validateInput(null, input) - first handler
|
|
1015
|
-
default:create # create(input) - standard parameters
|
|
1016
|
-
]
|
|
521
|
+
```html
|
|
522
|
+
{{ title }}
|
|
523
|
+
{{ post.authorName }}
|
|
524
|
+
{{ formData.email || '' }}
|
|
1017
525
|
```
|
|
1018
526
|
|
|
1019
|
-
###
|
|
1020
|
-
|
|
1021
|
-
When you have multiple models in a single module, the system generates individual services, use cases, controllers, and stores for each model:
|
|
527
|
+
### Loops
|
|
1022
528
|
|
|
1023
|
-
```
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
Comment:
|
|
1031
|
-
root: true
|
|
1032
|
-
fields:
|
|
1033
|
-
content: { type: string, required: true }
|
|
1034
|
-
post: { type: Post, required: true }
|
|
1035
|
-
|
|
1036
|
-
useCases:
|
|
1037
|
-
Post:
|
|
1038
|
-
list:
|
|
1039
|
-
handlers: [default:list]
|
|
1040
|
-
create:
|
|
1041
|
-
input: { from: Post }
|
|
1042
|
-
output: { from: Post }
|
|
1043
|
-
handlers: [default:create]
|
|
1044
|
-
Comment:
|
|
1045
|
-
create:
|
|
1046
|
-
input: { from: Comment }
|
|
1047
|
-
output: { from: Comment }
|
|
1048
|
-
handlers: [default:create]
|
|
1049
|
-
|
|
1050
|
-
api:
|
|
1051
|
-
Post:
|
|
1052
|
-
prefix: /api/posts
|
|
1053
|
-
endpoints:
|
|
1054
|
-
- method: GET
|
|
1055
|
-
path: /
|
|
1056
|
-
useCase: Post:list
|
|
1057
|
-
auth: all
|
|
1058
|
-
- method: POST
|
|
1059
|
-
path: /
|
|
1060
|
-
useCase: Post:create
|
|
1061
|
-
auth: authenticated
|
|
1062
|
-
Comment:
|
|
1063
|
-
prefix: /api/comments
|
|
1064
|
-
endpoints:
|
|
1065
|
-
- method: POST
|
|
1066
|
-
path: /
|
|
1067
|
-
useCase: Comment:create
|
|
1068
|
-
auth: authenticated
|
|
529
|
+
```html
|
|
530
|
+
<tbody x-for="items" x-row="item">
|
|
531
|
+
<tr>
|
|
532
|
+
<td>{{ item.name }}</td>
|
|
533
|
+
<td>{{ $index }}</td>
|
|
534
|
+
</tr>
|
|
535
|
+
</tbody>
|
|
1069
536
|
```
|
|
1070
537
|
|
|
1071
|
-
|
|
1072
|
-
- Each model gets its own Service, UseCase, Controller, and Store classes
|
|
1073
|
-
- In `api`/`web`, each model is a separate key (e.g., `api.Post`, `api.Comment`)
|
|
1074
|
-
- UseCase references use `ModelName:actionName` format (e.g., `Post:list`, `Comment:create`)
|
|
1075
|
-
- Handlers within `useCases` do not need a model prefix (model is already the key)
|
|
1076
|
-
|
|
1077
|
-
### Form Success/Error Handling
|
|
538
|
+
`x-for` specifies the data key to iterate over, `x-row` names the loop variable. `$index` gives the current iteration index.
|
|
1078
539
|
|
|
1079
|
-
|
|
540
|
+
### Conditionals
|
|
1080
541
|
|
|
1081
|
-
```
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
prefix: /posts
|
|
1085
|
-
pages:
|
|
1086
|
-
- path: /create
|
|
1087
|
-
method: POST
|
|
1088
|
-
useCase: Post:create
|
|
1089
|
-
auth: authenticated
|
|
1090
|
-
onSuccess:
|
|
1091
|
-
redirect: /posts/:id
|
|
1092
|
-
toast: "Post created successfully"
|
|
1093
|
-
onError:
|
|
1094
|
-
stay: true
|
|
1095
|
-
toast: error
|
|
542
|
+
```html
|
|
543
|
+
<div x-if="user.isAdmin">Admin-only content</div>
|
|
544
|
+
<span x-if="errors.name">{{ errors.name }}</span>
|
|
1096
545
|
```
|
|
1097
546
|
|
|
1098
|
-
|
|
1099
|
-
- `toast: "message"` - Show toast notification with custom message
|
|
1100
|
-
- `back: true` - Navigate back in browser history
|
|
1101
|
-
- `redirect: /path` - Redirect to specific URL
|
|
1102
|
-
- `stay: true` - Stay on current page
|
|
547
|
+
### Layouts
|
|
1103
548
|
|
|
1104
|
-
|
|
1105
|
-
- `stay: true` - Stay on current page (re-render form with errors)
|
|
1106
|
-
- `toast: error` - Show error toast notification
|
|
549
|
+
Templates are rendered inside a layout (specified per resource or per page in the `web` config). The layout receives the rendered template content as `{{ content }}`.
|
|
1107
550
|
|
|
1108
|
-
|
|
1109
|
-
```yaml
|
|
1110
|
-
# Show message and go back
|
|
1111
|
-
onSuccess: { toast: "Saved!", back: true }
|
|
551
|
+
### Forms
|
|
1112
552
|
|
|
1113
|
-
|
|
1114
|
-
onSuccess: { redirect: /posts/:id, toast: "Created!" }
|
|
553
|
+
Generated forms include `data-strategy` attributes for the frontend JavaScript to handle submission via AJAX:
|
|
1115
554
|
|
|
1116
|
-
|
|
1117
|
-
|
|
555
|
+
```html
|
|
556
|
+
<form data-strategy='["toast", "back"]'
|
|
557
|
+
data-entity-name="Post"
|
|
558
|
+
data-field-types='{"age": "number", "active": "boolean"}'>
|
|
559
|
+
<input name="title" type="text" required>
|
|
560
|
+
<button type="submit">Save</button>
|
|
561
|
+
</form>
|
|
1118
562
|
```
|
|
1119
563
|
|
|
1120
|
-
The
|
|
564
|
+
The `data-field-types` attribute tells the frontend how to convert form values before sending (e.g., string to number, checkbox to boolean).
|
|
1121
565
|
|
|
1122
|
-
###
|
|
566
|
+
### Template regeneration behavior
|
|
1123
567
|
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
```yaml
|
|
1127
|
-
api:
|
|
1128
|
-
Post:
|
|
1129
|
-
prefix: /api/posts
|
|
1130
|
-
endpoints:
|
|
1131
|
-
- method: GET
|
|
1132
|
-
path: /
|
|
1133
|
-
useCase: Post:list
|
|
1134
|
-
auth: all # Anyone (including anonymous)
|
|
1135
|
-
- method: POST
|
|
1136
|
-
path: /
|
|
1137
|
-
useCase: Post:create
|
|
1138
|
-
auth: authenticated # Any logged-in user
|
|
1139
|
-
- method: PUT
|
|
1140
|
-
path: /:id
|
|
1141
|
-
useCase: Post:update
|
|
1142
|
-
auth: [owner, admin] # Owner OR admin (OR logic)
|
|
1143
|
-
- method: DELETE
|
|
1144
|
-
path: /:id
|
|
1145
|
-
useCase: Post:delete
|
|
1146
|
-
auth: admin # Only admin role
|
|
1147
|
-
```
|
|
568
|
+
By default, `currentjs generate` does not overwrite existing HTML templates. Only missing templates are created. Use `--with-templates` to force regeneration of all templates.
|
|
1148
569
|
|
|
1149
|
-
|
|
1150
|
-
- `all` - Everyone (including anonymous users)
|
|
1151
|
-
- `authenticated` - Any logged-in user
|
|
1152
|
-
- `owner` - User who created the entity
|
|
1153
|
-
- `admin`, `editor`, `user` - Custom roles from JWT token
|
|
1154
|
-
- `[owner, admin]` - Array syntax: user must match ANY (OR logic). Privileged roles bypass ownership check.
|
|
1155
|
-
|
|
1156
|
-
**How Ownership Works:**
|
|
1157
|
-
The system automatically adds an `owner_id` field to aggregate roots to track who created each entity. When `owner` auth is specified:
|
|
1158
|
-
- **For reads (get)**: Post-fetch check compares `result.ownerId` with `context.request.user.id`
|
|
1159
|
-
- **For mutations (update/delete)**: Pre-mutation check calls `getResourceOwner(id)` before the operation to prevent unauthorized changes
|
|
1160
|
-
|
|
1161
|
-
### Code vs Configuration Guidelines
|
|
1162
|
-
|
|
1163
|
-
**โ
Use YAML Configuration For:**
|
|
1164
|
-
- Basic CRUD operations
|
|
1165
|
-
- Standard REST endpoints
|
|
1166
|
-
- Simple permission rules
|
|
1167
|
-
- Form success strategies
|
|
1168
|
-
- Standard data field types
|
|
1169
|
-
|
|
1170
|
-
**โ
Write Custom Code For:**
|
|
1171
|
-
- Complex business logic
|
|
1172
|
-
- Custom validation rules
|
|
1173
|
-
- Data transformations
|
|
1174
|
-
- Integration with external services
|
|
1175
|
-
- Complex database queries
|
|
1176
|
-
|
|
1177
|
-
## Part of the Framework Ecosystem ๐
|
|
1178
|
-
|
|
1179
|
-
This generator is the foundation of the `currentjs` framework:
|
|
1180
|
-
- Works seamlessly with `@currentjs/router` for HTTP handling
|
|
1181
|
-
- Integrates with `@currentjs/templating` for server-side rendering
|
|
1182
|
-
- Uses `@currentjs/provider-*` packages for database access
|
|
1183
|
-
- Follows clean architecture principles for maintainable code
|
|
1184
|
-
|
|
1185
|
-
## Notes
|
|
1186
|
-
|
|
1187
|
-
- `currentjs create app` scaffolds complete app structure with TypeScript configs and dependencies
|
|
1188
|
-
- `currentjs generate` creates domain entities, value objects, use cases, services, DTOs, controllers, stores, and templates
|
|
1189
|
-
- Generated code follows clean architecture: domain/application/infrastructure layers
|
|
1190
|
-
- Supports both API endpoints and web page routes in the same module
|
|
1191
|
-
- Includes change tracking system for safely modifying generated code
|
|
570
|
+
โ Reference: [Notes โ Template Regeneration](REFERENCE.md#template-regeneration) ยท [web](REFERENCE.md#web)
|
|
1192
571
|
|
|
1193
572
|
## Authorship & Contribution
|
|
1194
573
|
|
|
@@ -1203,4 +582,3 @@ GNU Lesser General Public License (LGPL)
|
|
|
1203
582
|
It simply means, that you:
|
|
1204
583
|
- can create a proprietary application that uses this library without having to open source their entire application code (this is the "lesser" aspect of LGPL compared to GPL).
|
|
1205
584
|
- can make any modifications, but must distribute those modifications under the LGPL (or a compatible license) and include the original copyright and license notice.
|
|
1206
|
-
|