sonamu 0.8.24 → 0.8.26
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/dist/api/__tests__/config.test.js +189 -0
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +7 -2
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +14 -10
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +2 -1
- package/dist/auth/knex-adapter.d.ts +23 -0
- package/dist/auth/knex-adapter.d.ts.map +1 -0
- package/dist/auth/knex-adapter.js +163 -0
- package/dist/auth/plugins/wrappers/admin.d.ts +2 -2
- package/dist/bin/__tests__/ts-loader-register.test.js +45 -0
- package/dist/bin/cli.js +47 -9
- package/dist/bin/ts-loader-register.js +3 -29
- package/dist/bin/ts-loader-registration.d.ts +2 -0
- package/dist/bin/ts-loader-registration.d.ts.map +1 -0
- package/dist/bin/ts-loader-registration.js +42 -0
- package/dist/cone/cone-generator.js +3 -3
- package/dist/database/puri-subset.test-d.js +9 -1
- package/dist/database/puri-subset.types.d.ts +1 -1
- package/dist/database/puri-subset.types.d.ts.map +1 -1
- package/dist/database/puri-subset.types.js +1 -1
- package/dist/testing/fixture-generator.js +5 -5
- package/dist/ui/ai-client.js +2 -2
- package/dist/ui/api.d.ts.map +1 -1
- package/dist/ui/api.js +14 -14
- package/dist/ui/cdd-service.d.ts +15 -18
- package/dist/ui/cdd-service.d.ts.map +1 -1
- package/dist/ui/cdd-service.js +246 -222
- package/dist/ui/cdd-types.d.ts +41 -68
- package/dist/ui/cdd-types.d.ts.map +1 -1
- package/dist/ui/cdd-types.js +2 -2
- package/dist/ui-web/assets/index-CKo0Z2Iu.css +1 -0
- package/dist/ui-web/assets/{index-CxiydzeC.js → index-DK-2aacv.js} +83 -83
- package/dist/ui-web/index.html +2 -2
- package/package.json +6 -2
- package/src/api/__tests__/config.test.ts +225 -0
- package/src/api/config.ts +10 -4
- package/src/api/sonamu.ts +16 -13
- package/src/auth/index.ts +1 -0
- package/src/auth/knex-adapter.ts +208 -0
- package/src/bin/__tests__/ts-loader-register.test.ts +62 -0
- package/src/bin/cli.ts +52 -9
- package/src/bin/ts-loader-register.ts +2 -32
- package/src/bin/ts-loader-registration.ts +55 -0
- package/src/cone/cone-generator.ts +2 -2
- package/src/database/puri-subset.test-d.ts +102 -0
- package/src/database/puri-subset.types.ts +1 -1
- package/src/skills/commands/sonamu-skills.md +20 -0
- package/src/skills/sonamu/SKILL.md +179 -137
- package/src/skills/sonamu/ai-agents.md +69 -69
- package/src/skills/sonamu/api.md +147 -147
- package/src/skills/sonamu/auth-migration.md +220 -220
- package/src/skills/sonamu/auth-plugins.md +83 -83
- package/src/skills/sonamu/auth.md +106 -106
- package/src/skills/sonamu/cdd.md +65 -200
- package/src/skills/sonamu/cone.md +138 -138
- package/src/skills/sonamu/config.md +191 -191
- package/src/skills/sonamu/create-sonamu.md +66 -66
- package/src/skills/sonamu/database.md +158 -158
- package/src/skills/sonamu/entity-basic.md +292 -293
- package/src/skills/sonamu/entity-relations.md +246 -246
- package/src/skills/sonamu/entity-validation-checklist.md +124 -124
- package/src/skills/sonamu/fixture-cli.md +231 -231
- package/src/skills/sonamu/framework-change.md +37 -37
- package/src/skills/sonamu/frontend.md +223 -223
- package/src/skills/sonamu/i18n.md +82 -82
- package/src/skills/sonamu/migration.md +77 -77
- package/src/skills/sonamu/model.md +222 -222
- package/src/skills/sonamu/naite.md +86 -86
- package/src/skills/sonamu/project-init.md +228 -228
- package/src/skills/sonamu/puri.md +122 -122
- package/src/skills/sonamu/scaffolding.md +154 -154
- package/src/skills/sonamu/skill-contribution.md +124 -124
- package/src/skills/sonamu/subset.md +46 -46
- package/src/skills/sonamu/tasks.md +82 -82
- package/src/skills/sonamu/testing-devrunner.md +147 -147
- package/src/skills/sonamu/testing.md +673 -673
- package/src/skills/sonamu/upsert.md +79 -79
- package/src/skills/sonamu/vector.md +67 -67
- package/src/testing/fixture-generator.ts +4 -4
- package/src/ui/ai-client.ts +1 -1
- package/src/ui/api.ts +18 -17
- package/src/ui/cdd-service.ts +264 -254
- package/src/ui/cdd-types.ts +40 -75
- package/dist/ui-web/assets/index-BrQKU3j9.css +0 -1
- package/src/skills/sonamu/workflow.md +0 -317
|
@@ -1,161 +1,161 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sonamu-entity-basic
|
|
3
|
-
description:
|
|
3
|
+
description: Reference for creating or modifying Sonamu entities. Field types, requirements analysis, parent-child relationships, OrderBy/Enum rules. Use when creating or modifying entities. See entity-validation-checklist.md for the validation checklist.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Entity
|
|
6
|
+
# Entity Basics
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
**Working code references:**
|
|
9
9
|
|
|
10
|
-
- `sonamu/examples/miomock/api/src/application/user/user.entity.json`
|
|
11
|
-
- `sonamu/examples/miomock/api/src/application/project/project.entity.json`
|
|
12
|
-
- `sonamu/examples/miomock/api/src/application/employee/employee.entity.json`
|
|
10
|
+
- `sonamu/examples/miomock/api/src/application/user/user.entity.json` — basic entity example
|
|
11
|
+
- `sonamu/examples/miomock/api/src/application/project/project.entity.json` — complex entity example
|
|
12
|
+
- `sonamu/examples/miomock/api/src/application/employee/employee.entity.json` — BelongsToOne relationship example
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## When a User Requests a New System
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
When the user requests a system to be built, proceed in the following order:
|
|
17
17
|
|
|
18
|
-
**1.
|
|
18
|
+
**1. Analyze requirements** (identify missing entities)
|
|
19
19
|
|
|
20
|
-
- "
|
|
21
|
-
- "
|
|
20
|
+
- "Do you need a User entity?"
|
|
21
|
+
- "Are there any other entities needed?"
|
|
22
22
|
|
|
23
|
-
**2.
|
|
23
|
+
**2. Confirm relationships between entities** (one question at a time)
|
|
24
24
|
|
|
25
|
-
- "A
|
|
26
|
-
- "
|
|
25
|
+
- "Is A to B a 1:N or N:M relationship?"
|
|
26
|
+
- "Should chapters be managed as children of courses?"
|
|
27
27
|
|
|
28
|
-
**3.
|
|
28
|
+
**3. Decide whether to use parentId**
|
|
29
29
|
|
|
30
|
-
- "
|
|
31
|
-
- "
|
|
30
|
+
- "Can it exist without a parent?"
|
|
31
|
+
- "Should it be created and deleted together with the parent?"
|
|
32
32
|
|
|
33
|
-
**4.
|
|
33
|
+
**4. Final confirmation with the user**
|
|
34
34
|
|
|
35
|
-
-
|
|
36
|
-
-
|
|
35
|
+
- Finalize entity list
|
|
36
|
+
- Provide a relationship diagram or clear description
|
|
37
37
|
|
|
38
|
-
###
|
|
38
|
+
### Entity Design Done Checklist
|
|
39
39
|
|
|
40
|
-
- [ ]
|
|
41
|
-
- [ ]
|
|
42
|
-
- [ ] parentId
|
|
43
|
-
- [ ]
|
|
40
|
+
- [ ] All required entities identified
|
|
41
|
+
- [ ] Relationships between entities defined
|
|
42
|
+
- [ ] parentId usage decided
|
|
43
|
+
- [ ] User confirmation complete
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
**When done:** proceed to "Entity Creation Workflow"
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
**Full workflow reference:** `.claude/workflow/project_init.md`
|
|
48
48
|
|
|
49
49
|
---
|
|
50
50
|
|
|
51
|
-
## Entity
|
|
51
|
+
## Entity Creation Workflow
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
**Prerequisite: CRITICAL!**
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
**Always run `pnpm dev` in `/packages/api` before starting!**
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
58
|
cd packages/api
|
|
59
|
-
pnpm dev #
|
|
59
|
+
pnpm dev # keep this running during all work
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
>
|
|
62
|
+
> **Why**: In dev mode, the syncer detects changes to entity.json and auto-generates types.ts.
|
|
63
63
|
>
|
|
64
|
-
>
|
|
64
|
+
> Dev mode is required **for all entity creation**, not just auth entities.
|
|
65
65
|
|
|
66
|
-
### 1
|
|
66
|
+
### Step 1: Generate stub
|
|
67
67
|
|
|
68
|
-
**CRITICAL: EntityId
|
|
68
|
+
**CRITICAL: EntityId must always start with an uppercase letter!**
|
|
69
69
|
|
|
70
70
|
```bash
|
|
71
71
|
pnpm sonamu stub entity {EntityId}
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
**Correct examples:**
|
|
75
75
|
|
|
76
76
|
- `pnpm sonamu stub entity Course` (correct)
|
|
77
77
|
- `pnpm sonamu stub entity User` (correct)
|
|
78
78
|
- `pnpm sonamu stub entity ConsultationHistory` (correct)
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
**Incorrect examples:**
|
|
81
81
|
|
|
82
|
-
- `pnpm sonamu stub entity course` (wrong
|
|
83
|
-
- `pnpm gen stub entity Course` (wrong
|
|
82
|
+
- `pnpm sonamu stub entity course` (wrong — starts with lowercase)
|
|
83
|
+
- `pnpm gen stub entity Course` (wrong — incorrect command)
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
Generated file: `api/src/application/{entity}/{entity}.entity.json`
|
|
86
86
|
|
|
87
|
-
### 2
|
|
87
|
+
### Step 2: Edit the stub file
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
Add props, relations, and subsets to the generated entity.json file.
|
|
90
90
|
|
|
91
|
-
### 3
|
|
91
|
+
### Step 3: Validate and generate required files
|
|
92
92
|
|
|
93
|
-
**CRITICAL:
|
|
93
|
+
**CRITICAL: Always validate before running sync!**
|
|
94
94
|
|
|
95
|
-
**A. entity.json
|
|
95
|
+
**A. Validate entity.json** (see `entity-validation-checklist.md` PHASE 1)
|
|
96
96
|
|
|
97
|
-
- [ ]
|
|
98
|
-
- [ ]
|
|
99
|
-
- [ ] Boolean dbDefault
|
|
100
|
-
- [ ] OrderBy enum
|
|
101
|
-
- [ ] Enum dbDefault
|
|
97
|
+
- [ ] Does every index have a `type` field?
|
|
98
|
+
- [ ] Does the subset use `relation.id` format instead of directly referencing FK columns?
|
|
99
|
+
- [ ] Is the Boolean `dbDefault` a string (`"true"` / `"false"`)?
|
|
100
|
+
- [ ] Does the OrderBy enum contain only `id-desc`?
|
|
101
|
+
- [ ] Is the Enum `dbDefault` using escaped double quotes? (e.g., `"\"pending\""`)
|
|
102
102
|
|
|
103
|
-
**B. model.ts (
|
|
103
|
+
**B. model.ts (must be created manually)**
|
|
104
104
|
|
|
105
|
-
-
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
-
|
|
105
|
+
- Must be created manually
|
|
106
|
+
- Reference another entity's model.ts as a template
|
|
107
|
+
- Required methods: findById, findOne, findMany, save, del
|
|
108
|
+
- See `entity-validation-checklist.md` PHASE 2 for template
|
|
109
109
|
|
|
110
|
-
**C. types.ts (
|
|
110
|
+
**C. types.ts (auto-generated — wait for it)**
|
|
111
111
|
|
|
112
|
-
- **pnpm dev
|
|
113
|
-
-
|
|
114
|
-
-
|
|
115
|
-
1. pnpm dev
|
|
116
|
-
2.
|
|
112
|
+
- **If `pnpm dev` is running**, the syncer will auto-generate it within 2–3 seconds
|
|
113
|
+
- Check: `ls packages/api/src/application/{entity}/{entity}.types.ts`
|
|
114
|
+
- If not generated:
|
|
115
|
+
1. Verify `pnpm dev` is running
|
|
116
|
+
2. If still not generated, create manually (see `entity-validation-checklist.md` PHASE 2 for template)
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
**Done criteria:**
|
|
119
119
|
|
|
120
|
-
- [ ] entity.json
|
|
121
|
-
- [ ] model.ts
|
|
122
|
-
- [ ] types.ts
|
|
120
|
+
- [ ] entity.json validation passed
|
|
121
|
+
- [ ] model.ts exists
|
|
122
|
+
- [ ] types.ts exists (auto-generated or manually created)
|
|
123
123
|
|
|
124
|
-
### 4
|
|
124
|
+
### Step 4: Sync
|
|
125
125
|
|
|
126
126
|
```bash
|
|
127
127
|
pnpm sonamu sync
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
-
|
|
130
|
+
**Note:** Do not write entity JSON by hand. Always generate using the stub command, then edit.
|
|
131
131
|
|
|
132
|
-
### 5
|
|
132
|
+
### Step 5: Migration and Scaffolding
|
|
133
133
|
|
|
134
|
-
1.
|
|
135
|
-
2.
|
|
136
|
-
3.
|
|
134
|
+
1. Generate and apply migration
|
|
135
|
+
2. Run scaffolding
|
|
136
|
+
3. Test the build
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
**Full workflow:** see step-by-step checklist in `entity-validation-checklist.md`
|
|
139
139
|
|
|
140
|
-
### 6
|
|
140
|
+
### Step 6: Handle nullable fields in types.ts (required)
|
|
141
141
|
|
|
142
|
-
**CRITICAL:
|
|
142
|
+
**CRITICAL: Do this immediately after scaffolding, before writing tests!**
|
|
143
143
|
|
|
144
|
-
scaffolding
|
|
144
|
+
After scaffolding completes, nullable fields in the generated `*.types.ts` must be handled.
|
|
145
145
|
|
|
146
146
|
```typescript
|
|
147
|
-
//
|
|
147
|
+
// Auto-generated types.ts
|
|
148
148
|
export const FAQSaveParams = FAQBaseSchema.partial({
|
|
149
149
|
id: true,
|
|
150
150
|
created_at: true,
|
|
151
151
|
});
|
|
152
152
|
|
|
153
|
-
// CORRECT:
|
|
153
|
+
// CORRECT: update immediately — add nullable fields
|
|
154
154
|
export const FAQSaveParams = FAQBaseSchema.partial({
|
|
155
155
|
id: true,
|
|
156
156
|
created_at: true,
|
|
157
|
-
category: true, // nullable
|
|
158
|
-
order_num: true, // nullable
|
|
157
|
+
category: true, // nullable added
|
|
158
|
+
order_num: true, // nullable added
|
|
159
159
|
}).extend({
|
|
160
160
|
category: z.string().nullish(),
|
|
161
161
|
order_num: z.number().nullish(),
|
|
@@ -163,220 +163,219 @@ export const FAQSaveParams = FAQBaseSchema.partial({
|
|
|
163
163
|
});
|
|
164
164
|
```
|
|
165
165
|
|
|
166
|
-
|
|
166
|
+
**Detailed guide:** see "Actions to take immediately after entity creation" in `testing.md`
|
|
167
167
|
|
|
168
|
-
##
|
|
168
|
+
## Checklist for New Entity Creation
|
|
169
169
|
|
|
170
|
-
1. **id**: PascalCase (
|
|
171
|
-
2. **table**: snake_case
|
|
172
|
-
3. **title**:
|
|
173
|
-
4. **props
|
|
174
|
-
5. **enums
|
|
170
|
+
1. **id**: PascalCase (e.g., `User`, `BlogPost`)
|
|
171
|
+
2. **table**: snake_case plural (e.g., `users`, `blog_posts`) — can be omitted
|
|
172
|
+
3. **title**: display name
|
|
173
|
+
4. **Recommended props**: `id`, `created_at` (not enforced by schema but best practice)
|
|
174
|
+
5. **Recommended enums**: `{EntityId}OrderBy`, `{EntityId}SearchField` (not enforced but best practice)
|
|
175
175
|
|
|
176
176
|
## IMPORTANT: Analyze Requirements Before Creating Entity
|
|
177
177
|
|
|
178
|
-
**STOP!
|
|
178
|
+
**STOP! Ask questions one at a time before creating any entity.**
|
|
179
179
|
|
|
180
|
-
###
|
|
180
|
+
### Identify missing entities
|
|
181
181
|
|
|
182
|
-
|
|
182
|
+
Do not only create entities explicitly mentioned by the user. **Ask one at a time:**
|
|
183
183
|
|
|
184
|
-
- "
|
|
185
|
-
- "User
|
|
186
|
-
- "
|
|
184
|
+
- "Do you need a User entity?" → wait for response
|
|
185
|
+
- "Does the User have multiple roles?" → wait for response
|
|
186
|
+
- "Are there any other entities needed?" → wait for response
|
|
187
187
|
|
|
188
|
-
**User
|
|
188
|
+
**Note on User entity**: `id` is an auto-increment sequence (PK) and is not a login ID. When using better-auth, a separate `login_id` is not needed (managed by the auth table).
|
|
189
189
|
|
|
190
|
-
|
|
190
|
+
**Commonly missed entities**: Content (Comment, Like, Tag, Category), Commerce (Review, Cart, Payment), Reservation (Reservation, Schedule), Education (Enrollment, Progress)
|
|
191
191
|
|
|
192
|
-
###
|
|
192
|
+
### When multiple entities are requested — confirm relationships
|
|
193
193
|
|
|
194
|
-
2
|
|
194
|
+
When 2+ entities are requested, **ask about relationships one at a time before writing any code**:
|
|
195
195
|
|
|
196
|
-
- BelongsToOne, HasMany, ManyToMany, parentId
|
|
197
|
-
-
|
|
196
|
+
- Which relationship type: BelongsToOne, HasMany, ManyToMany, or parentId
|
|
197
|
+
- Whether it is a parent-child dependency (delete together) or independent
|
|
198
198
|
|
|
199
|
-
###
|
|
199
|
+
### Always confirm before designing
|
|
200
200
|
|
|
201
|
-
**1. Polymorphic Association** (`entity_type + entity_id`
|
|
201
|
+
**1. Polymorphic Association** (`entity_type + entity_id` pattern):
|
|
202
202
|
|
|
203
|
-
- string PK
|
|
204
|
-
-
|
|
203
|
+
- If there is a string PK entity (e.g., better-auth User) → use `string` type for `entity_id` uniformly
|
|
204
|
+
- Otherwise → `integer` is fine
|
|
205
205
|
|
|
206
|
-
**2.
|
|
206
|
+
**2. Domain term ↔ entity English ID mapping**: finalize with the user before writing any code (e.g., "위탁연구과제" → `ResearchContract`). Changing this later requires a full rename.
|
|
207
207
|
|
|
208
|
-
##
|
|
208
|
+
## Parent-Child Relationships (parentId)
|
|
209
209
|
|
|
210
|
-
### parentId
|
|
210
|
+
### What is parentId?
|
|
211
211
|
|
|
212
|
-
|
|
212
|
+
A top-level option used when a child entity is managed as a dependent of a parent entity.
|
|
213
213
|
|
|
214
|
-
- parentId
|
|
215
|
-
- parentId
|
|
214
|
+
- With parentId: the child has no independent CRUD — it is created, updated, and deleted through the parent
|
|
215
|
+
- Without parentId: an independent entity with its own CRUD
|
|
216
216
|
|
|
217
|
-
###
|
|
217
|
+
### When to use parentId
|
|
218
218
|
|
|
219
|
-
|
|
|
220
|
-
|
|
221
|
-
|
|
|
222
|
-
|
|
|
223
|
-
|
|
|
224
|
-
|
|
|
219
|
+
| Situation | parentId | Example |
|
|
220
|
+
|-----------|----------|---------|
|
|
221
|
+
| Cannot exist without a parent | Yes | OrderItem → Order |
|
|
222
|
+
| Created and deleted together with parent | Yes | Chapter → Course, Lesson → Chapter |
|
|
223
|
+
| Can be independently CRUD'd | No | Comment → Post |
|
|
224
|
+
| Can belong to multiple parents | No | Tag → Post (ManyToMany) |
|
|
225
225
|
|
|
226
|
-
### parentId
|
|
226
|
+
### parentId usage example
|
|
227
227
|
|
|
228
228
|
```json
|
|
229
229
|
{
|
|
230
230
|
"id": "OrderItem",
|
|
231
231
|
"table": "order_items",
|
|
232
|
-
"title": "
|
|
232
|
+
"title": "Order Item",
|
|
233
233
|
"parentId": "Order",
|
|
234
234
|
"props": [...]
|
|
235
235
|
}
|
|
236
236
|
```
|
|
237
237
|
|
|
238
|
-
### parentId
|
|
238
|
+
### Child entities with parentId do not generate types.ts
|
|
239
239
|
|
|
240
|
-
parentId
|
|
240
|
+
Child entities with parentId set (e.g., Chapter, Lesson) do not get their own `types.ts` file. This is expected behavior — child entities are managed through the parent. If you need independent CRUD and types.ts, do not use parentId.
|
|
241
241
|
|
|
242
|
-
###
|
|
242
|
+
### Folder location for parentId child entities
|
|
243
243
|
|
|
244
|
-
parentId
|
|
244
|
+
Child entities with parentId must be placed **in the same folder as the root parent entity**.
|
|
245
245
|
|
|
246
|
-
|
|
|
247
|
-
|
|
248
|
-
| `course/course.entity.json`
|
|
249
|
-
| `course/chapter.entity.json` | parentId: "Course" →
|
|
250
|
-
| `course/lesson.entity.json`
|
|
251
|
-
| `course/course.types.ts`
|
|
246
|
+
| Structure | Description |
|
|
247
|
+
|-----------|-------------|
|
|
248
|
+
| `course/course.entity.json` | Root entity |
|
|
249
|
+
| `course/chapter.entity.json` | parentId: "Course" → same folder |
|
|
250
|
+
| `course/lesson.entity.json` | parentId: "Chapter" → same folder (based on root) |
|
|
251
|
+
| `course/course.types.ts` | types.ts generated only for root |
|
|
252
252
|
|
|
253
|
-
|
|
253
|
+
**Note:** Do not create child entities in a separate folder (e.g., `chapter/chapter.entity.json`).
|
|
254
254
|
|
|
255
255
|
### IMPORTANT: When Uncertain - Ask User (Never Guess)
|
|
256
256
|
|
|
257
|
-
|
|
257
|
+
**Do not guess — ask.** In situations like the following, ask the user directly:
|
|
258
258
|
|
|
259
|
-
- "
|
|
260
|
-
- "
|
|
259
|
+
- "Should chapters be managed as children of courses, or created as an independent entity?"
|
|
260
|
+
- "Should order items be saved together with the order, or managed separately?"
|
|
261
261
|
|
|
262
|
-
|
|
262
|
+
**When in doubt, ask. One question is better than a wrong design.**
|
|
263
263
|
|
|
264
|
-
|
|
264
|
+
**Helpful questions to ask the user:**
|
|
265
265
|
|
|
266
|
-
- "
|
|
267
|
-
- "
|
|
268
|
-
- "
|
|
266
|
+
- "Will this data ever need to be queried or updated independently without a parent?"
|
|
267
|
+
- "Should this data be deleted when the parent is deleted?"
|
|
268
|
+
- "Does the admin UI need a separate list page for this?"
|
|
269
269
|
|
|
270
|
-
##
|
|
270
|
+
## Minimal Template
|
|
271
271
|
|
|
272
272
|
```json
|
|
273
273
|
{
|
|
274
274
|
"id": "Product",
|
|
275
275
|
"table": "products",
|
|
276
|
-
"title": "
|
|
276
|
+
"title": "Product",
|
|
277
277
|
"props": [
|
|
278
278
|
{ "name": "id", "type": "integer", "desc": "ID" },
|
|
279
279
|
{
|
|
280
280
|
"name": "created_at",
|
|
281
281
|
"type": "date",
|
|
282
282
|
"dbDefault": "CURRENT_TIMESTAMP",
|
|
283
|
-
"desc": "
|
|
283
|
+
"desc": "Created at"
|
|
284
284
|
},
|
|
285
|
-
{ "name": "name", "type": "string", "length": 255, "desc": "
|
|
285
|
+
{ "name": "name", "type": "string", "length": 255, "desc": "Product name" }
|
|
286
286
|
],
|
|
287
287
|
"indexes": [],
|
|
288
288
|
"subsets": { "A": ["id", "name", "created_at"] },
|
|
289
289
|
"enums": {
|
|
290
|
-
"ProductOrderBy": { "id-desc": "
|
|
291
|
-
"ProductSearchField": { "id": "ID", "name": "
|
|
290
|
+
"ProductOrderBy": { "id-desc": "Newest" },
|
|
291
|
+
"ProductSearchField": { "id": "ID", "name": "Name" }
|
|
292
292
|
}
|
|
293
293
|
}
|
|
294
294
|
```
|
|
295
295
|
|
|
296
|
-
##
|
|
296
|
+
## Situation-Specific Guides
|
|
297
297
|
|
|
298
|
-
###
|
|
298
|
+
### Adding a string field
|
|
299
299
|
|
|
300
300
|
```json
|
|
301
|
-
{ "name": "title", "type": "string", "length": 255, "desc": "
|
|
301
|
+
{ "name": "title", "type": "string", "length": 255, "desc": "Title" }
|
|
302
302
|
```
|
|
303
303
|
|
|
304
|
-
- `length`
|
|
304
|
+
- Omitting `length` → stored as `text` type (for long text)
|
|
305
305
|
|
|
306
|
-
###
|
|
306
|
+
### Adding an enum field
|
|
307
307
|
|
|
308
308
|
```json
|
|
309
|
-
// 1. props
|
|
310
|
-
{ "name": "status", "type": "enum", "id": "ProductStatus", "desc": "
|
|
309
|
+
// 1. Add to props
|
|
310
|
+
{ "name": "status", "type": "enum", "id": "ProductStatus", "desc": "Status" }
|
|
311
311
|
|
|
312
|
-
// 2. enums
|
|
313
|
-
"ProductStatus": { "draft": "
|
|
312
|
+
// 2. Define in enums (MUST — missing this causes errors)
|
|
313
|
+
"ProductStatus": { "draft": "Draft", "published": "Published", "archived": "Archived" }
|
|
314
314
|
```
|
|
315
315
|
|
|
316
|
-
### IMPORTANT:
|
|
316
|
+
### IMPORTANT: Always use enum for fixed-value fields
|
|
317
317
|
|
|
318
|
-
|
|
318
|
+
Defining a field with a fixed set of choices as `string` breaks DB integrity.
|
|
319
319
|
|
|
320
|
-
|
|
320
|
+
**Rule: "Can this value be freely entered from outside the code?"** No → enum, Yes → string
|
|
321
321
|
|
|
322
|
-
**
|
|
322
|
+
**Enum candidates**: strings that look like `faker.helpers.arrayElement([...])`, fields described as "one of the following / type / category", select boxes / radio buttons
|
|
323
323
|
|
|
324
324
|
```json
|
|
325
|
-
// WRONG: string
|
|
326
|
-
{ "name": "budget_item", "type": "string", "desc": "
|
|
327
|
-
// CORRECT: enum
|
|
328
|
-
{ "name": "budget_item", "type": "enum", "id": "BudgetItem", "desc": "
|
|
325
|
+
// WRONG: defined as string
|
|
326
|
+
{ "name": "budget_item", "type": "string", "desc": "Budget item" }
|
|
327
|
+
// CORRECT: defined as enum
|
|
328
|
+
{ "name": "budget_item", "type": "enum", "id": "BudgetItem", "desc": "Budget item" }
|
|
329
329
|
```
|
|
330
330
|
|
|
331
|
-
###
|
|
331
|
+
### Adding a nullable field
|
|
332
332
|
|
|
333
333
|
```json
|
|
334
|
-
{ "name": "deleted_at", "type": "date", "nullable": true, "desc": "
|
|
334
|
+
{ "name": "deleted_at", "type": "date", "nullable": true, "desc": "Deleted at" }
|
|
335
335
|
```
|
|
336
336
|
|
|
337
|
-
**CRITICAL: nullable
|
|
337
|
+
**CRITICAL: Importance of the nullable attribute**
|
|
338
338
|
|
|
339
|
-
`nullable: true
|
|
339
|
+
A field without `nullable: true` is treated as **required**.
|
|
340
340
|
|
|
341
|
-
Sonamu
|
|
342
|
-
업데이트 시에도 **모든 필수 필드**를 포함해야 합니다.
|
|
341
|
+
Sonamu's `ubUpsert` uses PostgreSQL `ON CONFLICT ... DO UPDATE`, so **all required fields** must be included even on updates.
|
|
343
342
|
|
|
344
343
|
```json
|
|
345
|
-
//
|
|
344
|
+
// example
|
|
346
345
|
{
|
|
347
346
|
"props": [
|
|
348
|
-
{ "name": "title", "type": "string" },
|
|
349
|
-
{ "name": "content", "type": "string" },
|
|
350
|
-
{ "name": "category", "type": "string", "nullable": true } //
|
|
347
|
+
{ "name": "title", "type": "string" }, // required (no nullable)
|
|
348
|
+
{ "name": "content", "type": "string" }, // required (no nullable)
|
|
349
|
+
{ "name": "category", "type": "string", "nullable": true } // optional
|
|
351
350
|
]
|
|
352
351
|
}
|
|
353
352
|
```
|
|
354
353
|
|
|
355
|
-
|
|
354
|
+
**Rules**:
|
|
356
355
|
|
|
357
|
-
-
|
|
358
|
-
-
|
|
359
|
-
-
|
|
356
|
+
- Do not add `nullable: true` to fields that are not optional
|
|
357
|
+
- Always specify `nullable: true` for optional fields
|
|
358
|
+
- Required fields must always have a value in tests and API calls
|
|
360
359
|
|
|
361
|
-
|
|
360
|
+
**Details:** see "Quick Start" in `testing.md` and "CRITICAL: Required fields must be included" in `upsert.md`
|
|
362
361
|
|
|
363
|
-
###
|
|
362
|
+
### Adding a JSON field
|
|
364
363
|
|
|
365
364
|
```json
|
|
366
365
|
{
|
|
367
366
|
"name": "metadata",
|
|
368
367
|
"type": "json",
|
|
369
368
|
"id": "ProductMetadata",
|
|
370
|
-
"desc": "
|
|
369
|
+
"desc": "Metadata"
|
|
371
370
|
}
|
|
372
371
|
```
|
|
373
372
|
|
|
374
|
-
- `id`
|
|
375
|
-
-
|
|
373
|
+
- `id` is required (used as the type name)
|
|
374
|
+
- A separate TypeScript type definition is needed
|
|
376
375
|
|
|
377
|
-
###
|
|
376
|
+
### Adding a searchText field (for pg_trgm Fuzzy Search)
|
|
378
377
|
|
|
379
|
-
|
|
378
|
+
A dedicated prop type that consolidates multiple columns into a single generated column. Used with a GIN index.
|
|
380
379
|
|
|
381
380
|
```json
|
|
382
381
|
{
|
|
@@ -405,19 +404,19 @@ Sonamu의 `ubUpsert`는 PostgreSQL의 `ON CONFLICT ... DO UPDATE`를 사용하
|
|
|
405
404
|
}
|
|
406
405
|
```
|
|
407
406
|
|
|
408
|
-
source column
|
|
407
|
+
SQL expressions per source column type:
|
|
409
408
|
|
|
410
|
-
| source
|
|
411
|
-
|
|
409
|
+
| source type | caseInsensitive: true | caseInsensitive: false (default) |
|
|
410
|
+
|------------|----------------------|----------------------------------|
|
|
412
411
|
| `string` | `lower(COALESCE(col, ''))` | `COALESCE(col, '')` |
|
|
413
412
|
| `string[]` | `sonamu_text_array_agg(col)` | `sonamu_text_array_agg(col, false)` |
|
|
414
413
|
| `json` (z.array(z.string())) | `sonamu_jsonb_array_agg(col)` | `sonamu_jsonb_array_agg(col, false)` |
|
|
415
414
|
|
|
416
|
-
- `string[]`
|
|
417
|
-
- `searchText`
|
|
418
|
-
-
|
|
415
|
+
- If a `string[]` or `json(string[])` source is present, helper function DDL is automatically inserted in the migration
|
|
416
|
+
- `searchText` columns are generated columns and are excluded from SaveParams — INSERT/UPDATE is not allowed
|
|
417
|
+
- For query usage: see the "pg_trgm Fuzzy Search" section in `puri.md`
|
|
419
418
|
|
|
420
|
-
###
|
|
419
|
+
### Adding a unique constraint
|
|
421
420
|
|
|
422
421
|
```json
|
|
423
422
|
{
|
|
@@ -427,7 +426,7 @@ source column 타입별 SQL 표현:
|
|
|
427
426
|
}
|
|
428
427
|
```
|
|
429
428
|
|
|
430
|
-
###
|
|
429
|
+
### Composite unique constraint
|
|
431
430
|
|
|
432
431
|
```json
|
|
433
432
|
{
|
|
@@ -437,19 +436,19 @@ source column 타입별 SQL 표현:
|
|
|
437
436
|
}
|
|
438
437
|
```
|
|
439
438
|
|
|
440
|
-
### IMPORTANT:
|
|
439
|
+
### IMPORTANT: Use the actual DB column name in indexes
|
|
441
440
|
|
|
442
|
-
**
|
|
441
|
+
**The way FK columns are referenced differs between indexes and subsets. Do not confuse them.**
|
|
443
442
|
|
|
444
|
-
|
|
|
445
|
-
|
|
446
|
-
| `indexes` |
|
|
443
|
+
| Location | Format | Example |
|
|
444
|
+
|----------|--------|---------|
|
|
445
|
+
| `indexes` | Actual DB column name | `role_id`, `user_id`, `department_id` |
|
|
447
446
|
| `subsets` | FieldExpr (relation.field) | `role.id`, `user.id`, `department.id` |
|
|
448
447
|
|
|
449
448
|
**DO NOT:**
|
|
450
449
|
|
|
451
450
|
```json
|
|
452
|
-
//
|
|
451
|
+
// Using FieldExpr in indexes → error
|
|
453
452
|
"indexes": [
|
|
454
453
|
{ "name": "ix_role", "type": "index", "columns": [{ "name": "role.id" }] }
|
|
455
454
|
]
|
|
@@ -458,185 +457,185 @@ source column 타입별 SQL 표현:
|
|
|
458
457
|
**DO:**
|
|
459
458
|
|
|
460
459
|
```json
|
|
461
|
-
// indexes
|
|
460
|
+
// indexes use actual DB column names
|
|
462
461
|
"indexes": [
|
|
463
462
|
{ "name": "ix_role_id", "type": "index", "columns": [{ "name": "role_id" }] }
|
|
464
463
|
]
|
|
465
464
|
|
|
466
|
-
// subsets
|
|
465
|
+
// subsets use FieldExpr
|
|
467
466
|
"subsets": {
|
|
468
467
|
"A": ["id", "role.id", "role.name"]
|
|
469
468
|
}
|
|
470
469
|
```
|
|
471
470
|
|
|
472
|
-
### IMPORTANT:
|
|
471
|
+
### IMPORTANT: Unique constraints based on business rules
|
|
473
472
|
|
|
474
|
-
|
|
473
|
+
Not a technical decision — ask **"What if the same combination is inserted twice?"** → if it should error, use unique; if it should be allowed, use index only.
|
|
475
474
|
|
|
476
|
-
|
|
475
|
+
**Patterns that need composite unique**: per-year settings (`type, dept_id, year`), user-role mappings, per-year budgets (`project_id, year, budget_item`), likes/bookmarks (`user_id, entity_id`)
|
|
477
476
|
|
|
478
|
-
##
|
|
477
|
+
## Common Mistakes
|
|
479
478
|
|
|
480
|
-
|
|
|
481
|
-
|
|
482
|
-
| `id` prop
|
|
483
|
-
| `created_at` prop
|
|
484
|
-
| `OrderBy` enum
|
|
485
|
-
| `SearchField` enum
|
|
486
|
-
| enum prop
|
|
487
|
-
|
|
|
488
|
-
| `"type": "text"`
|
|
489
|
-
| `OrderBy` enum
|
|
490
|
-
|
|
|
491
|
-
|
|
|
492
|
-
|
|
|
493
|
-
|
|
|
479
|
+
| Mistake | Fix |
|
|
480
|
+
|---------|-----|
|
|
481
|
+
| Missing `id` prop | Recommended to add (needed by most Model logic) |
|
|
482
|
+
| Missing `created_at` prop | Recommended to add with `dbDefault: "CURRENT_TIMESTAMP"` |
|
|
483
|
+
| Missing `OrderBy` enum | Add `{EntityId}OrderBy` (needed for findMany sorting) |
|
|
484
|
+
| Missing `SearchField` enum | Add `{EntityId}SearchField` (needed for search) |
|
|
485
|
+
| enum prop `id` not defined in enums | Add definition to the enums section |
|
|
486
|
+
| Missing `id` on json prop | Add the `id` field |
|
|
487
|
+
| Using `"type": "text"` directly | `text` is invalid. Use `"type": "string"` without a length |
|
|
488
|
+
| Adding multiple values to `OrderBy` enum | **Default is `id-desc` only** (see below) |
|
|
489
|
+
| Defining fixed-choice fields as `string` | Convert to enum (check for fields with arrayElement-style fixtureGenerator) |
|
|
490
|
+
| Yearly/mapping tables without unique constraints | Add composite unique based on business rules |
|
|
491
|
+
| Using `number` type for integer fields | Use `integer` (use `numeric` only when decimal precision is needed) |
|
|
492
|
+
| Using `role.id` format in indexes | indexes use actual DB column name (`role_id`); only subsets use FieldExpr (`role.id`) |
|
|
494
493
|
|
|
495
|
-
## Entity
|
|
494
|
+
## Resolving Entity Schema Validation Errors
|
|
496
495
|
|
|
497
|
-
**→ `entity-validation-checklist.md` PHASE 1
|
|
496
|
+
**→ See `entity-validation-checklist.md` PHASE 1** (missing index type, Subset FieldExpr, duplicate columns, Boolean dbDefault, etc.)
|
|
498
497
|
|
|
499
|
-
|
|
498
|
+
**Quick checklist:**
|
|
500
499
|
|
|
501
|
-
- [ ]
|
|
502
|
-
- [ ]
|
|
503
|
-
- [ ] BelongsToOne relation
|
|
504
|
-
- [ ] Boolean dbDefault
|
|
505
|
-
- [ ]
|
|
506
|
-
- [ ]
|
|
500
|
+
- [ ] Does every index have a `type` field? (`"index"` | `"unique"` | `"hnsw"` | `"ivfflat"`)
|
|
501
|
+
- [ ] Does the subset reference FK using `relation.id` format? (`user_id` ✗ → `user.id` ✓)
|
|
502
|
+
- [ ] No duplicate definition of BelongsToOne relation and FK column?
|
|
503
|
+
- [ ] Is Boolean `dbDefault` a string (`"true"` / `"false"`)? (0, 1 ✗)
|
|
504
|
+
- [ ] Are all fields included in Subset A?
|
|
505
|
+
- [ ] Do index columns use actual DB column names (`role_id`)? (FieldExpr `role.id` ✗)
|
|
507
506
|
|
|
508
507
|
## IMPORTANT: OrderBy Enum Generation Rule
|
|
509
508
|
|
|
510
|
-
**IMPORTANT:
|
|
509
|
+
**IMPORTANT: It is strongly recommended to use only `id-desc` during initial scaffolding.**
|
|
511
510
|
|
|
512
511
|
```json
|
|
513
|
-
// RECOMMENDED
|
|
514
|
-
"ProductOrderBy": { "id-desc": "
|
|
512
|
+
// RECOMMENDED — for initial scaffolding
|
|
513
|
+
"ProductOrderBy": { "id-desc": "Newest" }
|
|
515
514
|
|
|
516
|
-
// AVOID
|
|
517
|
-
"ProductOrderBy": { "id-desc": "
|
|
515
|
+
// AVOID — do not add these before scaffolding
|
|
516
|
+
"ProductOrderBy": { "id-desc": "Newest", "name-asc": "Name (A-Z)", "created_at-desc": "Newest first" }
|
|
518
517
|
```
|
|
519
518
|
|
|
520
|
-
###
|
|
519
|
+
### Why only id-desc?
|
|
521
520
|
|
|
522
|
-
|
|
521
|
+
The model code generated by scaffolding handles only `id-desc` automatically. If the OrderBy enum has other values:
|
|
523
522
|
|
|
524
|
-
1. Scaffolding
|
|
525
|
-
2.
|
|
526
|
-
3.
|
|
523
|
+
1. Scaffolding succeeds, but a type error occurs in the model's `exhaustive()` function
|
|
524
|
+
2. The developer must manually add cases to the model
|
|
525
|
+
3. If this is missed, a runtime error may occur
|
|
527
526
|
|
|
528
|
-
|
|
527
|
+
**This is not a technical constraint — it is scaffolding best practice.** Add complex OrderBy values after scaffolding is complete.
|
|
529
528
|
|
|
530
|
-
###
|
|
529
|
+
### When additional sort options are needed
|
|
531
530
|
|
|
532
|
-
|
|
531
|
+
When sort options are required later:
|
|
533
532
|
|
|
534
|
-
1.
|
|
535
|
-
2.
|
|
533
|
+
1. Add values to the OrderBy enum in entity.json
|
|
534
|
+
2. Add the corresponding cases to the orderBy branch in the model
|
|
536
535
|
|
|
537
536
|
```typescript
|
|
538
|
-
// model.ts
|
|
537
|
+
// model.ts — adding orderBy cases
|
|
539
538
|
if (params.orderBy === "id-desc") {
|
|
540
539
|
qb.orderBy("products.id", "desc");
|
|
541
540
|
} else if (params.orderBy === "name-asc") {
|
|
542
|
-
//
|
|
541
|
+
// added
|
|
543
542
|
qb.orderBy("products.name", "asc");
|
|
544
543
|
} else {
|
|
545
544
|
exhaustive(params.orderBy);
|
|
546
545
|
}
|
|
547
546
|
```
|
|
548
547
|
|
|
549
|
-
|
|
548
|
+
**Rule**: start with `id-desc` only → add others as needed after scaffolding
|
|
550
549
|
|
|
551
|
-
## IMPORTANT: integer vs number
|
|
550
|
+
## IMPORTANT: integer vs number Type Selection
|
|
552
551
|
|
|
553
|
-
**CRITICAL:
|
|
552
|
+
**CRITICAL: Always follow the criteria below when creating numeric fields. Wrong type choice causes unnecessary ALTER migrations.**
|
|
554
553
|
|
|
555
|
-
PostgreSQL
|
|
554
|
+
PostgreSQL mapping:
|
|
556
555
|
|
|
557
|
-
- `integer` → DB `integer` (
|
|
558
|
-
- `number` → DB `numeric(p,s)` (
|
|
556
|
+
- `integer` → DB `integer` (whole numbers)
|
|
557
|
+
- `number` → DB `numeric(p,s)` (precise decimal numbers)
|
|
559
558
|
|
|
560
|
-
|
|
|
561
|
-
|
|
562
|
-
| PK, FK,
|
|
563
|
-
|
|
|
559
|
+
| Use case | Entity type | Example |
|
|
560
|
+
|----------|-------------|---------|
|
|
561
|
+
| PK, FK, count, order, quantity | `integer` | id, user_id, order_num, quantity |
|
|
562
|
+
| Amount, ratio, values requiring decimal | `number` (+ `precision`, `scale`) | price, rate, weight, score |
|
|
564
563
|
|
|
565
564
|
**DO NOT:**
|
|
566
565
|
|
|
567
566
|
```json
|
|
568
|
-
{ "name": "order_num", "type": "number", "desc": "
|
|
569
|
-
{ "name": "quantity", "type": "number", "desc": "
|
|
567
|
+
{ "name": "order_num", "type": "number", "desc": "Sort order" }
|
|
568
|
+
{ "name": "quantity", "type": "number", "desc": "Quantity" }
|
|
570
569
|
```
|
|
571
570
|
|
|
572
571
|
**DO:**
|
|
573
572
|
|
|
574
573
|
```json
|
|
575
|
-
{ "name": "order_num", "type": "integer", "desc": "
|
|
576
|
-
{ "name": "quantity", "type": "integer", "desc": "
|
|
577
|
-
{ "name": "price", "type": "number", "precision": 12, "scale": 2, "desc": "
|
|
578
|
-
{ "name": "rate", "type": "number", "precision": 5, "scale": 2, "desc": "
|
|
574
|
+
{ "name": "order_num", "type": "integer", "desc": "Sort order" }
|
|
575
|
+
{ "name": "quantity", "type": "integer", "desc": "Quantity" }
|
|
576
|
+
{ "name": "price", "type": "number", "precision": 12, "scale": 2, "desc": "Price" }
|
|
577
|
+
{ "name": "rate", "type": "number", "precision": 5, "scale": 2, "desc": "Rate" }
|
|
579
578
|
```
|
|
580
579
|
|
|
581
|
-
|
|
580
|
+
**Decision rule: "Does this value ever need a decimal point?"**
|
|
582
581
|
|
|
583
582
|
- No → `integer`
|
|
584
|
-
- Yes → `number` (
|
|
585
|
-
|
|
586
|
-
##
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
|
591
|
-
|
|
592
|
-
| `name` | string |
|
|
593
|
-
| `desc` | string |
|
|
594
|
-
| `nullable` | boolean | NULL
|
|
595
|
-
| `toFilter` | true | sonamuFilter
|
|
596
|
-
| `cone` | Cone | LLM
|
|
597
|
-
|
|
598
|
-
##
|
|
599
|
-
|
|
600
|
-
|
|
|
601
|
-
|
|
602
|
-
| `string`
|
|
603
|
-
| `integer`
|
|
604
|
-
| `bigInteger` |
|
|
605
|
-
| `number`
|
|
606
|
-
| `numeric`
|
|
607
|
-
| `enum`
|
|
608
|
-
| `json`
|
|
609
|
-
| `date`
|
|
610
|
-
| `boolean`
|
|
611
|
-
| `virtual`
|
|
612
|
-
| `vector`
|
|
613
|
-
| `tsvector`
|
|
583
|
+
- Yes → `number` (always specify `precision` and `scale`)
|
|
584
|
+
|
|
585
|
+
## Common Options (CommonProp)
|
|
586
|
+
|
|
587
|
+
Options applicable to all prop types:
|
|
588
|
+
|
|
589
|
+
| Option | Type | Description |
|
|
590
|
+
|--------|------|-------------|
|
|
591
|
+
| `name` | string | Field name (required) |
|
|
592
|
+
| `desc` | string | Field description |
|
|
593
|
+
| `nullable` | boolean | Whether NULL is allowed (default: false) |
|
|
594
|
+
| `toFilter` | true | Register as a sonamuFilter filtering target. See model.md |
|
|
595
|
+
| `cone` | Cone | LLM-based fixture generation metadata. See cone.md |
|
|
596
|
+
|
|
597
|
+
## Required Options by Type
|
|
598
|
+
|
|
599
|
+
| Type | Required | Optional |
|
|
600
|
+
|------|----------|---------|
|
|
601
|
+
| `string` | — | `length` (text if omitted), `zodFormat` (email, uuid, etc.) |
|
|
602
|
+
| `integer` | — | — |
|
|
603
|
+
| `bigInteger` | — | — |
|
|
604
|
+
| `number` | — | `precision`, `scale`, `numberType` (real/double precision/numeric) |
|
|
605
|
+
| `numeric` | — | `precision`, `scale` |
|
|
606
|
+
| `enum` | `id` | `nullable`, `dbDefault`, `length` |
|
|
607
|
+
| `json` | `id` | `dbDefault: "{}"` |
|
|
608
|
+
| `date` | — | `dbDefault`, `precision` |
|
|
609
|
+
| `boolean` | — | `dbDefault: "false"` |
|
|
610
|
+
| `virtual` | `id` | `virtualType` (query/code, default: code) |
|
|
611
|
+
| `vector` | `dimensions` | — |
|
|
612
|
+
| `tsvector` | — | — |
|
|
614
613
|
|
|
615
614
|
## IMPORTANT: ENUM Type dbDefault Setting
|
|
616
615
|
|
|
617
|
-
ENUM
|
|
616
|
+
When setting a default value on an ENUM field, the value must be wrapped in **escaped double quotes**.
|
|
618
617
|
|
|
619
618
|
### DO NOT - Incorrect Examples
|
|
620
619
|
|
|
621
620
|
```json
|
|
622
|
-
// Incorrect:
|
|
621
|
+
// Incorrect: no quotes — interpreted as a column reference in SQL, causes error
|
|
623
622
|
{ "name": "status", "type": "enum", "id": "ApprovalStatus", "dbDefault": "pending" }
|
|
624
|
-
//
|
|
623
|
+
// Error: cannot use column reference in DEFAULT expression
|
|
625
624
|
|
|
626
|
-
// Incorrect:
|
|
625
|
+
// Incorrect: single quotes — causes Biome format error
|
|
627
626
|
{ "name": "status", "type": "enum", "id": "ApprovalStatus", "dbDefault": "'pending'" }
|
|
628
627
|
```
|
|
629
628
|
|
|
630
629
|
### DO - Correct Example
|
|
631
630
|
|
|
632
631
|
```json
|
|
633
|
-
// Correct:
|
|
632
|
+
// Correct: escaped double quotes
|
|
634
633
|
{
|
|
635
634
|
"name": "status",
|
|
636
635
|
"type": "enum",
|
|
637
636
|
"id": "ApprovalStatus",
|
|
638
637
|
"dbDefault": "\"pending\"",
|
|
639
|
-
"desc": "
|
|
638
|
+
"desc": "Approval status"
|
|
640
639
|
}
|
|
641
640
|
```
|
|
642
641
|
|