sonamu 0.10.7 → 0.10.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/api/decorators.d.ts.map +1 -1
  2. package/dist/api/decorators.js +5 -12
  3. package/dist/bin/cli.js +10 -253
  4. package/dist/database/base-model.d.ts +1 -0
  5. package/dist/database/base-model.d.ts.map +1 -1
  6. package/dist/database/base-model.js +14 -8
  7. package/dist/database/db.d.ts +2 -1
  8. package/dist/database/db.d.ts.map +1 -1
  9. package/dist/database/db.js +13 -3
  10. package/dist/database/puri-wrapper.d.ts.map +1 -1
  11. package/dist/database/puri-wrapper.js +4 -11
  12. package/dist/database/transaction-context.d.ts +8 -3
  13. package/dist/database/transaction-context.d.ts.map +1 -1
  14. package/dist/database/transaction-context.js +12 -7
  15. package/dist/migration/code-generation.js +2 -2
  16. package/dist/naite/naite-reporter.d.ts +8 -3
  17. package/dist/naite/naite-reporter.d.ts.map +1 -1
  18. package/dist/naite/naite-reporter.js +9 -4
  19. package/dist/ui-web/assets/{index-D0MHYbxl.js → index-CJf8uJYf.js} +47 -41
  20. package/dist/ui-web/assets/index-GMMIVGja.css +1 -0
  21. package/dist/ui-web/index.html +2 -2
  22. package/package.json +3 -3
  23. package/src/api/decorators.ts +11 -22
  24. package/src/bin/cli.ts +12 -375
  25. package/src/database/__tests__/transaction-scope.test.ts +227 -0
  26. package/src/database/base-model.ts +31 -6
  27. package/src/database/db.ts +17 -2
  28. package/src/database/puri-wrapper.ts +5 -22
  29. package/src/database/transaction-context.ts +14 -6
  30. package/src/migration/code-generation.ts +1 -1
  31. package/src/naite/naite-reporter.ts +8 -3
  32. package/dist/ui-web/assets/index-Dx_JX4aQ.css +0 -1
  33. package/src/skills/AGENTS.md +0 -108
  34. package/src/skills/sonamu/SKILL.md +0 -75
  35. package/src/skills/sonamu-ai-agents/SKILL.md +0 -205
  36. package/src/skills/sonamu-api/SKILL.md +0 -480
  37. package/src/skills/sonamu-auth/SKILL.md +0 -327
  38. package/src/skills/sonamu-auth/references/plugins.md +0 -310
  39. package/src/skills/sonamu-auth/references/user-id-migration-followups.md +0 -192
  40. package/src/skills/sonamu-auth/references/user-id-migration.md +0 -455
  41. package/src/skills/sonamu-config/SKILL.md +0 -203
  42. package/src/skills/sonamu-config/references/database.md +0 -452
  43. package/src/skills/sonamu-config/references/environments.md +0 -178
  44. package/src/skills/sonamu-config/references/server-options.md +0 -400
  45. package/src/skills/sonamu-entity/SKILL.md +0 -180
  46. package/src/skills/sonamu-entity/references/creation-workflow.md +0 -581
  47. package/src/skills/sonamu-entity/references/design-guides.md +0 -243
  48. package/src/skills/sonamu-entity/references/field-types.md +0 -170
  49. package/src/skills/sonamu-entity/references/relations-detail.md +0 -245
  50. package/src/skills/sonamu-entity/references/relations.md +0 -463
  51. package/src/skills/sonamu-entity/references/subset.md +0 -156
  52. package/src/skills/sonamu-fixture/SKILL.md +0 -180
  53. package/src/skills/sonamu-fixture/references/cli-usage.md +0 -439
  54. package/src/skills/sonamu-fixture/references/cone.md +0 -298
  55. package/src/skills/sonamu-frontend/SKILL.md +0 -142
  56. package/src/skills/sonamu-frontend/references/components.md +0 -323
  57. package/src/skills/sonamu-frontend/references/examples.md +0 -64
  58. package/src/skills/sonamu-frontend/references/hooks.md +0 -273
  59. package/src/skills/sonamu-frontend/references/runtime.md +0 -165
  60. package/src/skills/sonamu-frontend/references/scaffolding.md +0 -439
  61. package/src/skills/sonamu-i18n/SKILL.md +0 -287
  62. package/src/skills/sonamu-migration/SKILL.md +0 -316
  63. package/src/skills/sonamu-naite/SKILL.md +0 -266
  64. package/src/skills/sonamu-query/SKILL.md +0 -48
  65. package/src/skills/sonamu-query/references/model-patterns.md +0 -390
  66. package/src/skills/sonamu-query/references/model.md +0 -366
  67. package/src/skills/sonamu-query/references/puri.md +0 -413
  68. package/src/skills/sonamu-query/references/search.md +0 -238
  69. package/src/skills/sonamu-query/references/upsert.md +0 -324
  70. package/src/skills/sonamu-tasks/SKILL.md +0 -236
  71. package/src/skills/sonamu-testing/SKILL.md +0 -251
  72. package/src/skills/sonamu-testing/references/devrunner.md +0 -405
  73. package/src/skills/sonamu-testing/references/helpers.md +0 -185
  74. package/src/skills/sonamu-testing/references/patterns.md +0 -263
  75. package/src/skills/sonamu-testing/references/pitfalls.md +0 -588
  76. package/src/skills/sonamu-testing/references/quick-start.md +0 -285
  77. package/src/skills/sonamu-testing/references/type-safety.md +0 -172
  78. package/src/skills/sonamu-testing/references/writing-plan.md +0 -375
  79. package/src/skills/sonamu-vector/SKILL.md +0 -222
@@ -1,287 +0,0 @@
1
- ---
2
- name: sonamu-i18n
3
- description: Adds and resolves translation keys through Sonamu's SD function. Use when registering labels for a new entity or enum, adding a language, or a UI string renders as a raw key. Covers dictionary configuration, SD(), and the i18n helper functions.
4
- ---
5
-
6
- # i18n (Internationalization)
7
-
8
- ## Configuration
9
-
10
- `sonamu.config.ts`:
11
-
12
- ```typescript
13
- export default defineConfig({
14
- i18n: {
15
- defaultLocale: "ko",
16
- supportedLocales: ["ko", "en"],
17
- },
18
- // ...
19
- });
20
- ```
21
-
22
- ## File Structure
23
-
24
- ```
25
- packages/api/src/i18n/
26
- ├── ko.ts # Default locale (defaultLocale)
27
- ├── en.ts # Additional locale
28
- └── sd.generated.ts # Auto-generated (pnpm sync)
29
- ```
30
-
31
- ## Dictionary Files
32
-
33
- ### Default locale (ko.ts)
34
-
35
- ```typescript
36
- import { createFormat, josa } from "sonamu/dict";
37
-
38
- const format = createFormat("ko");
39
-
40
- export default {
41
- "common.save": "저장",
42
- "common.cancel": "취소",
43
- "validation.required": (field: string) => `${josa(field, "은는")} 필수입니다`,
44
- "common.results": (count: number) => `${count}개 결과`,
45
- test: (date: Date) => format.date(date),
46
- } as const;
47
- ```
48
-
49
- ### Additional locale (en.ts)
50
-
51
- ```typescript
52
- import { plural } from "sonamu/dict";
53
- import { defineLocale } from "./sd.generated";
54
-
55
- export default defineLocale({
56
- "common.save": "Save",
57
- "common.cancel": "Cancel",
58
- "validation.required": (field: string) => `${field} is required`,
59
- "common.results": (count: number) =>
60
- plural(count, { one: `${count} result`, other: `${count} results` }),
61
- });
62
- ```
63
-
64
- ## Using the SD Function
65
-
66
- ### Basic Usage
67
-
68
- ```typescript
69
- import { SD } from "@/i18n/sd.generated";
70
-
71
- // Simple string
72
- SD("common.save"); // → "저장" (ko) / "Save" (en)
73
-
74
- // Function form (with parameters)
75
- SD("validation.required")("이메일"); // → "이메일은 필수입니다"
76
- SD("common.results")(5); // → "5개 결과" (ko) / "5 results" (en)
77
- ```
78
-
79
- ### Force a Specific Locale
80
-
81
- ```typescript
82
- const EN = SD.locale("en");
83
- EN("common.save"); // → "Save" (always English)
84
- ```
85
-
86
- ### Enum Labels
87
-
88
- ```typescript
89
- // Enum labels defined in entity.json
90
- SD.enumLabels("ProjectStatus")["in_progress"]; // → "진행중"
91
- ```
92
-
93
- ## Helper Functions
94
-
95
- ### plural (pluralization)
96
-
97
- ```typescript
98
- import { plural } from "sonamu/dict";
99
-
100
- // Handle singular/plural in English
101
- plural(count, {
102
- zero: "No items", // count === 0
103
- one: "1 item", // count === 1
104
- other: `${count} items`, // everything else
105
- });
106
- ```
107
-
108
- ### josa (Korean postposition helper)
109
-
110
- ```typescript
111
- import { josa } from "sonamu/dict";
112
-
113
- josa("이메일", "은는"); // → "이메일은"
114
- josa("이름", "이가"); // → "이름이"
115
- josa("파일", "을를"); // → "파일을"
116
- josa("회사", "과와"); // → "회사와"
117
- josa("서울", "으로"); // → "서울로"
118
- ```
119
-
120
- ### createFormat (number/date formatting)
121
-
122
- ```typescript
123
- import { createFormat } from "sonamu/dict";
124
-
125
- const format = createFormat("ko");
126
- format.number(1234567); // → "1,234,567"
127
- format.date(new Date()); // → "2024. 1. 15."
128
- ```
129
-
130
- ## Entity Integration
131
-
132
- Entity title, prop descriptions, and enum labels are automatically included in the dictionary.
133
-
134
- ```
135
- entity.{EntityId} → Entity title
136
- entity.{EntityId}.{propName} → Property description
137
- enum.{EnumId}.{value} → Enum value label
138
- ```
139
-
140
- Example:
141
-
142
- ```typescript
143
- SD("entity.User"); // → "사용자"
144
- SD("entity.User.email"); // → "이메일"
145
- SD("enum.UserRole.admin"); // → "관리자"
146
- ```
147
-
148
- ## localizedColumn
149
-
150
- Returns the value for the current locale when locale-specific columns or locale maps exist in the DB:
151
-
152
- ```typescript
153
- import { localizedColumn } from "@/i18n/sd.generated";
154
-
155
- // DB: { name: "태그", name_ko: "태그", name_en: "Tag" }
156
- localizedColumn(tag, "name"); // → "태그" (ko) / "Tag" (en)
157
-
158
- // DB: { name: { ko: ["태그"], en: ["Tag"] } }
159
- localizedColumn(tag, "name"); // → ["태그"] (ko) / ["Tag"] (en)
160
- ```
161
-
162
- - `string[]` values are returned as arrays instead of being stringified.
163
- - Unsupported Context locales use `defaultLocale` before lookup.
164
- - Direct suffix/base scalar values such as numbers are stringified for compatibility.
165
- - Locale map values support only `string` and `string[]`.
166
-
167
- ## Sonamu UI Management
168
-
169
- `http://localhost:34900/sonamu-ui` → i18n tab:
170
-
171
- - Browse/edit dictionary
172
- - Add/delete keys
173
- - Excel import/export
174
- - Detect unused keys
175
-
176
- ## Key Naming Conventions
177
-
178
- | Pattern | Purpose | Example |
179
- | -------------- | ------------------------- | ----------------------------------------- |
180
- | `common.*` | Common UI text | `common.save`, `common.cancel` |
181
- | `error.*` | Error messages | `error.notFound`, `error.forbidden` |
182
- | `validation.*` | Validation messages | `validation.required`, `validation.email` |
183
- | `entity.*` | Entity-related | `entity.User`, `entity.User.email` |
184
- | `enum.*` | Enum labels | `enum.UserRole.admin` |
185
- | `menu.*` | Menu/navigation | `menu.home`, `menu.settings` |
186
- | `rc.*` | react-components built-in | `rc.pagination.next` |
187
-
188
- ## Troubleshooting
189
-
190
- ### LocalizedString Type Error
191
-
192
- When you see `Type 'string' is not assignable to type '{ __brand: "LocalizedString"; }'`:
193
-
194
- **Cause 1: Missing i18n key in ko.ts**
195
-
196
- ```typescript
197
- // Wrong: type error when key is missing from ko.ts
198
- SD("validation.password"); // key not in ko.ts
199
- ```
200
-
201
- Fix: Add the key to ko.ts and run `pnpm sync`
202
-
203
- **Cause 2: Not using SD() in the model**
204
-
205
- ```typescript
206
- // Wrong: using a raw string directly
207
- throw new BadRequestException("이메일이 잘못되었습니다");
208
-
209
- // Correct: use the SD() function
210
- throw new BadRequestException(SD("error.invalidEmail"));
211
- ```
212
-
213
- All user-facing strings must be wrapped with the SD() function.
214
-
215
- ---
216
-
217
- ## Type Safety
218
-
219
- ### String PK Support
220
-
221
- When an Entity uses a String PK, the i18n function types must reflect this.
222
-
223
- **BAD: Number only**
224
-
225
- ```typescript
226
- // ko.ts
227
- notFound: (name: string, id: number) => `존재하지 않는 ${name} ID ${id}`,
228
-
229
- // Type error when used with String PK entities
230
- // Account, Session, User, Verification, etc.
231
- throw new NotFoundException(SD("notFound")(this.modelName, id));
232
- // Error: Argument of type 'string' is not assignable to parameter of type 'number'
233
- ```
234
-
235
- **GOOD: Support both with a union type**
236
-
237
- ```typescript
238
- // ko.ts
239
- notFound: (name: string, id: number | string) => `존재하지 않는 ${name} ID ${id}`,
240
-
241
- // Works for both Number PK and String PK
242
- throw new NotFoundException(SD("notFound")(this.modelName, id));
243
- ```
244
-
245
- **Applicable i18n keys:**
246
-
247
- - `notFound`: Entity lookup failure
248
- - `error.entityNotFound`: Specific entity lookup failure (deprecated, prefer `notFound`)
249
-
250
- ### Type Patterns by Namespace
251
-
252
- | i18n key | Parameter type | Purpose |
253
- | ---------------------- | -------------------------------------- | ------------------------- |
254
- | `notFound` | `(name: string, id: number \| string)` | Entity lookup failure |
255
- | `search.invalidField` | `(field: string)` | Invalid search field |
256
- | `validation.required` | `(field: string)` | Required field validation |
257
- | `validation.maxLength` | `(field: string, max: number)` | Max length validation |
258
- | `validation.minLength` | `(field: string, min: number)` | Min length validation |
259
- | `entity.create` | `(name: string)` | Entity creation screen |
260
- | `entity.edit` | `(name: string, id: number \| string)` | Entity edit screen |
261
-
262
- ### How to Check PK Type
263
-
264
- Check the id field type in the Entity file:
265
-
266
- ```json
267
- // String PK
268
- {
269
- "props": {
270
- "id": { "type": "varchar(255)", "autoIncrement": false }
271
- }
272
- }
273
-
274
- // Number PK (default)
275
- {
276
- "props": {
277
- "id": { "type": "int", "autoIncrement": true }
278
- }
279
- }
280
- ```
281
-
282
- **Entities that use a string PK (generated by better-auth):**
283
-
284
- - Account
285
- - Session
286
- - User
287
- - Verification
@@ -1,316 +0,0 @@
1
- ---
2
- name: sonamu-migration
3
- description: Generates and applies Sonamu database migrations. Use when a schema change needs a migration, a migration fails or conflicts, FK ordering breaks an apply, or a primary key type must change. Covers sonamu migrate generate/run/apply/status, up and down functions, and CREATE/ALTER TABLE output.
4
- ---
5
-
6
- # Migration
7
-
8
- ## CRITICAL: Migrations are generated via Sonamu UI or CLI
9
-
10
- **Do not write migration files manually or execute SQL directly.** Sonamu detects entity.json changes and generates accurate migration files.
11
-
12
- **Prerequisites:**
13
-
14
- - `pnpm dev` must be running in `/packages/api`
15
-
16
- **Method 1: Sonamu UI (confirm with user before choosing)**
17
-
18
- 1. Open Sonamu UI in browser: `http://localhost:34900/sonamu-ui`
19
- 2. Check the prepared list in the Migration menu
20
- 3. Generate a migration file with the "Generate" button
21
- 4. Apply to the actual DB with the "Apply" button
22
-
23
- **Method 2: CLI**
24
-
25
- ```bash
26
- cd packages/api
27
- pnpm sonamu migrate generate # Generate migration file
28
- pnpm sonamu migrate run # Apply to actual DB
29
- ```
30
-
31
- **CRITICAL: Ask the user whether to proceed with UI or CLI before starting.**
32
-
33
- **DO NOT:**
34
-
35
- - Write migration files manually
36
- - Execute SQL directly (`CREATE TABLE`, `ALTER TABLE`, etc.)
37
-
38
- **Exception:** Only raw SQL is allowed for special cases that Sonamu cannot handle automatically, such as PK type changes (see "PK Type Change" section below)
39
-
40
- **CRITICAL: Handle cross-table cascading changes in a single file.** Cascading changes across multiple tables — such as FK drop → type change → FK restore — must be executed in order within a single migration file. Splitting into separate files causes constraint violations in intermediate states. (Details: see "PK Type Change" section below)
41
-
42
- ## Basic Structure
43
-
44
- ```typescript
45
- import type { Knex } from "knex";
46
-
47
- export async function up(knex: Knex): Promise<void> {
48
- // Apply changes
49
- }
50
-
51
- export async function down(knex: Knex): Promise<void> {
52
- // Rollback changes
53
- }
54
- ```
55
-
56
- ## CREATE TABLE
57
-
58
- ```typescript
59
- export async function up(knex: Knex): Promise<void> {
60
- await knex.schema.createTable("users", (table) => {
61
- table.increments().primary();
62
- table.string("email", 255).notNullable();
63
- table.string("username", 255).notNullable();
64
- table.text("role").notNullable();
65
- table.timestamp("created_at", { useTz: true }).defaultTo(knex.fn.now());
66
- table.unique(["email"], "users_email_unique");
67
- });
68
- }
69
-
70
- export async function down(knex: Knex): Promise<void> {
71
- return knex.schema.dropTable("users");
72
- }
73
- ```
74
-
75
- ## ALTER TABLE
76
-
77
- ```typescript
78
- // Add column
79
- export async function up(knex: Knex): Promise<void> {
80
- await knex.schema.alterTable("users", (table) => {
81
- table.string("phone", 20).nullable();
82
- });
83
- }
84
-
85
- // Drop column
86
- export async function down(knex: Knex): Promise<void> {
87
- await knex.schema.alterTable("users", (table) => {
88
- table.dropColumns("phone");
89
- });
90
- }
91
- ```
92
-
93
- ## FOREIGN KEY
94
-
95
- ```typescript
96
- export async function up(knex: Knex): Promise<void> {
97
- return knex.schema.alterTable("employees", (table) => {
98
- table.foreign("user_id").references("users.id").onUpdate("CASCADE").onDelete("CASCADE");
99
- table
100
- .foreign("department_id")
101
- .references("departments.id")
102
- .onUpdate("CASCADE")
103
- .onDelete("SET NULL");
104
- });
105
- }
106
-
107
- export async function down(knex: Knex): Promise<void> {
108
- return knex.schema.alterTable("employees", (table) => {
109
- table.dropForeign(["user_id"]);
110
- table.dropForeign(["department_id"]);
111
- });
112
- }
113
- ```
114
-
115
- ## PK Type Change
116
-
117
- ### Situation
118
-
119
- When the PK type of an existing table must be changed (e.g. integer -> text, bigint -> uuid), with FKs referencing that PK in multiple tables.
120
-
121
- ### Required Order
122
-
123
- When changing the type of a column that has referential integrity (a PK referenced by FKs), the following order must be followed:
124
-
125
- ```typescript
126
- export async function up(knex: Knex): Promise<void> {
127
- // Step 1: DROP all FK constraints referencing this PK
128
- await knex.raw('ALTER TABLE "child_table_1" DROP CONSTRAINT "child_table_1_parent_id_foreign"');
129
- await knex.raw('ALTER TABLE "child_table_2" DROP CONSTRAINT "child_table_2_parent_id_foreign"');
130
-
131
- // Step 2: DROP the PK constraint
132
- await knex.raw('ALTER TABLE "parent_table" DROP CONSTRAINT "parent_table_pkey"');
133
-
134
- // Step 3: Change types of the PK column and all FK columns simultaneously
135
- await knex.raw('ALTER TABLE "parent_table" ALTER COLUMN "id" TYPE new_type USING "id"::new_type');
136
- await knex.raw(
137
- 'ALTER TABLE "child_table_1" ALTER COLUMN "parent_id" TYPE new_type USING "parent_id"::new_type',
138
- );
139
- await knex.raw(
140
- 'ALTER TABLE "child_table_2" ALTER COLUMN "parent_id" TYPE new_type USING "parent_id"::new_type',
141
- );
142
-
143
- // Step 4: ADD the PK constraint
144
- await knex.raw(
145
- 'ALTER TABLE "parent_table" ADD CONSTRAINT "parent_table_pkey" PRIMARY KEY ("id")',
146
- );
147
-
148
- // Step 5: ADD all FK constraints
149
- await knex.raw(
150
- 'ALTER TABLE "child_table_1" ADD CONSTRAINT "child_table_1_parent_id_foreign" FOREIGN KEY ("parent_id") REFERENCES "parent_table"("id") ON UPDATE RESTRICT ON DELETE CASCADE',
151
- );
152
- await knex.raw(
153
- 'ALTER TABLE "child_table_2" ADD CONSTRAINT "child_table_2_parent_id_foreign" FOREIGN KEY ("parent_id") REFERENCES "parent_table"("id") ON UPDATE RESTRICT ON DELETE RESTRICT',
154
- );
155
- }
156
- ```
157
-
158
- ### Core Principles
159
-
160
- 1. **Cannot change the type of a referenced column while FK constraints exist**: PostgreSQL throws an error if the types of the FK and referenced PK do not match. Constraints must be removed first.
161
-
162
- 2. **Handle all changes in a single migration**: Splitting into multiple migrations causes constraint violations in intermediate states. Change the types of the PK and all FKs in one go.
163
-
164
- 3. **Prefer raw SQL over the knex schema builder**: Guarantees clear execution order, allows explicit constraint naming, and supports complex type conversions (USING).
165
-
166
- ### Real Example: users.id integer -> text
167
-
168
- ```typescript
169
- export async function up(knex: Knex): Promise<void> {
170
- // 1. Remove FK constraints
171
- await knex.raw('ALTER TABLE "accounts" DROP CONSTRAINT "accounts_user_id_foreign"');
172
- await knex.raw('ALTER TABLE "sessions" DROP CONSTRAINT "sessions_user_id_foreign"');
173
-
174
- // 2. Remove PK constraint
175
- await knex.raw('ALTER TABLE "users" DROP CONSTRAINT "users_pkey"');
176
-
177
- // 3. Change types (explicit conversion with USING clause)
178
- await knex.raw('ALTER TABLE "users" ALTER COLUMN "id" TYPE text USING "id"::text');
179
- await knex.raw('ALTER TABLE "accounts" ALTER COLUMN "user_id" TYPE text USING "user_id"::text');
180
- await knex.raw('ALTER TABLE "sessions" ALTER COLUMN "user_id" TYPE text USING "user_id"::text');
181
-
182
- // 4. Restore PK constraint
183
- await knex.raw('ALTER TABLE "users" ADD CONSTRAINT "users_pkey" PRIMARY KEY ("id")');
184
-
185
- // 5. Restore FK constraints
186
- await knex.raw(
187
- 'ALTER TABLE "accounts" ADD CONSTRAINT "accounts_user_id_foreign" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON UPDATE RESTRICT ON DELETE CASCADE',
188
- );
189
- await knex.raw(
190
- 'ALTER TABLE "sessions" ADD CONSTRAINT "sessions_user_id_foreign" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON UPDATE RESTRICT ON DELETE CASCADE',
191
- );
192
- }
193
- ```
194
-
195
- ### Finding FK Reference Tables
196
-
197
- ```bash
198
- # Find relations referencing a specific entity in entity files
199
- grep -r "with.*User" --include="*.entity.json"
200
- ```
201
-
202
- ### Common Mistakes
203
-
204
- 1. **Splitting into multiple migrations**: Separating FK drop, type change, and FK restore into separate files causes a constraint violation immediately after the first file is applied. Always consolidate related changes into a single file.
205
-
206
- 2. **Changing type without removing constraint**: Causes `cannot alter type of a column used by a foreign key` error
207
-
208
- 3. **Missing USING clause**: Causes `column "id" cannot be cast automatically to type text` error. `USING "id"::text` is required for integer -> text.
209
-
210
- 4. **Constraint name mismatch**: Verify the exact constraint name with `SELECT constraint_name FROM information_schema.table_constraints WHERE table_name = 'accounts'`.
211
-
212
- ## Commands
213
-
214
- Run from the **`packages/api`** directory:
215
-
216
- ```bash
217
- cd packages/api
218
- pnpm sonamu migrate run # Run all pending migrations on local DB
219
- pnpm sonamu migrate status # Check migration status
220
- pnpm sonamu migrate apply # Apply to a specific DB configuration target
221
- ```
222
-
223
- **Note**: `migrate up` and `migrate rollback` are not provided in the CLI. Rollback is available from Sonamu UI.
224
-
225
- ## Entity Type → DB Type
226
-
227
- | Entity | DB | Knex |
228
- | -------------- | ------------------------- | ------------------------------------------- |
229
- | `string` | `varchar(n)` / `text` | `table.string(name, length)` |
230
- | `string[]` | `varchar(n)[]` / `text[]` | `table.specificType(name, 'text[]')` |
231
- | `integer` | `integer` | `table.integer(name)` |
232
- | `integer[]` | `integer[]` | `table.specificType(name, 'integer[]')` |
233
- | `bigInteger` | `bigint` | `table.bigInteger(name)` |
234
- | `bigInteger[]` | `bigint[]` | `table.specificType(name, 'bigint[]')` |
235
- | `boolean` | `boolean` | `table.boolean(name)` |
236
- | `boolean[]` | `boolean[]` | `table.specificType(name, 'boolean[]')` |
237
- | `number` | `numeric(p,s)` | `table.decimal(name, p, s)` |
238
- | `numeric` | `numeric(p,s)` | `table.decimal(name, p, s)` |
239
- | `date` | `timestamptz` | `table.timestamp(name, { useTz: true })` |
240
- | `date[]` | `timestamptz[]` | `table.specificType(name, 'timestamptz[]')` |
241
- | `uuid` | `uuid` | `table.uuid(name)` |
242
- | `uuid[]` | `uuid[]` | `table.specificType(name, 'uuid[]')` |
243
- | `json` | `jsonb` | `table.jsonb(name)` |
244
- | `enum` | `text` | `table.text(name)` |
245
- | `vector` | `vector(n)` | `table.specificType(name, 'vector(n)')` |
246
- | `vector[]` | `vector(n)[]` | `table.specificType(name, 'vector(n)[]')` |
247
- | `tsvector` | `tsvector` | `table.specificType(name, 'tsvector')` |
248
-
249
- ## Execution Order (Important!)
250
-
251
- ```
252
- 1. CREATE TABLE companies (no dependencies)
253
- 2. CREATE TABLE departments (company_id column only)
254
- 3. CREATE TABLE users (no dependencies)
255
- 4. CREATE TABLE employees (user_id, department_id columns only)
256
- 5. FOREIGN KEY departments (company_id → companies.id)
257
- 6. FOREIGN KEY employees (user_id → users.id, etc.)
258
- ```
259
-
260
- ## Rules
261
-
262
- - Migration files are generated from Sonamu UI
263
- - MUST implement rollback logic in `down` function
264
- - Foreign keys MUST be added in separate migration after table creation
265
-
266
- ## IMPORTANT: Check Before Running sync
267
-
268
- ### 1. MUST Build dist First
269
-
270
- The sync command references `dist/sonamu.config.js`. An error will occur if it does not exist.
271
-
272
- ```bash
273
- cd packages/api
274
- pnpm exec tsdown --config tsdown.config.ts
275
- ```
276
-
277
- ### 2. Verify DB Connection
278
-
279
- ```bash
280
- # Check Docker containers
281
- docker ps | grep container
282
-
283
- # Check DB configuration in sonamu.config.ts
284
- # - host, port, database, user, password
285
- # - Distinguish PostgreSQL vs MySQL
286
- ```
287
-
288
- ### 3. Circular References
289
-
290
- Split the migration, or set the FK to nullable and add it later.
291
-
292
- ## Full Workflow
293
-
294
- ```bash
295
- cd packages/api
296
-
297
- # 1. Build config
298
- pnpm exec tsdown --config tsdown.config.ts
299
-
300
- # 2. Run sync
301
- pnpm sonamu sync
302
-
303
- # 3. Generate/run migration from Sonamu UI
304
-
305
- # 4. Run scaffolding
306
-
307
- # 5. Check/add orderBy cases in model (see model.md)
308
-
309
- # 6. Build API
310
- npm run build
311
-
312
- # 7. Check/fix web form (see frontend.md)
313
-
314
- # 8. Build web
315
- cd ../web && npm run build
316
- ```