sonamu 0.7.11 → 0.7.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.
Files changed (153) hide show
  1. package/dist/api/config.d.ts +10 -6
  2. package/dist/api/config.d.ts.map +1 -1
  3. package/dist/api/config.js +2 -1
  4. package/dist/api/sonamu.d.ts +4 -0
  5. package/dist/api/sonamu.d.ts.map +1 -1
  6. package/dist/api/sonamu.js +49 -5
  7. package/dist/bin/cli.js +118 -170
  8. package/dist/database/base-model.d.ts +10 -50
  9. package/dist/database/base-model.d.ts.map +1 -1
  10. package/dist/database/base-model.js +19 -84
  11. package/dist/database/base-model.types.d.ts +4 -4
  12. package/dist/database/base-model.types.d.ts.map +1 -1
  13. package/dist/database/base-model.types.js +1 -1
  14. package/dist/database/db.d.ts +1 -0
  15. package/dist/database/db.d.ts.map +1 -1
  16. package/dist/database/db.js +24 -13
  17. package/dist/database/puri-subset.test-d.js +1 -1
  18. package/dist/database/puri-subset.types.d.ts +1 -0
  19. package/dist/database/puri-subset.types.d.ts.map +1 -1
  20. package/dist/database/puri-subset.types.js +2 -2
  21. package/dist/database/puri.d.ts +82 -3
  22. package/dist/database/puri.d.ts.map +1 -1
  23. package/dist/database/puri.js +180 -14
  24. package/dist/database/puri.types.d.ts +33 -6
  25. package/dist/database/puri.types.d.ts.map +1 -1
  26. package/dist/database/puri.types.js +1 -1
  27. package/dist/database/puri.types.test-d.js +1 -1
  28. package/dist/entity/entity-manager.d.ts +5 -4
  29. package/dist/entity/entity-manager.d.ts.map +1 -1
  30. package/dist/entity/entity-manager.js +8 -1
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +3 -3
  34. package/dist/migration/code-generation.d.ts.map +1 -1
  35. package/dist/migration/code-generation.js +33 -2
  36. package/dist/migration/postgresql-schema-reader.d.ts.map +1 -1
  37. package/dist/migration/postgresql-schema-reader.js +53 -22
  38. package/dist/naite/messaging-types.d.ts.map +1 -1
  39. package/dist/naite/messaging-types.js +1 -1
  40. package/dist/naite/naite.js +2 -2
  41. package/dist/stream/sse.d.ts +2 -6
  42. package/dist/stream/sse.d.ts.map +1 -1
  43. package/dist/stream/sse.js +9 -3
  44. package/dist/syncer/api-parser.d.ts.map +1 -1
  45. package/dist/syncer/api-parser.js +7 -2
  46. package/dist/syncer/file-patterns.d.ts +1 -1
  47. package/dist/syncer/file-patterns.d.ts.map +1 -1
  48. package/dist/syncer/file-patterns.js +6 -5
  49. package/dist/syncer/module-loader.d.ts +5 -0
  50. package/dist/syncer/module-loader.d.ts.map +1 -1
  51. package/dist/syncer/module-loader.js +17 -1
  52. package/dist/syncer/syncer.d.ts +5 -1
  53. package/dist/syncer/syncer.d.ts.map +1 -1
  54. package/dist/syncer/syncer.js +28 -19
  55. package/dist/tasks/decorator.d.ts +26 -0
  56. package/dist/tasks/decorator.d.ts.map +1 -0
  57. package/dist/tasks/decorator.js +28 -0
  58. package/dist/tasks/step-wrapper.d.ts +18 -0
  59. package/dist/tasks/step-wrapper.d.ts.map +1 -0
  60. package/dist/tasks/step-wrapper.js +38 -0
  61. package/dist/tasks/workflow-manager.d.ts +40 -0
  62. package/dist/tasks/workflow-manager.d.ts.map +1 -0
  63. package/dist/tasks/workflow-manager.js +193 -0
  64. package/dist/template/implementations/generated.template.d.ts.map +1 -1
  65. package/dist/template/implementations/generated.template.js +7 -3
  66. package/dist/types/types.d.ts +26 -10
  67. package/dist/types/types.d.ts.map +1 -1
  68. package/dist/types/types.js +15 -2
  69. package/dist/ui/ai-api.d.ts +1 -0
  70. package/dist/ui/ai-api.d.ts.map +1 -0
  71. package/dist/ui/ai-api.js +50 -0
  72. package/dist/ui/ai-client.d.ts +1 -0
  73. package/dist/ui/ai-client.d.ts.map +1 -0
  74. package/dist/ui/ai-client.js +438 -0
  75. package/dist/ui/api.d.ts +3 -0
  76. package/dist/ui/api.d.ts.map +1 -0
  77. package/dist/ui/api.js +680 -0
  78. package/dist/ui-web/assets/brand-icons-Cu_C0hZ4.svg +1008 -0
  79. package/dist/ui-web/assets/brand-icons-F3SPCeH1.woff +0 -0
  80. package/dist/ui-web/assets/brand-icons-XL9sxUpA.woff2 +0 -0
  81. package/dist/ui-web/assets/brand-icons-sqJ2Pg7a.eot +0 -0
  82. package/dist/ui-web/assets/brand-icons-ubhWoxly.ttf +0 -0
  83. package/dist/ui-web/assets/flags-DOLqOU7Y.png +0 -0
  84. package/dist/ui-web/assets/icons-BOCtAERH.woff +0 -0
  85. package/dist/ui-web/assets/icons-CHzK1VD9.eot +0 -0
  86. package/dist/ui-web/assets/icons-D29ZQHHw.ttf +0 -0
  87. package/dist/ui-web/assets/icons-Du6TOHnR.woff2 +0 -0
  88. package/dist/ui-web/assets/icons-RwhydX30.svg +1518 -0
  89. package/dist/ui-web/assets/index-CpaB9P6g.css +1 -0
  90. package/dist/ui-web/assets/index-J9MCfjCd.js +95 -0
  91. package/dist/ui-web/assets/outline-icons-BfdLr8tr.svg +366 -0
  92. package/dist/ui-web/assets/outline-icons-DD8jm0uy.ttf +0 -0
  93. package/dist/ui-web/assets/outline-icons-DInHoiqI.woff2 +0 -0
  94. package/dist/ui-web/assets/outline-icons-LX8adJ4n.eot +0 -0
  95. package/dist/ui-web/assets/outline-icons-aQ88nltS.woff +0 -0
  96. package/dist/ui-web/assets/provider-utils_false-BKJD46kk.js +1 -0
  97. package/dist/ui-web/assets/provider-utils_false-Bu5lmX18.js +1 -0
  98. package/dist/ui-web/index.html +13 -0
  99. package/dist/ui-web/vite.svg +1 -0
  100. package/dist/utils/formatter.d.ts.map +1 -1
  101. package/dist/utils/formatter.js +10 -2
  102. package/dist/utils/model.d.ts +9 -2
  103. package/dist/utils/model.d.ts.map +1 -1
  104. package/dist/utils/model.js +16 -1
  105. package/dist/utils/type-utils.d.ts.map +1 -1
  106. package/dist/utils/type-utils.js +3 -1
  107. package/dist/vector/embedding.d.ts +2 -5
  108. package/dist/vector/embedding.d.ts.map +1 -1
  109. package/dist/vector/embedding.js +9 -13
  110. package/dist/vector/types.d.ts.map +1 -1
  111. package/dist/vector/types.js +1 -1
  112. package/package.json +9 -5
  113. package/src/api/config.ts +15 -11
  114. package/src/api/sonamu.ts +60 -6
  115. package/src/bin/cli.ts +57 -119
  116. package/src/database/base-model.ts +21 -128
  117. package/src/database/base-model.types.ts +3 -4
  118. package/src/database/db.ts +28 -18
  119. package/src/database/puri-subset.test-d.ts +1 -0
  120. package/src/database/puri-subset.types.ts +2 -0
  121. package/src/database/puri.ts +238 -27
  122. package/src/database/puri.types.test-d.ts +1 -1
  123. package/src/database/puri.types.ts +49 -6
  124. package/src/entity/entity-manager.ts +9 -0
  125. package/src/index.ts +1 -1
  126. package/src/migration/code-generation.ts +40 -1
  127. package/src/migration/postgresql-schema-reader.ts +53 -22
  128. package/src/naite/messaging-types.ts +43 -44
  129. package/src/naite/naite.ts +1 -1
  130. package/src/shared/app.shared.ts.txt +13 -0
  131. package/src/shared/web.shared.ts.txt +13 -0
  132. package/src/stream/sse.ts +15 -3
  133. package/src/syncer/api-parser.ts +6 -1
  134. package/src/syncer/file-patterns.ts +11 -9
  135. package/src/syncer/module-loader.ts +35 -0
  136. package/src/syncer/syncer.ts +34 -21
  137. package/src/tasks/decorator.ts +71 -0
  138. package/src/tasks/step-wrapper.ts +84 -0
  139. package/src/tasks/workflow-manager.ts +330 -0
  140. package/src/template/implementations/generated.template.ts +19 -6
  141. package/src/types/types.ts +20 -4
  142. package/src/ui/ai-api.ts +60 -0
  143. package/src/ui/ai-client.ts +499 -0
  144. package/src/ui/api.ts +786 -0
  145. package/src/utils/formatter.ts +8 -1
  146. package/src/utils/model.ts +26 -2
  147. package/src/utils/type-utils.ts +2 -0
  148. package/src/vector/embedding.ts +10 -14
  149. package/src/vector/types.ts +1 -2
  150. package/dist/vector/vector-search.d.ts +0 -47
  151. package/dist/vector/vector-search.d.ts.map +0 -1
  152. package/dist/vector/vector-search.js +0 -176
  153. package/src/vector/vector-search.ts +0 -261
package/src/bin/cli.ts CHANGED
@@ -22,66 +22,68 @@ import { BUILD_DIR, SWC_BUILD_COMMAND, TSC_TYPE_CHECK_COMMAND } from "./build-co
22
22
  let migrator: Migrator;
23
23
 
24
24
  async function bootstrap() {
25
- const notToInit = ["dev", "build", "start", "ui"].includes(process.argv[2] ?? "");
25
+ const notToInit = ["dev", "build", "start"].includes(process.argv[2] ?? "");
26
26
  if (!notToInit) {
27
27
  await Sonamu.init(false, false);
28
28
  }
29
29
 
30
- await tsicli(process.argv, {
31
- types: {
32
- "#entityId": {
33
- type: "autocomplete",
34
- name: "#entityId",
35
- message: "Please input #entityId",
36
- choices: EntityManager.getAllParentIds().map((entityId) => ({
37
- title: entityId,
38
- value: entityId,
39
- })),
30
+ try {
31
+ await tsicli(process.argv, {
32
+ types: {
33
+ "#entityId": {
34
+ type: "autocomplete",
35
+ name: "#entityId",
36
+ message: "Please input #entityId",
37
+ choices: EntityManager.getAllParentIds().map((entityId) => ({
38
+ title: entityId,
39
+ value: entityId,
40
+ })),
41
+ },
42
+ "#recordIds": "number[]",
43
+ "#name": "string",
40
44
  },
41
- "#recordIds": "number[]",
42
- "#name": "string",
43
- },
44
- args: [
45
- ["fixture", "init"],
46
- ["fixture", "import", "#entityId", "#recordIds"],
47
- ["fixture", "sync"],
48
- ["migrate", "run"],
49
- ["migrate", "check"],
50
- ["migrate", "rollback"],
51
- ["migrate", "reset"],
52
- ["migrate", "clear"],
53
- ["migrate", "status"],
54
- ["stub", "practice", "#name"],
55
- ["stub", "entity", "#name"],
56
- ["scaffold", "model", "#entityId"],
57
- ["scaffold", "model_test", "#entityId"],
58
- ["scaffold", "view_list", "#entityId"],
59
- ["scaffold", "view_form", "#entityId"],
60
- ["ui"],
61
- ["sync"],
62
- ["dev"],
63
- ["build"],
64
- ["start"],
65
- ],
66
- runners: {
67
- migrate_status,
68
- migrate_run,
69
- fixture_init,
70
- fixture_import,
71
- fixture_sync,
72
- stub_practice,
73
- stub_entity,
74
- scaffold_model,
75
- scaffold_model_test,
76
- ui,
77
- // scaffold_view_list,
78
- // scaffold_view_form,
79
- sync,
80
- dev,
81
- build,
82
- start,
83
- },
84
- });
45
+ args: [
46
+ ["fixture", "init"],
47
+ ["fixture", "import", "#entityId", "#recordIds"],
48
+ ["fixture", "sync"],
49
+ ["migrate", "run"],
50
+ ["migrate", "check"],
51
+ ["migrate", "rollback"],
52
+ ["migrate", "reset"],
53
+ ["migrate", "clear"],
54
+ ["migrate", "status"],
55
+ ["stub", "practice", "#name"],
56
+ ["stub", "entity", "#name"],
57
+ ["scaffold", "model", "#entityId"],
58
+ ["scaffold", "model_test", "#entityId"],
59
+ ["scaffold", "view_list", "#entityId"],
60
+ ["scaffold", "view_form", "#entityId"],
61
+ ["sync"],
62
+ ["dev"],
63
+ ["build"],
64
+ ["start"],
65
+ ],
66
+ runners: {
67
+ migrate_status,
68
+ migrate_run,
69
+ fixture_init,
70
+ fixture_import,
71
+ fixture_sync,
72
+ stub_practice,
73
+ stub_entity,
74
+ scaffold_model,
75
+ scaffold_model_test,
76
+ // scaffold_view_list,
77
+ // scaffold_view_form,
78
+ sync,
79
+ dev,
80
+ build,
81
+ start,
82
+ },
83
+ });
84
+ } finally {
85
+ await Sonamu.destroy();
86
+ }
85
87
  }
86
88
 
87
89
  bootstrap().finally(async () => {
@@ -448,67 +450,3 @@ async function scaffold_model_test(entityId: string) {
448
450
  entityId,
449
451
  });
450
452
  }
451
-
452
- async function ui() {
453
- try {
454
- const apiRootPath = findApiRootPath();
455
-
456
- // 사용자 프로젝트의 패키지들 중에서 @sonamu-kit/ui를 찾습니다.
457
- // 이를 위해서 createRequire를 사용하여 프로젝트 경로 기준으로 resolve합니다.
458
- const projectRequire = createRequire(path.join(apiRootPath, "package.json"));
459
- const uiPackagePath = projectRequire.resolve("@sonamu-kit/ui"); // 없으면 여기서 터져요(MODULE_NOT_FOUND)
460
- const uiNodePath = path.join(path.dirname(uiPackagePath), "run-ui.js");
461
-
462
- if (!(await exists(uiNodePath))) {
463
- console.log(
464
- chalk.red(`UI runner script not found at ${uiNodePath}. Please rebuild @sonamu-kit/ui.`),
465
- );
466
- return;
467
- }
468
-
469
- // UI를 별도 프로세스로 실행 (hmr-hook 활성화)
470
- const uiProcess = spawn(
471
- process.execPath,
472
- [
473
- "--import",
474
- "sonamu/ts-loader-register",
475
- "--import",
476
- "sonamu/hmr-hook-register",
477
- "--enable-source-maps",
478
- "--no-warnings",
479
- uiNodePath,
480
- ],
481
- {
482
- stdio: "inherit",
483
- env: {
484
- ...process.env,
485
- HOT: "yes",
486
- API_ROOT_PATH: apiRootPath, // UI는 얘만 알면 돼요! 나머지는 얘가 떠서 알아서 할 것임 ㅎ
487
- },
488
- },
489
- );
490
-
491
- // 종료 처리
492
- const cleanup = () => {
493
- console.log(chalk.yellow("\n\n👋 Shutting down UI server..."));
494
- uiProcess.kill("SIGTERM");
495
- process.exit(0);
496
- };
497
-
498
- process.on("SIGINT", cleanup);
499
- process.on("SIGTERM", cleanup);
500
-
501
- uiProcess.on("exit", (code) => {
502
- if (code !== 0) {
503
- console.error(chalk.red(`❌ UI server exited with code ${code}`));
504
- process.exit(code || 1);
505
- }
506
- });
507
- } catch (e: unknown) {
508
- if (e instanceof Error && e.message.includes("isn't declared")) {
509
- console.log(`You need to install ${chalk.blue(`@sonamu-kit/ui`)} first.`);
510
- return;
511
- }
512
- throw e;
513
- }
514
- }
@@ -2,24 +2,13 @@
2
2
 
3
3
  import type { Knex } from "knex";
4
4
  import { group, isObject, omit, set } from "radashi";
5
+ import type { ListResult } from "..";
5
6
  import { Sonamu } from "../api";
6
- import { EntityManager } from "../entity/entity-manager";
7
- import type { DatabaseSchemaExtend } from "../types/types";
7
+ import type { DatabaseSchemaExtend, SonamuQueryMode } from "../types/types";
8
8
  import { getJoinTables, getTableNamesFromWhere } from "../utils/sql-parser";
9
9
  import { chunk } from "../utils/utils";
10
- import type {
11
- EmbeddingItem,
12
- EmbeddingProvider,
13
- HybridSearchOptions,
14
- HybridSearchResult,
15
- ProgressCallback,
16
- VectorSearchOptions,
17
- VectorSearchResult,
18
- } from "../vector/types";
19
- import { VectorSearch } from "../vector/vector-search";
20
10
  import type {
21
11
  EnhancerMap,
22
- ExecuteSubsetQueryResult,
23
12
  ResolveSubsetIntersection,
24
13
  UnionExtractedTTables,
25
14
  } from "./base-model.types";
@@ -69,118 +58,7 @@ export class BaseModelClass<
69
58
  return new PuriWrapper(db, new UpsertBuilder());
70
59
  }
71
60
 
72
- // VectorSearch 인스턴스 캐시
73
- private _vectorSearch: VectorSearch<any> | null = null;
74
-
75
- /**
76
- * 벡터 검색 인스턴스 반환
77
- * - 기본 provider: voyage
78
- * - 기본 dimensions: 1024 (DEFAULT_VECTOR_CONFIG 사용)
79
- */
80
- getVector<T = Record<string, unknown>>(): VectorSearch<T> {
81
- if (this._vectorSearch) {
82
- return this._vectorSearch as VectorSearch<T>;
83
- }
84
-
85
- const entity = EntityManager.get(this.modelName);
86
-
87
- this._vectorSearch = new VectorSearch<T>(this.getDB("w"), entity.table);
88
-
89
- return this._vectorSearch as VectorSearch<T>;
90
- }
91
-
92
- /**
93
- * 벡터 검색 (코사인 유사도)
94
- * @param query - 검색어
95
- * @param options - 검색 옵션
96
- */
97
- async vectorSearch<T = Record<string, unknown>>(
98
- query: string,
99
- options: VectorSearchOptions & { provider?: EmbeddingProvider } = {},
100
- ): Promise<VectorSearchResult<T>[]> {
101
- const entity = EntityManager.get(this.modelName);
102
- const vectorProp = entity.getVectorColumn();
103
- if (!vectorProp) {
104
- throw new Error(`${this.modelName} Entity에 vector 컬럼이 정의되지 않았습니다.`);
105
- }
106
-
107
- const vs = new VectorSearch<T>(this.getDB("w"), entity.table);
108
- return vs.search(query, options.provider ?? "voyage", {
109
- ...options,
110
- embeddingColumn: options.embeddingColumn ?? vectorProp.name,
111
- });
112
- }
113
-
114
- /**
115
- * 하이브리드 검색 (Vector + FTS)
116
- * @param query - 검색어
117
- * @param options - 검색 옵션
118
- */
119
- async hybridSearch<T = Record<string, unknown>>(
120
- query: string,
121
- options: HybridSearchOptions & { provider?: EmbeddingProvider } = {},
122
- ): Promise<HybridSearchResult<T>[]> {
123
- const entity = EntityManager.get(this.modelName);
124
- const vectorProp = entity.getVectorColumn();
125
- if (!vectorProp) {
126
- throw new Error(`${this.modelName} Entity에 vector 컬럼이 정의되지 않았습니다.`);
127
- }
128
-
129
- const vs = new VectorSearch<T>(this.getDB("w"), entity.table);
130
- return vs.hybridSearch(query, options.provider ?? "voyage", {
131
- ...options,
132
- embeddingColumn: options.embeddingColumn ?? vectorProp.name,
133
- });
134
- }
135
-
136
- /**
137
- * 단일 레코드에 임베딩 저장
138
- * @param id - 레코드 ID
139
- * @param text - 임베딩할 텍스트
140
- * @param options - provider, embeddingColumn 옵션
141
- */
142
- async saveEmbedding(
143
- id: number,
144
- text: string,
145
- options: { provider?: EmbeddingProvider; embeddingColumn?: string } = {},
146
- ): Promise<void> {
147
- const entity = EntityManager.get(this.modelName);
148
- const vectorProp = entity.getVectorColumn(options.embeddingColumn);
149
- if (!vectorProp) {
150
- throw new Error(`${this.modelName} Entity에 vector 컬럼이 정의되지 않았습니다.`);
151
- }
152
-
153
- const { provider = "voyage" } = options;
154
- const vs = this.getVector();
155
- return vs.saveEmbedding(id, text, provider, vectorProp.name);
156
- }
157
-
158
- /**
159
- * 여러 레코드에 임베딩 일괄 저장
160
- * @param items - { id, text } 배열
161
- * @param options - provider, embeddingColumn, onProgress 옵션
162
- */
163
- async saveEmbeddingsBatch(
164
- items: EmbeddingItem[],
165
- options: {
166
- provider?: EmbeddingProvider;
167
- embeddingColumn?: string;
168
- onProgress?: ProgressCallback;
169
- } = {},
170
- ): Promise<void> {
171
- const entity = EntityManager.get(this.modelName);
172
- const vectorProp = entity.getVectorColumn(options.embeddingColumn);
173
- if (!vectorProp) {
174
- throw new Error(`${this.modelName} Entity에 vector 컬럼이 정의되지 않았습니다.`);
175
- }
176
-
177
- const { provider = "voyage", onProgress } = options;
178
- const vs = this.getVector();
179
- return vs.saveEmbeddingsBatch(items, provider, vectorProp.name, onProgress);
180
- }
181
-
182
61
  async destroy() {
183
- this._vectorSearch = null;
184
62
  return DB.destroy();
185
63
  }
186
64
 
@@ -275,6 +153,11 @@ export class BaseModelClass<
275
153
  async executeSubsetQuery<
276
154
  T extends TSubsetKey,
277
155
  TComputedResults extends InferAllSubsets<TSubsetQueries, TLoaderQueries>,
156
+ LP extends {
157
+ num?: number;
158
+ page?: number;
159
+ queryMode?: SonamuQueryMode;
160
+ },
278
161
  >(
279
162
  params: {
280
163
  subset: T;
@@ -282,12 +165,12 @@ export class BaseModelClass<
282
165
  params: {
283
166
  num: number;
284
167
  page: number;
285
- queryMode?: "list" | "count" | "both";
168
+ queryMode?: SonamuQueryMode;
286
169
  };
287
170
  debug?: boolean;
288
171
  optimizeCountQuery?: boolean;
289
172
  } & EnhancerParam<TSubsetKey, TComputedResults, TSubsetMapping>,
290
- ): Promise<ExecuteSubsetQueryResult<TSubsetMapping, T>> {
173
+ ): Promise<ListResult<LP, TSubsetMapping[T]>> {
291
174
  const { subset, qb, params: queryParams, debug = false, optimizeCountQuery = false } = params;
292
175
 
293
176
  if (!this.loaderQueries) {
@@ -296,9 +179,13 @@ export class BaseModelClass<
296
179
 
297
180
  const { num, page } = queryParams;
298
181
 
299
- // COUNT 쿼리 실행
182
+ // COUNT 쿼리 실행 (queryMode: list일 때는 0 리턴)
300
183
  const total = await this.executeCountQuery(qb, queryParams, debug, optimizeCountQuery);
301
184
 
185
+ if (queryParams?.queryMode === "count") {
186
+ return { total } as ListResult<LP, TSubsetMapping[T]>;
187
+ }
188
+
302
189
  // LIST 쿼리 실행
303
190
  const computedRows = await this.executeListQuery(subset, qb, queryParams, num, page, debug);
304
191
 
@@ -308,7 +195,13 @@ export class BaseModelClass<
308
195
  computedRows.map((row) => enhancer?.(row) ?? row),
309
196
  )) as TSubsetMapping[T][];
310
197
 
311
- return { rows, total };
198
+ if (queryParams.queryMode === "list") {
199
+ // 리스트만 리턴
200
+ return { rows } as ListResult<LP, TSubsetMapping[T]>;
201
+ } else {
202
+ // 둘다 리턴
203
+ return { rows, total } as ListResult<LP, TSubsetMapping[T]>;
204
+ }
312
205
  }
313
206
 
314
207
  /**
@@ -7,6 +7,7 @@
7
7
  * Enhancer, SubsetQuery 교집합 등 Model 계층에서 필요한 타입 정의.
8
8
  */
9
9
 
10
+ import type { ListResult } from "..";
10
11
  import type { DatabaseSchemaExtend } from "../types/types";
11
12
  import type { Puri } from "./puri";
12
13
  import type { PuriSubsetFn } from "./puri-subset.types";
@@ -151,7 +152,5 @@ export type ExecuteSubsetQueryParams<
151
152
  export type ExecuteSubsetQueryResult<
152
153
  TSubsetMapping extends Record<string, any>,
153
154
  T extends string,
154
- > = {
155
- rows: TSubsetMapping[T][];
156
- total: number;
157
- };
155
+ LP extends { queryMode?: "list" | "count" | "both" },
156
+ > = ListResult<LP, TSubsetMapping[T]>;
@@ -56,30 +56,40 @@ export class DBClass {
56
56
  const instanceName = which === "w" ? "wdb" : "rdb";
57
57
 
58
58
  if (!this[instanceName]) {
59
- let config: Knex.Config;
60
- switch (process.env.NODE_ENV ?? "development") {
61
- case "development":
62
- case "staging":
63
- config =
64
- which === "w"
65
- ? dbConfig.development_master
66
- : (dbConfig.development_slave ?? dbConfig.development_master);
67
- break;
68
- case "production":
69
- config =
70
- which === "w"
71
- ? dbConfig.production_master
72
- : (dbConfig.production_slave ?? dbConfig.production_master);
73
- break;
74
- default:
75
- throw new Error(`현재 ENV ${process.env.NODE_ENV}에는 설정 가능한 DB설정이 없습니다.`);
76
- }
59
+ const config = this.getDBConfig(which);
77
60
  this[instanceName] = knex(config);
78
61
  }
79
62
 
80
63
  return this[instanceName];
81
64
  }
82
65
 
66
+ getDBConfig(which: DBPreset): Knex.Config {
67
+ const dbConfig = Sonamu.dbConfig;
68
+ if (process.env.NODE_ENV === "test") {
69
+ return {
70
+ ...dbConfig.test,
71
+ // 단일 풀
72
+ pool: {
73
+ min: 1,
74
+ max: 1,
75
+ },
76
+ };
77
+ }
78
+ switch (process.env.NODE_ENV ?? "development") {
79
+ case "development":
80
+ case "staging":
81
+ return which === "w"
82
+ ? dbConfig.development_master
83
+ : (dbConfig.development_slave ?? dbConfig.development_master);
84
+ case "production":
85
+ return which === "w"
86
+ ? dbConfig.production_master
87
+ : (dbConfig.production_slave ?? dbConfig.production_master);
88
+ default:
89
+ throw new Error(`현재 ENV ${process.env.NODE_ENV}에는 설정 가능한 DB설정이 없습니다.`);
90
+ }
91
+ }
92
+
83
93
  async destroy(): Promise<void> {
84
94
  if (this.wdb !== undefined) {
85
95
  await this.wdb.destroy();
@@ -4,6 +4,7 @@ import type { Puri } from "./puri";
4
4
  import type { Hydrate, InferAllSubsets, LoadersResult } from "./puri-subset.types";
5
5
  import type { PuriWrapper } from "./puri-wrapper";
6
6
 
7
+ /** biome-ignore lint/suspicious/noExplicitAny: Puri Subset 타입 시스템에서 any를 허용함 */
7
8
  type MockPuri<T> = Puri<DatabaseSchemaExtend, any, T>;
8
9
 
9
10
  describe("Hydrate", () => {
@@ -1,3 +1,5 @@
1
+ /** biome-ignore-all lint/suspicious/noExplicitAny: Puri Subset 타입 시스템에서 any를 허용함 */
2
+
1
3
  /**
2
4
  * Puri Subset 타입 시스템
3
5
  *