sonamu 0.8.25 → 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.
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/sonamu-ui/setting.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>{{projectName}}: Sonamu UI</title>
8
- <script type="module" crossorigin src="/sonamu-ui/assets/index-CxiydzeC.js"></script>
9
- <link rel="stylesheet" crossorigin href="/sonamu-ui/assets/index-BrQKU3j9.css">
8
+ <script type="module" crossorigin src="/sonamu-ui/assets/index-DK-2aacv.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/sonamu-ui/assets/index-CKo0Z2Iu.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sonamu",
3
- "version": "0.8.25",
3
+ "version": "0.8.26",
4
4
  "description": "Sonamu — TypeScript Fullstack API Framework",
5
5
  "keywords": [
6
6
  "typescript",
@@ -55,6 +55,10 @@
55
55
  "import": "./dist/filter/index.js",
56
56
  "types": "./dist/filter/index.d.ts"
57
57
  },
58
+ "./auth/plugins": {
59
+ "import": "./dist/auth/plugins/index.js",
60
+ "types": "./dist/auth/plugins/index.d.ts"
61
+ },
58
62
  "./cdd-types": {
59
63
  "import": "./dist/ui/cdd-types.js",
60
64
  "types": "./dist/ui/cdd-types.d.ts"
@@ -124,9 +128,9 @@
124
128
  "vite": "7.3.0",
125
129
  "vitest": "^4.0.10",
126
130
  "@sonamu-kit/hmr-hook": "^0.4.1",
127
- "@sonamu-kit/tasks": "^0.2.0",
128
131
  "@sonamu-kit/hmr-runner": "^0.1.1",
129
- "@sonamu-kit/ts-loader": "^2.1.3"
132
+ "@sonamu-kit/ts-loader": "^2.1.3",
133
+ "@sonamu-kit/tasks": "^0.2.0"
130
134
  },
131
135
  "devDependencies": {
132
136
  "@biomejs/biome": "^2.3.13",
@@ -26,7 +26,7 @@ export const sonamuKnexAdapter = () => {
26
26
  const createCustomAdapter = (
27
27
  getDb: () => Knex | Knex.Transaction,
28
28
  ): AdapterFactoryCustomizeAdapterCreator => {
29
- return () => ({
29
+ return ({ getFieldName }) => ({
30
30
  create: async ({ model, data }) => {
31
31
  const [row] = await getDb()(model).insert(data).returning("*");
32
32
  return row;
@@ -45,7 +45,8 @@ export const sonamuKnexAdapter = () => {
45
45
  query = applyWhere(query, where);
46
46
  }
47
47
  if (sortBy) {
48
- query = query.orderBy(sortBy.field, sortBy.direction);
48
+ const dbField = getFieldName({ model, field: sortBy.field });
49
+ query = query.orderBy(dbField, sortBy.direction);
49
50
  }
50
51
  if (limit) {
51
52
  query = query.limit(limit);
@@ -63,7 +63,7 @@ function getApiKey(): string {
63
63
  try {
64
64
  // Sonamu가 초기화되어 있는 경우
65
65
  const { Sonamu } = require("../api");
66
- apiKey = Sonamu.secret?.anthropic_api_key;
66
+ apiKey = Sonamu.secrets?.anthropic_api_key;
67
67
  } catch {
68
68
  // Sonamu가 초기화되지 않은 경우 (테스트 등)
69
69
  apiKey = undefined;
@@ -294,7 +294,7 @@ async function callAnthropicAPI(
294
294
  });
295
295
 
296
296
  const { text, usage } = await generateText({
297
- model: anthropic("claude-sonnet-4-5"),
297
+ model: anthropic("claude-sonnet-4-6"),
298
298
  prompt,
299
299
  });
300
300
 
@@ -160,6 +160,51 @@ describe("LoadersResult", () => {
160
160
  }>();
161
161
  });
162
162
 
163
+ it("중첩 로더의 as 경로도 hydrate한다", () => {
164
+ type IngredientLoaderQb = (
165
+ qbWrapper: PuriWrapper<DatabaseSchemaExtend>,
166
+ fromIds: number[] | string[],
167
+ ) => MockPuri<{ id: number; name: string; refId: number }>;
168
+
169
+ type PrescriptionItemLoaderQb = (
170
+ qbWrapper: PuriWrapper<DatabaseSchemaExtend>,
171
+ fromIds: number[] | string[],
172
+ ) => MockPuri<{
173
+ id: number;
174
+ test__id: number;
175
+ test__name: string;
176
+ refId: number;
177
+ }>;
178
+
179
+ type Loaders = [
180
+ {
181
+ as: "items";
182
+ refId: "id";
183
+ qb: PrescriptionItemLoaderQb;
184
+ loaders: [
185
+ {
186
+ as: "test__items";
187
+ refId: "test__id";
188
+ qb: IngredientLoaderQb;
189
+ },
190
+ ];
191
+ },
192
+ ];
193
+ type Result = LoadersResult<Loaders>;
194
+
195
+ const result = {} as Result;
196
+ expectTypeOf(result).toEqualTypeOf<{
197
+ items: {
198
+ id: number;
199
+ test: {
200
+ id: number;
201
+ name: string;
202
+ items: { id: number; name: string }[];
203
+ };
204
+ }[];
205
+ }>();
206
+ });
207
+
163
208
  it("여러 로더를 처리한다", () => {
164
209
  type ProjectLoader = {
165
210
  as: "projects";
@@ -351,6 +396,63 @@ describe("InferAllSubsets", () => {
351
396
  };
352
397
  }>();
353
398
  });
399
+
400
+ it("중첩 로더 경로 alias를 포함한 Subset 결과를 추론한다", () => {
401
+ type SubsetFnA = (qbWrapper: PuriWrapper<DatabaseSchemaExtend>) => MockPuri<{
402
+ id: number;
403
+ root__id: number;
404
+ }>;
405
+ type SubsetQueries = {
406
+ A: SubsetFnA;
407
+ };
408
+
409
+ type RootItemLoader = {
410
+ as: "root__items";
411
+ refId: "root__id";
412
+ qb: (
413
+ qbWrapper: PuriWrapper<DatabaseSchemaExtend>,
414
+ fromIds: number[] | string[],
415
+ ) => MockPuri<{
416
+ id: number;
417
+ test__id: number;
418
+ test__name: string;
419
+ refId: number;
420
+ }>;
421
+ loaders: [
422
+ {
423
+ as: "test__inner_items";
424
+ refId: "test__id";
425
+ qb: (
426
+ qbWrapper: PuriWrapper<DatabaseSchemaExtend>,
427
+ fromIds: number[] | string[],
428
+ ) => MockPuri<{ id: number; name: string; refId: number }>;
429
+ },
430
+ ];
431
+ };
432
+ type LoaderQueries = {
433
+ A: [RootItemLoader];
434
+ };
435
+
436
+ type Result = InferAllSubsets<SubsetQueries, LoaderQueries>;
437
+
438
+ const result = {} as Result;
439
+ expectTypeOf(result).toEqualTypeOf<{
440
+ A: {
441
+ id: number;
442
+ root: {
443
+ id: number;
444
+ items: {
445
+ id: number;
446
+ test: {
447
+ id: number;
448
+ name: string;
449
+ inner_items: { id: number; name: string }[];
450
+ };
451
+ }[];
452
+ };
453
+ };
454
+ }>();
455
+ });
354
456
  });
355
457
 
356
458
  describe("서브셋이 여러 개인 경우", () => {
@@ -134,7 +134,7 @@ export type LoadersResult<TLoaders extends GenericPuriLoader[]> = Expand<{
134
134
  * 기본 결과와 Loader 결과 병합
135
135
  */
136
136
  type WithLoaders<TBase, TLoaders> = TLoaders extends GenericPuriLoader[]
137
- ? Expand<TBase & LoadersResult<TLoaders>>
137
+ ? Expand<Hydrate<TBase & LoadersResult<TLoaders>>>
138
138
  : TBase;
139
139
 
140
140
  // ============================================================================
@@ -187,6 +187,49 @@ Documents are persisted as files, so project context is preserved even when the
187
187
  | **Troubleshooting → skill contribution** | **skill-contribution** |
188
188
  | **Framework bug / constraint response** | **framework-change** |
189
189
 
190
+ ## Skill Selection by CDD Claim Type
191
+
192
+ **For Planners:** Use this table to populate `required_skills` in each Claim blueprint.
193
+
194
+ ### `surface` — Entity / Schema / Shared Type Work
195
+
196
+ | Case | Required Skills |
197
+ |------|----------------|
198
+ | Add new entity | `entity-basic`, `entity-relations`, `migration`, `cone`, `fixture-cli`, `i18n` |
199
+ | Define subset | `entity-basic`, `subset` |
200
+ | Add auth entity | `auth`, `auth-migration` |
201
+ | Add auth plugin | `auth-plugins` |
202
+ | Add batch save structure | `upsert` |
203
+ | Add vector search structure | `vector`, `entity-basic` |
204
+ | Resolve scaffolding errors | `scaffolding` |
205
+
206
+ ### `test` — Test Writing
207
+
208
+ | Case | Required Skills |
209
+ |------|----------------|
210
+ | Business logic tests | `testing`, `testing-devrunner` |
211
+ | Tests requiring fixtures | `testing`, `fixture-cli` |
212
+ | Debugging / tracing | `naite` |
213
+
214
+ ### `implement` — Business Logic Implementation
215
+
216
+ | Case | Required Skills |
217
+ |------|----------------|
218
+ | Model CRUD implementation | `model`, `puri` |
219
+ | API endpoint implementation | `api`, `model` |
220
+ | File upload implementation | `api`, `framework-change` |
221
+ | SQL query writing | `puri` |
222
+ | Internationalization | `i18n` |
223
+ | Auth guard / session handling | `auth` |
224
+ | Auth plugin implementation | `auth-plugins` |
225
+ | Batch save implementation | `upsert`, `model` |
226
+ | AI Agent implementation | `ai-agents` |
227
+ | Background job / scheduling | `tasks` |
228
+ | Vector search implementation | `vector`, `puri` |
229
+ | Frontend integration | `frontend`, `scaffolding` |
230
+
231
+ ---
232
+
190
233
  ## Command Execution Path
191
234
 
192
235
  All `pnpm` commands are run from the **`packages/api`** directory.
@@ -57,7 +57,7 @@ export class FixtureGenerator {
57
57
  locale: options?.locale || "ko",
58
58
  useLLM: options?.useLLM || false,
59
59
  enableLLMCache: options?.enableLLMCache !== false,
60
- llmModel: options?.llmModel || "claude-sonnet-4-5",
60
+ llmModel: options?.llmModel || "claude-sonnet-4-6",
61
61
  };
62
62
  }
63
63
 
@@ -825,7 +825,7 @@ export class FixtureGenerator {
825
825
  const { generateText } = await import("ai");
826
826
 
827
827
  const rowResponse = await generateText({
828
- model: createAnthropic({ apiKey })(this.options.llmModel || "claude-sonnet-4-5"),
828
+ model: createAnthropic({ apiKey })(this.options.llmModel || "claude-sonnet-4-6"),
829
829
  prompt: this.buildRowLLMPrompt(llmProps, entity),
830
830
  });
831
831
  if (!rowResponse || typeof rowResponse.text !== "string") {
@@ -869,7 +869,7 @@ export class FixtureGenerator {
869
869
  const { generateText } = await import("ai");
870
870
 
871
871
  const singleResponse = await generateText({
872
- model: createAnthropic({ apiKey })(this.options.llmModel || "claude-sonnet-4-5"),
872
+ model: createAnthropic({ apiKey })(this.options.llmModel || "claude-sonnet-4-6"),
873
873
  prompt: this.buildLLMPrompt(fixtureHint, prop, entity),
874
874
  });
875
875
  if (!singleResponse || typeof singleResponse.text !== "string") {
@@ -1208,7 +1208,7 @@ Rules:
1208
1208
 
1209
1209
  try {
1210
1210
  const { Sonamu } = require("../api");
1211
- apiKey = Sonamu.secret?.anthropic_api_key;
1211
+ apiKey = Sonamu.secrets?.anthropic_api_key;
1212
1212
  } catch {
1213
1213
  // Sonamu가 초기화되지 않은 경우 (테스트 환경 등)
1214
1214
  }
@@ -34,7 +34,7 @@ class AIClient {
34
34
  const { anthropic } = await import("@ai-sdk/anthropic");
35
35
  const aiModule = await import("ai");
36
36
  this.aiSdk = { ...aiModule, anthropic };
37
- this.model = anthropic("claude-sonnet-4-5");
37
+ this.model = anthropic("claude-sonnet-4-6");
38
38
  } catch (error) {
39
39
  console.warn(
40
40
  "AI SDK packages not installed. Install @ai-sdk/anthropic and ai to use AI features.",
package/src/ui/api.ts CHANGED
@@ -37,15 +37,16 @@ import {
37
37
  } from "../types/types";
38
38
  import { nonNullable } from "../utils/utils";
39
39
  import { setAiApi } from "./ai-api";
40
+ import type { CddAddRuleRequest } from "./cdd-service";
40
41
  import {
42
+ addRule,
41
43
  editContent,
42
- editSchema,
44
+ getAcList,
43
45
  getCddTree,
44
- getDashboard,
45
- listSchemas,
46
+ listRules,
46
47
  openSourceFile,
47
48
  readContent,
48
- readSchema,
49
+ readRule,
49
50
  } from "./cdd-service";
50
51
 
51
52
  export async function sonamuUIApiPlugin(fastify: FastifyInstance) {
@@ -1404,10 +1405,6 @@ export async function sonamuUIApiPlugin(fastify: FastifyInstance) {
1404
1405
  });
1405
1406
 
1406
1407
  // CDD API
1407
- server.get("/api/cdd/dashboard", async () => {
1408
- return getDashboard();
1409
- });
1410
-
1411
1408
  server.get("/api/cdd/tree", async () => {
1412
1409
  return getCddTree();
1413
1410
  });
@@ -1428,19 +1425,23 @@ export async function sonamuUIApiPlugin(fastify: FastifyInstance) {
1428
1425
  return { success: true };
1429
1426
  });
1430
1427
 
1431
- // CDD Schema API
1432
- server.get("/api/cdd/schemas", async () => {
1433
- return listSchemas();
1428
+ // CDD Rules API
1429
+ server.get("/api/cdd/rules", async () => {
1430
+ return listRules();
1431
+ });
1432
+
1433
+ server.post<{ Body: { ruleKey: string } }>("/api/cdd/readRule", async (request) => {
1434
+ const { ruleKey } = request.body;
1435
+ return readRule(ruleKey);
1434
1436
  });
1435
1437
 
1436
- server.post<{ Body: { schemaKey: string } }>("/api/cdd/readSchema", async (request) => {
1437
- const { schemaKey } = request.body;
1438
- return readSchema(schemaKey);
1438
+ server.post<{ Body: CddAddRuleRequest }>("/api/cdd/addRule", async (request) => {
1439
+ return addRule(request.body);
1439
1440
  });
1440
1441
 
1441
- server.post<{ Body: { schemaKey: string } }>("/api/cdd/editSchema", async (request) => {
1442
- const { schemaKey } = request.body;
1443
- return editSchema(schemaKey);
1442
+ // CDD AC API
1443
+ server.get("/api/cdd/ac", async () => {
1444
+ return getAcList();
1444
1445
  });
1445
1446
 
1446
1447
  // ui-web 빌드 파일 서빙