sonamu 0.8.12 → 0.8.13
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/testing/parallel-db-manager.js +6 -3
- package/package.json +9 -9
- package/src/skills/sonamu/SKILL.md +5 -1
- package/src/skills/sonamu/api.md +26 -0
- package/src/skills/sonamu/auth.md +1 -1
- package/src/skills/sonamu/cdd.md +441 -0
- package/src/skills/sonamu/entity-relations.md +17 -26
- package/src/skills/sonamu/frontend.md +23 -16
- package/src/skills/sonamu/model.md +1 -0
- package/src/skills/sonamu/workflow.md +47 -1
- package/src/testing/parallel-db-manager.ts +2 -2
|
@@ -25,8 +25,11 @@ import { createKnexInstance } from "../database/knex.js";
|
|
|
25
25
|
const workerDbNames = Array.from({
|
|
26
26
|
length: this.maxWorkers
|
|
27
27
|
}, (_, i)=>`${this.templateDb}_${i + 1}`);
|
|
28
|
-
// 1. 기존 연결 종료 (병렬)
|
|
29
|
-
await Promise.all(
|
|
28
|
+
// 1. 기존 연결 종료 (병렬) worker DB + template DB (PG 18에서 FILE_COPY 시 template에 exclusive access 필요)
|
|
29
|
+
await Promise.all([
|
|
30
|
+
...workerDbNames,
|
|
31
|
+
this.templateDb
|
|
32
|
+
].map((dbName)=>adminDb.raw(`
|
|
30
33
|
SELECT pg_terminate_backend(pg_stat_activity.pid)
|
|
31
34
|
FROM pg_stat_activity
|
|
32
35
|
WHERE pg_stat_activity.datname = ?
|
|
@@ -82,4 +85,4 @@ import { createKnexInstance } from "../database/knex.js";
|
|
|
82
85
|
}
|
|
83
86
|
}
|
|
84
87
|
|
|
85
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90ZXN0aW5nL3BhcmFsbGVsLWRiLW1hbmFnZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUgeyBLbmV4IH0gZnJvbSBcImtuZXhcIjtcbmltcG9ydCB7IGNyZWF0ZUtuZXhJbnN0YW5jZSB9IGZyb20gXCIuLi9kYXRhYmFzZS9rbmV4XCI7XG5cbi8qKlxuICog67OR66CsIO2FjOyKpO2KuOulvCDsnITtlZwgV29ya2Vy67OEIERCIOq0gOumrCDtgbTrnpjsiqTsnoXri4jri6QuXG4gKlxuICogVml0ZXN07J2YIGdsb2JhbFNldHVwL2dsb2JhbFRlYXJkb3du7JeQ7IScIOyCrOyaqe2VmOyXrFxuICog7YWc7ZSM66a/IERC7JeQ7IScIHdvcmtlciDsiJjrp4ztgbwg7YWM7Iqk7Yq4IERC66W8IOuzteygnC/sgq3soJztlanri4jri6QuXG4gKi9cbmV4cG9ydCBjbGFzcyBQYXJhbGxlbERCTWFuYWdlciB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgbWF4V29ya2VyczogbnVtYmVyLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgZGJDb25maWc6IEtuZXguQ29uZmlnLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgdGVtcGxhdGVEYjogc3RyaW5nLFxuICApIHt9XG4gIC8qKlxuICAgKiBXb3JrZXLrs4Qg7YWM7Iqk7Yq4IERC66W8IO2FnO2UjOumv+yXkOyEnCDrs7XsoJztlZjsl6wg7IOd7ISx7ZWp64uI64ukLlxuICAgKiBnbG9iYWxTZXR1cOyXkOyEnCDtmLjstpzrkKnri4jri6QuXG4gICAqL1xuICBhc3luYyBjcmVhdGVXb3JrZXJEYXRhYmFzZXMoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gcG9zdGdyZXMgRELsl5Ag7Jew6rKw7ZWY7JesIENSRUFURSBEQVRBQkFTRSDsi6TtlolcbiAgICBjb25zdCBhZG1pbkRiID0gY3JlYXRlS25leEluc3RhbmNlKHtcbiAgICAgIC4uLnRoaXMuZGJDb25maWcsXG4gICAgfSk7XG5cbiAgICB0cnkge1xuICAgICAgY29uc3Qgd29ya2VyRGJOYW1lcyA9IEFycmF5LmZyb20oXG4gICAgICAgIHsgbGVuZ3RoOiB0aGlzLm1heFdvcmtlcnMgfSxcbiAgICAgICAgKF8sIGkpID0+
|
|
88
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90ZXN0aW5nL3BhcmFsbGVsLWRiLW1hbmFnZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUgeyBLbmV4IH0gZnJvbSBcImtuZXhcIjtcbmltcG9ydCB7IGNyZWF0ZUtuZXhJbnN0YW5jZSB9IGZyb20gXCIuLi9kYXRhYmFzZS9rbmV4XCI7XG5cbi8qKlxuICog67OR66CsIO2FjOyKpO2KuOulvCDsnITtlZwgV29ya2Vy67OEIERCIOq0gOumrCDtgbTrnpjsiqTsnoXri4jri6QuXG4gKlxuICogVml0ZXN07J2YIGdsb2JhbFNldHVwL2dsb2JhbFRlYXJkb3du7JeQ7IScIOyCrOyaqe2VmOyXrFxuICog7YWc7ZSM66a/IERC7JeQ7IScIHdvcmtlciDsiJjrp4ztgbwg7YWM7Iqk7Yq4IERC66W8IOuzteygnC/sgq3soJztlanri4jri6QuXG4gKi9cbmV4cG9ydCBjbGFzcyBQYXJhbGxlbERCTWFuYWdlciB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgbWF4V29ya2VyczogbnVtYmVyLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgZGJDb25maWc6IEtuZXguQ29uZmlnLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgdGVtcGxhdGVEYjogc3RyaW5nLFxuICApIHt9XG4gIC8qKlxuICAgKiBXb3JrZXLrs4Qg7YWM7Iqk7Yq4IERC66W8IO2FnO2UjOumv+yXkOyEnCDrs7XsoJztlZjsl6wg7IOd7ISx7ZWp64uI64ukLlxuICAgKiBnbG9iYWxTZXR1cOyXkOyEnCDtmLjstpzrkKnri4jri6QuXG4gICAqL1xuICBhc3luYyBjcmVhdGVXb3JrZXJEYXRhYmFzZXMoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gcG9zdGdyZXMgRELsl5Ag7Jew6rKw7ZWY7JesIENSRUFURSBEQVRBQkFTRSDsi6TtlolcbiAgICBjb25zdCBhZG1pbkRiID0gY3JlYXRlS25leEluc3RhbmNlKHtcbiAgICAgIC4uLnRoaXMuZGJDb25maWcsXG4gICAgfSk7XG5cbiAgICB0cnkge1xuICAgICAgY29uc3Qgd29ya2VyRGJOYW1lcyA9IEFycmF5LmZyb20oXG4gICAgICAgIHsgbGVuZ3RoOiB0aGlzLm1heFdvcmtlcnMgfSxcbiAgICAgICAgKF8sIGkpID0+IGAke3RoaXMudGVtcGxhdGVEYn1fJHtpICsgMX1gLFxuICAgICAgKTtcblxuICAgICAgLy8gMS4g6riw7KG0IOyXsOqysCDsooXro4wgKOuzkeugrCkgd29ya2VyIERCICsgdGVtcGxhdGUgREIgKFBHIDE47JeQ7IScIEZJTEVfQ09QWSDsi5wgdGVtcGxhdGXsl5AgZXhjbHVzaXZlIGFjY2VzcyDtlYTsmpQpXG4gICAgICBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgICAgWy4uLndvcmtlckRiTmFtZXMsIHRoaXMudGVtcGxhdGVEYl0ubWFwKChkYk5hbWUpID0+XG4gICAgICAgICAgYWRtaW5EYi5yYXcoXG4gICAgICAgICAgICBgXG4gICAgICAgICAgICBTRUxFQ1QgcGdfdGVybWluYXRlX2JhY2tlbmQocGdfc3RhdF9hY3Rpdml0eS5waWQpXG4gICAgICAgICAgICBGUk9NIHBnX3N0YXRfYWN0aXZpdHlcbiAgICAgICAgICAgIFdIRVJFIHBnX3N0YXRfYWN0aXZpdHkuZGF0bmFtZSA9ID9cbiAgICAgICAgICAgIEFORCBwaWQgPD4gcGdfYmFja2VuZF9waWQoKVxuICAgICAgICAgIGAsXG4gICAgICAgICAgICBbZGJOYW1lXSxcbiAgICAgICAgICApLFxuICAgICAgICApLFxuICAgICAgKTtcblxuICAgICAgLy8gMi4g6riw7KG0IERCIOyCreygnCAo67OR66CsKVxuICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICAgIHdvcmtlckRiTmFtZXMubWFwKChkYk5hbWUpID0+IGFkbWluRGIucmF3KGBEUk9QIERBVEFCQVNFIElGIEVYSVNUUyBcIiR7ZGJOYW1lfVwiYCkpLFxuICAgICAgKTtcblxuICAgICAgLy8gMy4g7YWc7ZSM66a/7JeQ7IScIOuzteygnCAo67OR66CsKVxuICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICAgIHdvcmtlckRiTmFtZXMubWFwKChkYk5hbWUpID0+XG4gICAgICAgICAgYWRtaW5EYi5yYXcoXG4gICAgICAgICAgICBgQ1JFQVRFIERBVEFCQVNFIFwiJHtkYk5hbWV9XCIgVEVNUExBVEUgXCIke3RoaXMudGVtcGxhdGVEYn1cIiBTVFJBVEVHWSBGSUxFX0NPUFlgLFxuICAgICAgICAgICksXG4gICAgICAgICksXG4gICAgICApO1xuICAgIH0gZmluYWxseSB7XG4gICAgICBhd2FpdCBhZG1pbkRiLmRlc3Ryb3koKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICog7IOd7ISx7ZWcIFdvcmtlciBEQuuTpOydhCDsgq3soJztlanri4jri6QuXG4gICAqIGdsb2JhbFRlYXJkb3du7JeQ7IScIO2YuOy2nOuQqeuLiOuLpC5cbiAgICovXG4gIGFzeW5jIGRyb3BXb3JrZXJEYXRhYmFzZXMoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgYWRtaW5EYiA9IGNyZWF0ZUtuZXhJbnN0YW5jZSh7XG4gICAgICAuLi50aGlzLmRiQ29uZmlnLFxuICAgIH0pO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHdvcmtlckRiTmFtZXMgPSBBcnJheS5mcm9tKFxuICAgICAgICB7IGxlbmd0aDogdGhpcy5tYXhXb3JrZXJzIH0sXG4gICAgICAgIChfLCBpKSA9PiBgJHt0aGlzLnRlbXBsYXRlRGJ9XyR7aSArIDF9YCxcbiAgICAgICk7XG5cbiAgICAgIC8vIOyXsOqysCDsooXro4wgKOuzkeugrClcbiAgICAgIGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgICB3b3JrZXJEYk5hbWVzLm1hcCgoZGJOYW1lKSA9PlxuICAgICAgICAgIGFkbWluRGIucmF3KFxuICAgICAgICAgICAgYFxuICAgICAgICAgICAgU0VMRUNUIHBnX3Rlcm1pbmF0ZV9iYWNrZW5kKHBnX3N0YXRfYWN0aXZpdHkucGlkKVxuICAgICAgICAgICAgRlJPTSBwZ19zdGF0X2FjdGl2aXR5XG4gICAgICAgICAgICBXSEVSRSBwZ19zdGF0X2FjdGl2aXR5LmRhdG5hbWUgPSA/XG4gICAgICAgICAgICBBTkQgcGlkIDw+IHBnX2JhY2tlbmRfcGlkKClcbiAgICAgICAgICBgLFxuICAgICAgICAgICAgW2RiTmFtZV0sXG4gICAgICAgICAgKSxcbiAgICAgICAgKSxcbiAgICAgICk7XG5cbiAgICAgIC8vIERCIOyCreygnCAo67OR66CsKVxuICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICAgIHdvcmtlckRiTmFtZXMubWFwKChkYk5hbWUpID0+IGFkbWluRGIucmF3KGBEUk9QIERBVEFCQVNFIElGIEVYSVNUUyBcIiR7ZGJOYW1lfVwiYCkpLFxuICAgICAgKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgYXdhaXQgYWRtaW5EYi5kZXN0cm95KCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIO2YhOyerCBXb3JrZXLsnZggSUTrpbwg67CY7ZmY7ZWp64uI64ukLlxuICAgKiBWaXRlc3TripQgVklURVNUX1BPT0xfSUQg7ZmY6rK967OA7IiY66GcIHdvcmtlcuulvCDsi53rs4Ttlanri4jri6QuXG4gICAqL1xuICBnZXRXb3JrZXJJZCgpOiBudW1iZXIge1xuICAgIC8vIFZJVEVTVF9QT09MX0lE64qUIDHrtoDthLAg7Iuc7J6RXG4gICAgcmV0dXJuIHBhcnNlSW50KHByb2Nlc3MuZW52LlZJVEVTVF9QT09MX0lEID8/IFwiMVwiLCAxMCk7XG4gIH1cblxuICAvKipcbiAgICog67OR66CsIO2FjOyKpO2KuCDrqqjrk5zsnbjsp4Ag7ZmV7J247ZWp64uI64ukLlxuICAgKi9cbiAgaXNQYXJhbGxlbE1vZGUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHByb2Nlc3MuZW52LlNPTkFNVV9XT1JLRVJfREIgPT09IFwidHJ1ZVwiO1xuICB9XG59XG4iXSwibmFtZXMiOlsiY3JlYXRlS25leEluc3RhbmNlIiwiUGFyYWxsZWxEQk1hbmFnZXIiLCJtYXhXb3JrZXJzIiwiZGJDb25maWciLCJ0ZW1wbGF0ZURiIiwiY3JlYXRlV29ya2VyRGF0YWJhc2VzIiwiYWRtaW5EYiIsIndvcmtlckRiTmFtZXMiLCJBcnJheSIsImZyb20iLCJsZW5ndGgiLCJfIiwiaSIsIlByb21pc2UiLCJhbGwiLCJtYXAiLCJkYk5hbWUiLCJyYXciLCJkZXN0cm95IiwiZHJvcFdvcmtlckRhdGFiYXNlcyIsImdldFdvcmtlcklkIiwicGFyc2VJbnQiLCJwcm9jZXNzIiwiZW52IiwiVklURVNUX1BPT0xfSUQiLCJpc1BhcmFsbGVsTW9kZSIsIlNPTkFNVV9XT1JLRVJfREIiXSwibWFwcGluZ3MiOiJBQUNBLFNBQVNBLGtCQUFrQixRQUFRLHNCQUFtQjtBQUV0RDs7Ozs7Q0FLQyxHQUNELE9BQU8sTUFBTUM7Ozs7SUFDWCxZQUNFLEFBQWlCQyxVQUFrQixFQUNuQyxBQUFpQkMsUUFBcUIsRUFDdEMsQUFBaUJDLFVBQWtCLENBQ25DO2FBSGlCRixhQUFBQTthQUNBQyxXQUFBQTthQUNBQyxhQUFBQTtJQUNoQjtJQUNIOzs7R0FHQyxHQUNELE1BQU1DLHdCQUF1QztRQUMzQyx1Q0FBdUM7UUFDdkMsTUFBTUMsVUFBVU4sbUJBQW1CO1lBQ2pDLEdBQUcsSUFBSSxDQUFDRyxRQUFRO1FBQ2xCO1FBRUEsSUFBSTtZQUNGLE1BQU1JLGdCQUFnQkMsTUFBTUMsSUFBSSxDQUM5QjtnQkFBRUMsUUFBUSxJQUFJLENBQUNSLFVBQVU7WUFBQyxHQUMxQixDQUFDUyxHQUFHQyxJQUFNLEdBQUcsSUFBSSxDQUFDUixVQUFVLENBQUMsQ0FBQyxFQUFFUSxJQUFJLEdBQUc7WUFHekMsK0ZBQStGO1lBQy9GLE1BQU1DLFFBQVFDLEdBQUcsQ0FDZjttQkFBSVA7Z0JBQWUsSUFBSSxDQUFDSCxVQUFVO2FBQUMsQ0FBQ1csR0FBRyxDQUFDLENBQUNDLFNBQ3ZDVixRQUFRVyxHQUFHLENBQ1QsQ0FBQzs7Ozs7VUFLSCxDQUFDLEVBQ0M7b0JBQUNEO2lCQUFPO1lBS2QsbUJBQW1CO1lBQ25CLE1BQU1ILFFBQVFDLEdBQUcsQ0FDZlAsY0FBY1EsR0FBRyxDQUFDLENBQUNDLFNBQVdWLFFBQVFXLEdBQUcsQ0FBQyxDQUFDLHlCQUF5QixFQUFFRCxPQUFPLENBQUMsQ0FBQztZQUdqRixtQkFBbUI7WUFDbkIsTUFBTUgsUUFBUUMsR0FBRyxDQUNmUCxjQUFjUSxHQUFHLENBQUMsQ0FBQ0MsU0FDakJWLFFBQVFXLEdBQUcsQ0FDVCxDQUFDLGlCQUFpQixFQUFFRCxPQUFPLFlBQVksRUFBRSxJQUFJLENBQUNaLFVBQVUsQ0FBQyxvQkFBb0IsQ0FBQztRQUl0RixTQUFVO1lBQ1IsTUFBTUUsUUFBUVksT0FBTztRQUN2QjtJQUNGO0lBRUE7OztHQUdDLEdBQ0QsTUFBTUMsc0JBQXFDO1FBQ3pDLE1BQU1iLFVBQVVOLG1CQUFtQjtZQUNqQyxHQUFHLElBQUksQ0FBQ0csUUFBUTtRQUNsQjtRQUVBLElBQUk7WUFDRixNQUFNSSxnQkFBZ0JDLE1BQU1DLElBQUksQ0FDOUI7Z0JBQUVDLFFBQVEsSUFBSSxDQUFDUixVQUFVO1lBQUMsR0FDMUIsQ0FBQ1MsR0FBR0MsSUFBTSxHQUFHLElBQUksQ0FBQ1IsVUFBVSxDQUFDLENBQUMsRUFBRVEsSUFBSSxHQUFHO1lBR3pDLGFBQWE7WUFDYixNQUFNQyxRQUFRQyxHQUFHLENBQ2ZQLGNBQWNRLEdBQUcsQ0FBQyxDQUFDQyxTQUNqQlYsUUFBUVcsR0FBRyxDQUNULENBQUM7Ozs7O1VBS0gsQ0FBQyxFQUNDO29CQUFDRDtpQkFBTztZQUtkLGFBQWE7WUFDYixNQUFNSCxRQUFRQyxHQUFHLENBQ2ZQLGNBQWNRLEdBQUcsQ0FBQyxDQUFDQyxTQUFXVixRQUFRVyxHQUFHLENBQUMsQ0FBQyx5QkFBeUIsRUFBRUQsT0FBTyxDQUFDLENBQUM7UUFFbkYsU0FBVTtZQUNSLE1BQU1WLFFBQVFZLE9BQU87UUFDdkI7SUFDRjtJQUVBOzs7R0FHQyxHQUNERSxjQUFzQjtRQUNwQix5QkFBeUI7UUFDekIsT0FBT0MsU0FBU0MsUUFBUUMsR0FBRyxDQUFDQyxjQUFjLElBQUksS0FBSztJQUNyRDtJQUVBOztHQUVDLEdBQ0RDLGlCQUEwQjtRQUN4QixPQUFPSCxRQUFRQyxHQUFHLENBQUNHLGdCQUFnQixLQUFLO0lBQzFDO0FBQ0YifQ==
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sonamu",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.13",
|
|
4
4
|
"description": "Sonamu — TypeScript Fullstack API Framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -76,8 +76,8 @@
|
|
|
76
76
|
"@aws-sdk/client-s3": "^3.958.0",
|
|
77
77
|
"@aws-sdk/lib-storage": "^3.971.0",
|
|
78
78
|
"@aws-sdk/s3-request-presigner": "^3.958.0",
|
|
79
|
-
"@better-auth/passkey": "
|
|
80
|
-
"@better-auth/sso": "
|
|
79
|
+
"@better-auth/passkey": "~1.4.18",
|
|
80
|
+
"@better-auth/sso": "~1.4.18",
|
|
81
81
|
"@biomejs/js-api": "^4.0.0",
|
|
82
82
|
"@biomejs/wasm-nodejs": "^2.3.7",
|
|
83
83
|
"@faker-js/faker": "^9.2.0",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"@swc/core": "^1.13.5",
|
|
97
97
|
"bcrypt": "^6.0.0",
|
|
98
98
|
"bentocache": "^1.5.0",
|
|
99
|
-
"better-auth": "
|
|
99
|
+
"better-auth": "~1.4.18",
|
|
100
100
|
"chalk": "^4.1.2",
|
|
101
101
|
"chokidar": "^4.0.3",
|
|
102
102
|
"date-fns-tz": "^3.2.0",
|
|
@@ -120,9 +120,9 @@
|
|
|
120
120
|
"vite": "7.3.0",
|
|
121
121
|
"vitest": "^4.0.10",
|
|
122
122
|
"@sonamu-kit/hmr-hook": "^0.4.1",
|
|
123
|
+
"@sonamu-kit/hmr-runner": "^0.1.1",
|
|
123
124
|
"@sonamu-kit/ts-loader": "^2.1.3",
|
|
124
|
-
"@sonamu-kit/tasks": "^0.2.0"
|
|
125
|
-
"@sonamu-kit/hmr-runner": "^0.1.1"
|
|
125
|
+
"@sonamu-kit/tasks": "^0.2.0"
|
|
126
126
|
},
|
|
127
127
|
"devDependencies": {
|
|
128
128
|
"@biomejs/biome": "^2.3.13",
|
|
@@ -143,12 +143,12 @@
|
|
|
143
143
|
"@ai-sdk/openai": "^3.0.0",
|
|
144
144
|
"@ai-sdk/provider": "^3.0.0",
|
|
145
145
|
"@ai-sdk/provider-utils": "^4.0.0",
|
|
146
|
-
"@better-auth/passkey": "
|
|
147
|
-
"@better-auth/sso": "
|
|
146
|
+
"@better-auth/passkey": "~1.4.18",
|
|
147
|
+
"@better-auth/sso": "~1.4.18",
|
|
148
148
|
"@swc/cli": "^0.7.8",
|
|
149
149
|
"@swc/core": "^1.13.5",
|
|
150
150
|
"ai": "^6.0.1",
|
|
151
|
-
"better-auth": "
|
|
151
|
+
"better-auth": "~1.4.18",
|
|
152
152
|
"fastify": "^4",
|
|
153
153
|
"ioredis": "^5.8.2",
|
|
154
154
|
"knex": "^3.1.0",
|
|
@@ -27,6 +27,7 @@ Sonamu 프레임워크로 프로젝트를 개발하기 위한 Claude Code skill
|
|
|
27
27
|
|
|
28
28
|
```
|
|
29
29
|
PHASE 0: 프로젝트 생성 및 초기 설정 (프로젝트 생성 → requirements.md/business-logic.md 작성 → auth generate → Users 시퀀스 설정)
|
|
30
|
+
PHASE 0.5: Contract 및 Spec 작성 (main.contract.json → 도메인별 contract → spec → 사용자 검토 완료)
|
|
30
31
|
PHASE 1: 엔티티 설계 (사용자와 함께 설계 확인)
|
|
31
32
|
PHASE 2: 엔티티 생성 및 마이그레이션 (entity.json + migration + cone + scaffolding)
|
|
32
33
|
PHASE 3: 테스트 및 API 구현 (batch별 테스트 → API 구현 반복)
|
|
@@ -43,7 +44,8 @@ PHASE 5: Frontend 개발 (batch별 진행, 사용자 확인)
|
|
|
43
44
|
| 사용자 지시 | 시작 PHASE | 전제 조건 확인 |
|
|
44
45
|
|------------|------------|----------------|
|
|
45
46
|
| "새 프로젝트 만들어줘" | PHASE 0 | — |
|
|
46
|
-
| "
|
|
47
|
+
| "contract 작성해줘" / "spec 작성해줘" | PHASE 0.5 | requirements.md, business-logic.md 존재 |
|
|
48
|
+
| "엔티티 추가해줘" / "엔티티 생성해줘" | PHASE 1 | 프로젝트 존재, dev 서버 실행 중, skills/project/ 문서 읽기, contract/spec 완료 |
|
|
47
49
|
| "테스트 작성해줘" / "API 구현해줘" | PHASE 3 | entity.json 존재, migration 완료, scaffolding 완료 |
|
|
48
50
|
| "fixture 생성해줘" | PHASE 4 | 테스트 통과, cone.note 존재 확인 |
|
|
49
51
|
| "프론트엔드 개발해줘" | PHASE 5 | API 구현 완료 |
|
|
@@ -221,6 +223,7 @@ confirmed → cancelled (환자/의사 취소, 24시간 전까지만)
|
|
|
221
223
|
| Skill | 파일 | 용도 |
|
|
222
224
|
|-------|------|------|
|
|
223
225
|
| **전체 워크플로우** | `workflow.md` | **엔티티 설계 → Frontend 개발 7단계 가이드** |
|
|
226
|
+
| **CDD (Contract-Driven Dev)** | `cdd.md` | **main.contract.json, 도메인 contract, spec 작성 절차** |
|
|
224
227
|
| 프로젝트 생성 | `create-sonamu.md` | create-sonamu CLI 옵션 |
|
|
225
228
|
| 프로젝트 초기화 | `project-init.md` | 프로젝트 생성 여부 확인, 대화 흐름 |
|
|
226
229
|
| 프로젝트 설정 | `config.md` | .env, sonamu.config.ts 설정 |
|
|
@@ -257,6 +260,7 @@ confirmed → cancelled (환자/의사 취소, 24시간 전까지만)
|
|
|
257
260
|
| 작업 | 참고 Skill |
|
|
258
261
|
|------|-----------|
|
|
259
262
|
| **처음부터 전체 시스템 개발** | **workflow.md (7단계 마스터 가이드)** |
|
|
263
|
+
| **Contract/Spec 작성** | **cdd.md** |
|
|
260
264
|
| 프로젝트 생성 | create-sonamu, project-init |
|
|
261
265
|
| 프로젝트 설정 | config |
|
|
262
266
|
| Sonamu 로컬 개발 설정 | config |
|
package/src/skills/sonamu/api.md
CHANGED
|
@@ -94,6 +94,32 @@ async me(): Promise<User | null> {
|
|
|
94
94
|
|
|
95
95
|
## 파일 업로드 (@upload)
|
|
96
96
|
|
|
97
|
+
> **CRITICAL: `@upload`는 `@api` 없이 단독으로 사용한다.**
|
|
98
|
+
> `@upload`를 붙이면 POST 엔드포인트와 `axios-multipart`/`tanstack-mutation-multipart` 클라이언트가 **자동 생성**된다.
|
|
99
|
+
> `@api`를 함께 붙이면 `checkSingleDecorator` 충돌로 **빌드 에러**가 발생한다.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
// ✅ CORRECT
|
|
103
|
+
@upload({ limits: { files: 10 }, guards: ["user"] })
|
|
104
|
+
async upload(...): Promise<number[]> { }
|
|
105
|
+
|
|
106
|
+
// ❌ WRONG — 빌드 에러 발생
|
|
107
|
+
@api({ httpMethod: "POST", clients: ["axios-multipart"] })
|
|
108
|
+
@upload({ limits: { files: 10 } })
|
|
109
|
+
async upload(...): Promise<number[]> { }
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**`@upload` 지원 옵션** (`httpMethod`, `clients`는 지원하지 않음 — 자동 설정됨)
|
|
113
|
+
|
|
114
|
+
| 옵션 | 설명 |
|
|
115
|
+
|------|------|
|
|
116
|
+
| `guards` | 인증/권한 가드 |
|
|
117
|
+
| `limits` | 파일 개수/크기 제한 (`{ files: N }`) |
|
|
118
|
+
| `consume` | `"buffer"` (기본) 또는 `"stream"` |
|
|
119
|
+
| `description` | API 문서 설명 |
|
|
120
|
+
| `destination` | 스트림 모드 전용: 스토리지 드라이버 키 |
|
|
121
|
+
| `keyGenerator` | 스트림 모드 전용: 저장 경로 생성 함수 |
|
|
122
|
+
|
|
97
123
|
### 버퍼 모드 (기본)
|
|
98
124
|
|
|
99
125
|
```typescript
|
|
@@ -35,7 +35,7 @@ pnpm sonamu auth generate
|
|
|
35
35
|
|
|
36
36
|
## 필드 매핑 (자동 적용)
|
|
37
37
|
|
|
38
|
-
**소스코드:** `modules/sonamu/src/auth/better-auth-entities.ts` (
|
|
38
|
+
**소스코드:** `modules/sonamu/src/auth/better-auth-entities.ts` (BASE_FIELD_MAPPINGS)
|
|
39
39
|
|
|
40
40
|
| better-auth | Sonamu |
|
|
41
41
|
|-------------|--------|
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sonamu-cdd
|
|
3
|
+
description: Contract-Driven Development(CDD) 가이드. main.contract.json, 도메인별 contract, spec 작성 절차, CDD CLI, 개발 프로세스. Entity 설계 전에 반드시 완료해야 함. Use when writing contracts or specs before entity design.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Contract-Driven Development (CDD)
|
|
7
|
+
|
|
8
|
+
This project follows Contract-Driven Development (CDD). All development work must follow the rules below.
|
|
9
|
+
|
|
10
|
+
## Core Principles
|
|
11
|
+
|
|
12
|
+
- The `.contract.json` files under the `contract/` directory are the Single Source of Truth (SSoT) for this project.
|
|
13
|
+
- All development follows a **Waterfall process**. Move to the next stage only after the current stage is completed.
|
|
14
|
+
- Authority flows in this order: **Contract -> Spec -> Code**.
|
|
15
|
+
- Code must always follow Spec.
|
|
16
|
+
- Spec must always follow Contract.
|
|
17
|
+
- **1 Contract Feature = 1 Spec File**. Each feature in Contract's `Features/Capabilities` maps to exactly one Spec file. Shared infrastructure across features may be separated into `shared/*.spec.json`.
|
|
18
|
+
- Even if a better structure appears during implementation, do not change code first. Update Spec first, then update code.
|
|
19
|
+
- Contract is human-managed. AI must not modify Contract files without user request. When the user explicitly asks to update Contract, AI may edit directly. Otherwise, AI should only propose changes.
|
|
20
|
+
- Code-document consistency is verified by AI automated validation (1st pass) and review checklist (2nd pass for feature mapping/coverage).
|
|
21
|
+
|
|
22
|
+
## Project Structure
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
packages/api/contract/
|
|
26
|
+
|- main.contract.json # project root contract
|
|
27
|
+
|- {domain}/
|
|
28
|
+
| |- main.contract.json # domain representative contract
|
|
29
|
+
| |- {sub-contract}.contract.json
|
|
30
|
+
| |- {feature-key}.spec.json # 1 feature = 1 spec file
|
|
31
|
+
|- shared/
|
|
32
|
+
| |- {shared-infra}.spec.json # cross-feature shared infrastructure
|
|
33
|
+
\- ...
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Example:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
packages/api/contract/
|
|
40
|
+
|- main.contract.json
|
|
41
|
+
|- auth/
|
|
42
|
+
| |- main.contract.json
|
|
43
|
+
| |- login.spec.json # login feature spec (1:1)
|
|
44
|
+
| |- session.spec.json # session feature spec (1:1)
|
|
45
|
+
| \- password-reset.spec.json # password-reset feature spec (1:1)
|
|
46
|
+
|- payment/
|
|
47
|
+
| |- main.contract.json
|
|
48
|
+
| |- checkout.spec.json # checkout feature spec (1:1)
|
|
49
|
+
| \- refund.spec.json # refund feature spec (1:1)
|
|
50
|
+
\- shared/
|
|
51
|
+
\- auth-session.spec.json # shared session infrastructure
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- Contract: `*.contract.json` - folder-based tree structure, with `main.contract.json` as the folder representative.
|
|
55
|
+
- Spec: `*.spec.json` - one file per feature, placed in the same folder as related Contract files. The filename is the feature key (`login.spec.json` -> feature key `login`).
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Document Model
|
|
60
|
+
|
|
61
|
+
### Contract (`.contract.json`)
|
|
62
|
+
|
|
63
|
+
A business-logic document that non-developers can read. AI must treat this file as **read-only**. If an update is needed, AI should only propose the change to the user.
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"lastModified": "YYYY-MM-DD",
|
|
68
|
+
"content": ["## Overview", "", "Markdown lines as string array", ...]
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`content` is a `string[]` where each element is one line of Markdown.
|
|
73
|
+
|
|
74
|
+
`content` fixed sections:
|
|
75
|
+
|
|
76
|
+
`Overview -> Domain Glossary -> Features/Capabilities -> User Roles/Actors -> Business Rules/Constraints -> Edge Cases`
|
|
77
|
+
|
|
78
|
+
### Spec (`.spec.json`)
|
|
79
|
+
|
|
80
|
+
A feature-level technical document derived from Contract. Each file represents exactly one feature. AI can create and update Spec files. Deletion requires user approval.
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"schemaVersion": 1,
|
|
85
|
+
"summary": "Login processing and session issuance",
|
|
86
|
+
"description": [
|
|
87
|
+
"Validates user credentials and issues JWT-based sessions.",
|
|
88
|
+
"Includes password retry limit and account lockout policy."
|
|
89
|
+
],
|
|
90
|
+
"acceptanceCriteria": [
|
|
91
|
+
"Valid email/password login returns a JWT token",
|
|
92
|
+
"5 wrong password attempts locks account for 30 minutes",
|
|
93
|
+
"Expired session request returns 401 response"
|
|
94
|
+
],
|
|
95
|
+
"lastModified": "YYYY-MM-DD",
|
|
96
|
+
"status": "draft | in-progress | done",
|
|
97
|
+
"sources": ["packages/api/src/application/auth/login.ts", "packages/api/src/application/auth/login.test.ts"],
|
|
98
|
+
"contracts": ["./main.contract.json"],
|
|
99
|
+
"dependsOnSpecs": ["./session.spec.json"],
|
|
100
|
+
"modules": {
|
|
101
|
+
"LoginService": "Handles login processing",
|
|
102
|
+
"SessionManager": "Manages sessions"
|
|
103
|
+
},
|
|
104
|
+
"interfaces": {
|
|
105
|
+
"LoginService.authenticate()": "Performs authentication",
|
|
106
|
+
"LoginService.validate()": "Validates input"
|
|
107
|
+
},
|
|
108
|
+
"dataFlow": [
|
|
109
|
+
"1. Client -> LoginService.validate(): validate email/password input",
|
|
110
|
+
"2. LoginService.validate() -> LoginService.authenticate(): pass validated credentials",
|
|
111
|
+
"3. LoginService.authenticate() -> Database: query user record and compare password hash",
|
|
112
|
+
"4. LoginService.authenticate() -> SessionManager: request session creation on auth success",
|
|
113
|
+
"5. SessionManager -> Client: return Access Token + Refresh Token"
|
|
114
|
+
],
|
|
115
|
+
"errorHandling": {
|
|
116
|
+
"InvalidCredentialsError": "Wrong password",
|
|
117
|
+
"AccountLockedError": "Account locked after 5 retries"
|
|
118
|
+
},
|
|
119
|
+
"constraints": ["Session timeout: 30 min", "Login retry limit: 5 attempts"]
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
| Field | Type | Required | Description |
|
|
124
|
+
|---|---|---|---|
|
|
125
|
+
| `schemaVersion` | `number` | Y | Schema version |
|
|
126
|
+
| `summary` | `string` | Y | One-line feature summary |
|
|
127
|
+
| `description` | `string[]` | Y | Detailed feature description |
|
|
128
|
+
| `acceptanceCriteria` | `string[]` | Y | Completion criteria (verifiable conditions) |
|
|
129
|
+
| `lastModified` | `string` | Y | Last modified date (YYYY-MM-DD) |
|
|
130
|
+
| `status` | `string` | Y | `"draft"` / `"in-progress"` / `"done"` |
|
|
131
|
+
| `sources` | `string[]` | Y | Implementation/test files (relative to project root) |
|
|
132
|
+
| `contracts` | `string[]` | Y | Referenced Contract files (relative to Spec file) |
|
|
133
|
+
| `dependsOnSpecs` | `string[]` | N | Dependent Spec files (relative to Spec file) |
|
|
134
|
+
| `modules` | `Record<string, string>` | Y | Module structure (key: module name, value: role) |
|
|
135
|
+
| `interfaces` | `Record<string, string>` | Y | Functions/APIs (key: function name, value: description) |
|
|
136
|
+
| `dataFlow` | `string[]` | Y | Inter-module data flow |
|
|
137
|
+
| `errorHandling` | `Record<string, string>` | Y | Error handling (key: error name, value: trigger condition) |
|
|
138
|
+
| `constraints` | `string[]` | Y | Technical constraints |
|
|
139
|
+
|
|
140
|
+
**Empty section notation**: `string[]` -> `[]`, `Record<string, string>` -> `{}`
|
|
141
|
+
|
|
142
|
+
**Spec is higher authority than code.** Code must always follow the confirmed Spec. If Spec and code conflict, code is wrong.
|
|
143
|
+
|
|
144
|
+
### Contract-Spec linking
|
|
145
|
+
|
|
146
|
+
Contract is not extended as a structural source of feature keys. **Spec references Contract unidirectionally.**
|
|
147
|
+
|
|
148
|
+
1. **Spec filename = feature key**: `login.spec.json` -> `login`
|
|
149
|
+
2. **`contracts` field** points to referenced Contract files
|
|
150
|
+
3. **`summary`/`description`** describes which Contract feature this Spec implements (human-readable)
|
|
151
|
+
4. Duplicate feature keys within the same Contract boundary are not allowed
|
|
152
|
+
5. Renaming a Spec file = changing the feature key
|
|
153
|
+
|
|
154
|
+
### `status` field
|
|
155
|
+
|
|
156
|
+
| Value | Meaning | Transition condition |
|
|
157
|
+
|---|---|---|
|
|
158
|
+
| `draft` | Spec is being written, not confirmed yet | Initial state |
|
|
159
|
+
| `in-progress` | Spec confirmed, implementation in progress | After all Spec sections are confirmed |
|
|
160
|
+
| `done` | Implementation complete, consistency validation passed, all `acceptanceCriteria` met | After code passes consistency check against Spec |
|
|
161
|
+
|
|
162
|
+
**Regression**: When `sources`, `contracts`, `dependsOnSpecs`, or `acceptanceCriteria` change on a `done` Spec, `status` reverts to `in-progress`.
|
|
163
|
+
|
|
164
|
+
### `acceptanceCriteria` field
|
|
165
|
+
|
|
166
|
+
Conditions that must be met for this Spec's implementation to be considered "done". AI uses these as a checklist during consistency validation.
|
|
167
|
+
|
|
168
|
+
**Authoring rules**:
|
|
169
|
+
- Each item must be a verifiable, specific condition. Vague expressions like "should work well" are not allowed.
|
|
170
|
+
- Include conditions derived from Contract's business rules and Edge Cases.
|
|
171
|
+
- Conditions derived from `constraints` and `errorHandling` may also be included.
|
|
172
|
+
- Recommended format: "When X, then Y" (input-result).
|
|
173
|
+
|
|
174
|
+
**Validation usage**: When transitioning `status` to `"done"`, AI verifies all items are satisfied in code. If any item is unmet, `"done"` transition is blocked.
|
|
175
|
+
|
|
176
|
+
### Spec detail level
|
|
177
|
+
|
|
178
|
+
- Include: module structure, file/class responsibilities, function/API names with short descriptions, inter-module data flow.
|
|
179
|
+
- Exclude: internal implementation logic, algorithm details, variable names, code snippets.
|
|
180
|
+
|
|
181
|
+
### Reference rules
|
|
182
|
+
|
|
183
|
+
- `contracts` field: relative path from the Spec file (e.g. `"./payment.contract.json"`).
|
|
184
|
+
- `sources` field: relative path from the project root (e.g. `"packages/api/src/application/auth/login.ts"`).
|
|
185
|
+
- `dependsOnSpecs` field: relative path from the Spec file (e.g. `"../shared/auth-session.spec.json"`).
|
|
186
|
+
|
|
187
|
+
### `lastModified` update rule
|
|
188
|
+
|
|
189
|
+
Whenever any Spec field changes, update `lastModified` to today's date.
|
|
190
|
+
|
|
191
|
+
### Change history tracking
|
|
192
|
+
|
|
193
|
+
Spec files do not store history internally. Git handles it.
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
git log -- packages/api/contract/auth/login.spec.json
|
|
197
|
+
git log --follow -- packages/api/contract/auth/login.spec.json # track renames
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Development Process
|
|
203
|
+
|
|
204
|
+
All processes follow Waterfall. Each stage starts only after the previous stage is complete. If you need to change a previous stage artifact, go back, update the document first, then re-run downstream stages.
|
|
205
|
+
|
|
206
|
+
### 1. New feature development
|
|
207
|
+
|
|
208
|
+
```text
|
|
209
|
+
Contract review -> Spec authoring/fix -> Code implementation -> Test authoring/execution -> Consistency validation
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Step 1: Contract review**
|
|
213
|
+
- Read related `.contract.json` files and identify business requirements for the target feature.
|
|
214
|
+
- Confirm the feature is defined under Contract `Features/Capabilities`.
|
|
215
|
+
- If not defined, propose a Contract update to the user. Continue only after Contract is updated.
|
|
216
|
+
|
|
217
|
+
**Step 2: Spec authoring/fix**
|
|
218
|
+
- Create `{feature-key}.spec.json` in the same folder as related Contract files.
|
|
219
|
+
- Set `status` to `"draft"`.
|
|
220
|
+
- Fill `contracts` with relative paths to base Contract files.
|
|
221
|
+
- Fill `summary` and `description` to clearly state which Contract feature this Spec implements.
|
|
222
|
+
- Fill all structured fields (`modules`, `interfaces`, `dataFlow`, `errorHandling`, `constraints`).
|
|
223
|
+
- Define `acceptanceCriteria` with verifiable completion conditions.
|
|
224
|
+
- Add planned implementation file paths to `sources`.
|
|
225
|
+
- **All fields must be confirmed in this step.** After confirmation, set `status` to `"in-progress"` and continue.
|
|
226
|
+
|
|
227
|
+
**Step 3: Code implementation**
|
|
228
|
+
- Implement exactly following the confirmed module structure and interfaces defined in Spec.
|
|
229
|
+
- If a better structure appears during implementation, do not change code first. Go back to Step 2, update Spec first, then implement against the updated Spec.
|
|
230
|
+
- If new files are added or plans change, update `sources` in Spec first.
|
|
231
|
+
|
|
232
|
+
**Step 4: Test authoring and execution**
|
|
233
|
+
- Write tests for implemented code.
|
|
234
|
+
- Add test file paths to `sources`.
|
|
235
|
+
- Run tests and confirm they pass.
|
|
236
|
+
|
|
237
|
+
**Step 5: Consistency validation**
|
|
238
|
+
- Validate that implemented code follows the confirmed Spec exactly.
|
|
239
|
+
- Check whether `modules`, `interfaces`, and `dataFlow` match Spec.
|
|
240
|
+
- Verify all `acceptanceCriteria` items are satisfied in code.
|
|
241
|
+
- **If mismatch exists, fix code.** Spec should not be changed to match code.
|
|
242
|
+
- After all validations pass, set `status` to `"done"` and update `lastModified` to today.
|
|
243
|
+
|
|
244
|
+
### 2. Existing code changes
|
|
245
|
+
|
|
246
|
+
```text
|
|
247
|
+
Impact analysis -> Contract/Spec review -> Spec update/fix -> Code update -> Test execution -> Consistency validation
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Step 1: Impact analysis**
|
|
251
|
+
- Find all Spec files whose `sources` include the target files.
|
|
252
|
+
- Check `contracts` and `dependsOnSpecs` in those Specs to identify chained impact scope.
|
|
253
|
+
|
|
254
|
+
**Step 2: Contract/Spec review**
|
|
255
|
+
- Read related Specs to understand current module structure and interfaces.
|
|
256
|
+
- Read Contract as well to ensure the change does not violate business rules.
|
|
257
|
+
- If the change conflicts with Contract business rules, notify the user and continue only after Contract update.
|
|
258
|
+
|
|
259
|
+
**Step 3: Spec update/fix**
|
|
260
|
+
- Determine whether the change affects Spec scope.
|
|
261
|
+
- Interface changes, module add/remove, data flow changes -> Spec update is required.
|
|
262
|
+
- Internal-only changes (refactoring, performance tuning) -> Spec update may be unnecessary, but verify `modules` is still accurate.
|
|
263
|
+
- If Spec update is required, update and confirm Spec first.
|
|
264
|
+
- If files are added/removed, update `sources`.
|
|
265
|
+
- Update `acceptanceCriteria` if completion conditions have changed.
|
|
266
|
+
- Set `status` to `"in-progress"`.
|
|
267
|
+
- **Continue only after Spec is confirmed.**
|
|
268
|
+
|
|
269
|
+
**Step 4: Code update**
|
|
270
|
+
- Update code within confirmed Spec scope.
|
|
271
|
+
- If a better structure appears, do not change code first. Go back to Step 3 and update Spec first.
|
|
272
|
+
|
|
273
|
+
**Step 5: Test execution**
|
|
274
|
+
- Confirm existing tests pass.
|
|
275
|
+
- Add/update tests according to the change.
|
|
276
|
+
|
|
277
|
+
**Step 6: Consistency validation**
|
|
278
|
+
- Validate that updated code follows confirmed Spec exactly.
|
|
279
|
+
- Verify all `acceptanceCriteria` items are satisfied.
|
|
280
|
+
- **If mismatch exists, fix code.**
|
|
281
|
+
- After all validations pass, set `status` to `"done"` and update `lastModified` to today.
|
|
282
|
+
|
|
283
|
+
### 3. Bug fixes
|
|
284
|
+
|
|
285
|
+
```text
|
|
286
|
+
Bug analysis -> Related Spec/Contract review -> Spec update/fix (if needed) -> Code fix -> Tests -> Consistency validation
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**Step 1: Bug analysis**
|
|
290
|
+
- Identify root cause and related source files.
|
|
291
|
+
|
|
292
|
+
**Step 2: Related Spec/Contract review**
|
|
293
|
+
- Find Spec files whose `sources` include affected files.
|
|
294
|
+
- Classify root cause:
|
|
295
|
+
- Business rule violation -> verify Spec and code against Contract.
|
|
296
|
+
- Implementation bug -> check Spec `errorHandling` and Contract `Edge Cases`.
|
|
297
|
+
- Spec defect -> Spec failed to represent Contract correctly.
|
|
298
|
+
|
|
299
|
+
**Step 3: Spec update/fix (if needed)**
|
|
300
|
+
- If the bug is a missing technical case in Spec `errorHandling` or `constraints`, update those fields first.
|
|
301
|
+
- If the bug is a missing business case in Contract `Edge Cases`, propose Contract update to user. After Contract update, update Spec.
|
|
302
|
+
- Add missing conditions to `acceptanceCriteria` if applicable.
|
|
303
|
+
- Set `status` to `"in-progress"`.
|
|
304
|
+
- **Continue only after Spec is confirmed.**
|
|
305
|
+
|
|
306
|
+
**Step 4: Code fix**
|
|
307
|
+
- Fix code according to confirmed Spec.
|
|
308
|
+
|
|
309
|
+
**Step 5: Tests**
|
|
310
|
+
- Add a reproducing test case for the bug.
|
|
311
|
+
- Confirm tests pass after the fix.
|
|
312
|
+
- Confirm existing tests are not broken.
|
|
313
|
+
|
|
314
|
+
**Step 6: Consistency validation**
|
|
315
|
+
- Validate that fixed code follows confirmed Spec exactly.
|
|
316
|
+
- Verify all `acceptanceCriteria` items are satisfied.
|
|
317
|
+
- **If mismatch exists, fix code.**
|
|
318
|
+
- After all validations pass, set `status` to `"done"` and update `lastModified` to today.
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Contract/Spec Authoring Guide
|
|
323
|
+
|
|
324
|
+
### Contract authoring principles
|
|
325
|
+
|
|
326
|
+
- Use language that non-developers can understand.
|
|
327
|
+
- Do not include code, technical jargon, or implementation details.
|
|
328
|
+
- Define project-specific terms in `Domain Glossary`.
|
|
329
|
+
- Each item in `Features/Capabilities` must include clear business rules.
|
|
330
|
+
- `Business Rules/Constraints` should contain only global rules that cross individual features.
|
|
331
|
+
- `Edge Cases` should define business-level boundary conditions and decisions.
|
|
332
|
+
|
|
333
|
+
### Spec authoring principles
|
|
334
|
+
|
|
335
|
+
- `summary` must state the feature in one line. `description` provides detailed explanation.
|
|
336
|
+
- `summary`/`description` must make it clear which Contract feature this Spec implements.
|
|
337
|
+
- `modules` and `interfaces` use `Record<string, string>` format (key: name, value: description).
|
|
338
|
+
- In `interfaces`, include only function/API names and short descriptions (no signatures or implementation logic).
|
|
339
|
+
- `dataFlow` and `constraints` use `string[]` format.
|
|
340
|
+
- `errorHandling` uses `Record<string, string>` format (key: error name, value: trigger condition).
|
|
341
|
+
- `acceptanceCriteria` must contain verifiable, specific conditions.
|
|
342
|
+
- `sources` must list all related implementation and test files.
|
|
343
|
+
- `contracts` must list relative paths to base Contract files.
|
|
344
|
+
- Empty sections: `[]` for `string[]` fields, `{}` for `Record<string, string>` fields.
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## CDD CLI (`@sonamu-kit/cdd`)
|
|
349
|
+
|
|
350
|
+
The `cdd` CLI tool automates CDD workflow tasks. Run via `pnpm cdd <command>`.
|
|
351
|
+
|
|
352
|
+
### Commands
|
|
353
|
+
|
|
354
|
+
| Command | Description |
|
|
355
|
+
|---|---|
|
|
356
|
+
| `cdd init [dir]` | Initialize a CDD project (creates `contract/`, `main.contract.json`, `cdd.md`) |
|
|
357
|
+
| `cdd tree` | Display Contract/Spec tree grouped by domain with status colors |
|
|
358
|
+
| `cdd status` | Show project dashboard (Contract/Spec counts, status breakdown) |
|
|
359
|
+
| `cdd status <file>` | Spec/Contract status with relationship info (contracts, deps, dependents) |
|
|
360
|
+
| `cdd validate` | Verify schema/path/reference integrity (file existence, path resolution, required fields) |
|
|
361
|
+
| `cdd impact <file>` | Analyze source file change impact (direct Specs, chain Contracts, indirect Specs) |
|
|
362
|
+
| `cdd check` | Verify Code-Spec-Contract consistency + `acceptanceCriteria` fulfillment |
|
|
363
|
+
| `cdd spec create <n>` | Create a Spec template. Requires `--domain <n>` or `--contract <path>` |
|
|
364
|
+
| `cdd spec set-status <spec> <status>` | Change Spec status |
|
|
365
|
+
| `cdd spec list` | List Specs. Filters: `--status`, `--domain`, `--contract` |
|
|
366
|
+
| `cdd spec get <spec>` | Show full Spec or a specific field (`--field`) |
|
|
367
|
+
| `cdd spec set <spec>` | Update a Spec field (`--field`, `--value`, `--json`) |
|
|
368
|
+
| `cdd spec add <spec>` | Add an item to an array/map field (`--field`, `--value`, `--key`) |
|
|
369
|
+
| `cdd spec remove <spec>` | Remove an item from an array/map field (`--field`, `--index`/`--value`/`--key`) |
|
|
370
|
+
| `cdd spec blame <feature>` | Contributor analysis per Spec (ownership, score, AI role summary) |
|
|
371
|
+
| `cdd spec log <feature>` | Change timeline grouped by time period and author |
|
|
372
|
+
| `cdd spec explain <feature>` | AI-powered diff analysis: what changed, why, and impact level |
|
|
373
|
+
| `cdd source blame <file>` | Contributor analysis per source file |
|
|
374
|
+
| `cdd source log <file>` | Source file change timeline grouped by time period and author |
|
|
375
|
+
| `cdd source explain <file>` | AI-powered source file diff analysis |
|
|
376
|
+
|
|
377
|
+
### Common Options
|
|
378
|
+
|
|
379
|
+
- `--cwd <dir>` : Set working directory (default: current directory)
|
|
380
|
+
- `--raw` / `--json` : Force raw JSON output (auto-enabled in pipe/CI environments)
|
|
381
|
+
- `-h, --help` : Show help
|
|
382
|
+
|
|
383
|
+
### Git + AI Options (blame, log, explain)
|
|
384
|
+
|
|
385
|
+
- `--since=<date>` : Start date filter (ISO 8601, e.g. `2025-01-01`)
|
|
386
|
+
- `--until=<date>` : End date filter (ISO 8601, default: HEAD)
|
|
387
|
+
- `--group-by=day|week|month` : Grouping interval for `spec log` / `source log` (default: `day`)
|
|
388
|
+
- `--commit=<hash>` : Analyze a single commit for `spec explain` / `source explain`
|
|
389
|
+
|
|
390
|
+
AI uses `claude --model haiku` via local CLI.
|
|
391
|
+
|
|
392
|
+
### Usage Examples
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
# List in-progress specs
|
|
396
|
+
pnpm cdd spec list --status in-progress
|
|
397
|
+
|
|
398
|
+
# Show full spec
|
|
399
|
+
pnpm cdd spec get signin
|
|
400
|
+
|
|
401
|
+
# Update a field
|
|
402
|
+
pnpm cdd spec set signin --field summary --value "Updated summary"
|
|
403
|
+
|
|
404
|
+
# Add a constraint
|
|
405
|
+
pnpm cdd spec add signin --field constraints --value "New constraint"
|
|
406
|
+
|
|
407
|
+
# Contributor analysis for a spec
|
|
408
|
+
pnpm cdd spec blame signin
|
|
409
|
+
|
|
410
|
+
# Weekly changelog for a spec
|
|
411
|
+
pnpm cdd spec log signin --group-by=week
|
|
412
|
+
|
|
413
|
+
# Explain changes in a date range
|
|
414
|
+
pnpm cdd spec explain signin --since=2025-03-01
|
|
415
|
+
|
|
416
|
+
# JSON output for scripting
|
|
417
|
+
pnpm cdd spec list --raw | jq '.[].status'
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## Edge Cases
|
|
423
|
+
|
|
424
|
+
| Situation | Handling |
|
|
425
|
+
|---|---|
|
|
426
|
+
| Feature rename | `git mv` to rename Spec file |
|
|
427
|
+
| Feature split | Keep/delete existing file + create new files, in the same commit |
|
|
428
|
+
| Feature merge | Consolidate into one file, delete the rest, in the same commit |
|
|
429
|
+
| Feature removal | Confirm removal from Contract, delete Spec file with user approval |
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## Prohibitions
|
|
434
|
+
|
|
435
|
+
- AI must not modify Contract files without user request.
|
|
436
|
+
- AI must not delete Spec files without user approval.
|
|
437
|
+
- Do not write code without checking Contract and Spec first.
|
|
438
|
+
- Do not include implementation internals, algorithm details, or code snippets in Spec.
|
|
439
|
+
- **Do not start implementation (Entity design) before Spec is confirmed.**
|
|
440
|
+
- **If Spec and code conflict, do not change Spec to match code. Always fix code to match Spec.**
|
|
441
|
+
- **If a better approach appears during implementation, do not change code first. Update Spec first.**
|
|
@@ -460,56 +460,47 @@ parentId를 설정하면 자식 엔티티의 **BaseSchema에서 FK 컬럼이 제
|
|
|
460
460
|
|
|
461
461
|
## FK 참조 규칙 (FieldExpr)
|
|
462
462
|
|
|
463
|
-
BelongsToOne 관계를 정의하면 `{name}_id`
|
|
463
|
+
BelongsToOne 관계를 정의하면 `{name}_id` 컬럼이 자동 생성됩니다. **subsets에서는 `{name}.id` 형태(FieldExpr)로 참조**하고, **indexes에서는 실제 DB 컬럼명(`{name}_id`)을 사용**합니다.
|
|
464
464
|
|
|
465
465
|
### 적용 대상
|
|
466
466
|
|
|
467
|
-
| 위치 |
|
|
468
|
-
|
|
469
|
-
| subsets | `
|
|
470
|
-
| indexes |
|
|
471
|
-
| unique |
|
|
472
|
-
| search | `
|
|
467
|
+
| 위치 | 사용 형식 | 예시 |
|
|
468
|
+
|------|----------|------|
|
|
469
|
+
| subsets | FieldExpr (`relation.field`) | `"user.id"` |
|
|
470
|
+
| indexes | 실제 DB 컬럼명 | `"user_id"` |
|
|
471
|
+
| unique | 실제 DB 컬럼명 | `["user_id", "date"]` |
|
|
472
|
+
| search | FieldExpr (`relation.field`) | `"user.id"` |
|
|
473
473
|
|
|
474
474
|
### 예시
|
|
475
475
|
|
|
476
476
|
```json
|
|
477
|
-
// WRONG - "user_id"를 직접 사용하면 에러 발생
|
|
478
477
|
{
|
|
479
478
|
"id": "ApiLog",
|
|
480
479
|
"props": [
|
|
481
480
|
{ "type": "relation", "name": "user", "with": "User", "relationType": "BelongsToOne" }
|
|
482
481
|
],
|
|
483
482
|
"subsets": {
|
|
484
|
-
"A": ["id", "
|
|
483
|
+
"A": ["id", "user.id", "api_path"] // FieldExpr 사용
|
|
485
484
|
},
|
|
486
485
|
"indexes": [
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
{
|
|
493
|
-
"id": "ApiLog",
|
|
494
|
-
"props": [
|
|
495
|
-
{ "type": "relation", "name": "user", "with": "User", "relationType": "BelongsToOne" }
|
|
496
|
-
],
|
|
497
|
-
"subsets": {
|
|
498
|
-
"A": ["id", "user.id", "api_path"] // CORRECT: user.id
|
|
499
|
-
},
|
|
500
|
-
"indexes": [
|
|
501
|
-
["user.id"] // CORRECT: user.id
|
|
486
|
+
{
|
|
487
|
+
"type": "index",
|
|
488
|
+
"name": "api_logs_user_id_index",
|
|
489
|
+
"columns": [{ "name": "user_id" }]
|
|
490
|
+
}
|
|
502
491
|
]
|
|
503
492
|
}
|
|
504
493
|
```
|
|
505
494
|
|
|
506
|
-
### 에러 메시지
|
|
495
|
+
### subsets에서의 에러 메시지
|
|
507
496
|
|
|
508
497
|
```
|
|
509
498
|
Error: ApiLog -- 잘못된 FieldExpr 'user_id' (사용 가능한 props: id, created_at, ..., user)
|
|
510
499
|
```
|
|
511
500
|
|
|
512
|
-
이 에러가 보이면 `user_id` → `user.id`로 변경하세요.
|
|
501
|
+
subsets에서 이 에러가 보이면 `user_id` → `user.id`로 변경하세요. indexes에서는 `user_id`가 올바른 형식입니다.
|
|
502
|
+
|
|
503
|
+
> **참고:** indexes와 subsets의 참조 방식 차이에 대한 상세 설명은 `entity-basic.md`의 "IMPORTANT: indexes에서 FK 컬럼명은 실제 DB 컬럼명을 사용한다" 섹션을 참고하세요.
|
|
513
504
|
|
|
514
505
|
---
|
|
515
506
|
|
|
@@ -589,44 +589,51 @@ function ProfileForm() {
|
|
|
589
589
|
|
|
590
590
|
**IMPORTANT**: SonamuProvider에 uploader 함수 필수 설정 (아래 참조)
|
|
591
591
|
|
|
592
|
-
##
|
|
592
|
+
## Select (다중 선택 모드)
|
|
593
593
|
|
|
594
|
-
|
|
594
|
+
`Select` 컴포넌트에 `multiple: true`를 설정하면 다중 선택 모드로 동작합니다.
|
|
595
595
|
|
|
596
596
|
```typescript
|
|
597
|
-
import {
|
|
598
|
-
import type { MultiSelectOption } from "@sonamu-kit/react-components/components";
|
|
597
|
+
import { Select } from "@sonamu-kit/react-components/components";
|
|
599
598
|
|
|
600
599
|
function TagForm() {
|
|
601
600
|
const { register } = useTypeForm(PostSaveParams, {
|
|
602
601
|
tag_ids: [], // number[]
|
|
603
602
|
});
|
|
604
603
|
|
|
605
|
-
const
|
|
606
|
-
{
|
|
607
|
-
{
|
|
608
|
-
{
|
|
609
|
-
{
|
|
604
|
+
const items = [
|
|
605
|
+
{ value: 1, label: "JavaScript" },
|
|
606
|
+
{ value: 2, label: "TypeScript" },
|
|
607
|
+
{ value: 3, label: "React" },
|
|
608
|
+
{ value: 4, label: "Vue" },
|
|
610
609
|
];
|
|
611
610
|
|
|
612
611
|
return (
|
|
613
|
-
<
|
|
612
|
+
<Select
|
|
614
613
|
{...register("tag_ids")}
|
|
615
|
-
|
|
614
|
+
items={items}
|
|
615
|
+
multiple
|
|
616
616
|
placeholder="태그 선택"
|
|
617
|
-
emptyIndicator={<span>태그가 없습니다</span>}
|
|
618
617
|
/>
|
|
619
618
|
);
|
|
620
619
|
}
|
|
621
620
|
```
|
|
622
621
|
|
|
623
|
-
|
|
622
|
+
**다중 선택 전용 Props:**
|
|
624
623
|
|
|
625
|
-
- `
|
|
626
|
-
- `groups`: 옵션 그룹화
|
|
624
|
+
- `multiple`: `true` (다중 선택 활성화)
|
|
627
625
|
- `maxCount`: 표시할 최대 배지 개수
|
|
628
|
-
- `
|
|
626
|
+
- `hideSelectAll`: 전체 선택 버튼 숨기기
|
|
627
|
+
- `searchable`: 검색 입력 활성화
|
|
628
|
+
|
|
629
|
+
**공통 Props:**
|
|
630
|
+
|
|
631
|
+
- `items`: `SelectItemDef[]` (값만 또는 `{ value, label, disabled }` 형태)
|
|
632
|
+
- `placeholder`: 선택 전 표시 텍스트
|
|
633
|
+
- `clearable`: X 버튼으로 전체 해제
|
|
629
634
|
- `disabled`: 비활성화
|
|
635
|
+
- `renderItem`: 커스텀 렌더링 함수
|
|
636
|
+
- `async`: `true` 설정 시 `onSearch` 콜백으로 비동기 검색 지원
|
|
630
637
|
|
|
631
638
|
## EnumSelect
|
|
632
639
|
|
|
@@ -672,6 +672,7 @@ if (params.search === "id") {
|
|
|
672
672
|
- [ ] debug 옵션 불필요하게 명시하지 않음
|
|
673
673
|
- [ ] orderBy 모든 케이스 exhaustive 처리
|
|
674
674
|
- [ ] ManyToMany relation이 있으면 _ids 배열 SaveParams에 추가
|
|
675
|
+
- [ ] `@upload` 메서드에 `@api`가 붙어 있지 않은가? (`@upload`는 단독 사용, 함께 쓰면 빌드 에러)
|
|
675
676
|
- [ ] SearchField enum과 findMany 구현이 일치하는가?
|
|
676
677
|
- [ ] 승인 워크플로우 엔티티에 status/type 필터가 ListParams와 findMany 양쪽에 모두 있는가?
|
|
677
678
|
|
|
@@ -85,11 +85,57 @@ description: Sonamu 전체 개발 워크플로우. 프로젝트 생성부터 Fro
|
|
|
85
85
|
|
|
86
86
|
---
|
|
87
87
|
|
|
88
|
+
## PHASE 0.5: Contract 및 Spec 작성
|
|
89
|
+
|
|
90
|
+
**참조 스킬:** cdd.md
|
|
91
|
+
|
|
92
|
+
**CRITICAL: 이 PHASE가 완료되기 전에 엔티티 설계(PHASE 1)를 시작하지 않는다.**
|
|
93
|
+
|
|
94
|
+
### 7. main.contract.json 작성
|
|
95
|
+
|
|
96
|
+
20. `packages/api/contract/main.contract.json` 생성
|
|
97
|
+
- `requirements.md`, `business-logic.md` 기반으로 작성
|
|
98
|
+
- 전체 프로젝트 Overview, 하위 도메인 목록, Domain Glossary, User Roles, Business Rules, Edge Cases 포함
|
|
99
|
+
- `content`는 Markdown 한 줄씩 `string[]`로 작성
|
|
100
|
+
21. 사용자에게 보고 후 승인 대기
|
|
101
|
+
|
|
102
|
+
### 8. 도메인별 contract 작성
|
|
103
|
+
|
|
104
|
+
22. `business-logic.md`에서 도메인을 식별하고 도메인 목록을 사용자에게 확인받기
|
|
105
|
+
23. 도메인별로 `packages/api/contract/{domain}/main.contract.json` 작성
|
|
106
|
+
- 도메인 폴더명은 영문 소문자 (예: `auth`, `organization`, `research`)
|
|
107
|
+
- 각 도메인 contract 작성 후 사용자 확인받기 (도메인별로 하나씩)
|
|
108
|
+
- Features/Capabilities 섹션을 상세하게 작성한다 (이후 spec의 1:1 기반이 됨)
|
|
109
|
+
|
|
110
|
+
### 9. Spec 작성
|
|
111
|
+
|
|
112
|
+
**CRITICAL: 1 Feature = 1 Spec 파일. 도메인 contract의 Features/Capabilities에 정의된 기능 하나당 spec 파일 하나.**
|
|
113
|
+
|
|
114
|
+
24. 각 도메인의 Features/Capabilities를 기반으로 spec 파일 목록 작성 후 사용자 확인
|
|
115
|
+
25. 도메인별로 spec 파일 작성
|
|
116
|
+
- spec 파일은 해당 도메인 폴더에 위치
|
|
117
|
+
- 파일명은 feature key (예: `signin.spec.json`)
|
|
118
|
+
- `status: "draft"`로 시작
|
|
119
|
+
26. 전체 spec 작성 완료 후 사용자에게 검토 요청
|
|
120
|
+
- 사용자 검토 및 수정 반영
|
|
121
|
+
- 모든 spec 검토 완료 후에만 다음 PHASE로 진행
|
|
122
|
+
|
|
123
|
+
**완료 기준:**
|
|
124
|
+
|
|
125
|
+
- [ ] `packages/api/contract/main.contract.json` 작성 및 사용자 승인
|
|
126
|
+
- [ ] 모든 도메인 contract 작성 및 사용자 승인
|
|
127
|
+
- [ ] 모든 domain spec 파일 작성 완료
|
|
128
|
+
- [ ] 사용자 spec 검토 완료
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
88
132
|
## PHASE 1: 엔티티 설계
|
|
89
133
|
|
|
90
134
|
**참조 스킬:** entity-basic.md, entity-relations.md
|
|
91
135
|
|
|
92
|
-
|
|
136
|
+
**전제 조건:** PHASE 0.5 완료 (모든 spec 사용자 검토 완료)
|
|
137
|
+
|
|
138
|
+
### 10. 엔티티 설계
|
|
93
139
|
|
|
94
140
|
20. 사용자 요구사항에 맞는 엔티티 설계
|
|
95
141
|
- 설계하면서 사용자에게 비즈니스 로직에 맞는지 **지속적으로 디테일하게** 확인받을 것
|
|
@@ -29,9 +29,9 @@ export class ParallelDBManager {
|
|
|
29
29
|
(_, i) => `${this.templateDb}_${i + 1}`,
|
|
30
30
|
);
|
|
31
31
|
|
|
32
|
-
// 1. 기존 연결 종료 (병렬)
|
|
32
|
+
// 1. 기존 연결 종료 (병렬) worker DB + template DB (PG 18에서 FILE_COPY 시 template에 exclusive access 필요)
|
|
33
33
|
await Promise.all(
|
|
34
|
-
workerDbNames.map((dbName) =>
|
|
34
|
+
[...workerDbNames, this.templateDb].map((dbName) =>
|
|
35
35
|
adminDb.raw(
|
|
36
36
|
`
|
|
37
37
|
SELECT pg_terminate_backend(pg_stat_activity.pid)
|