sonamu 0.2.54 → 0.4.1
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/.pnp.cjs +11 -0
- package/dist/base-model-BzMJ2E_I.d.mts +43 -0
- package/dist/base-model-CWRKUX49.d.ts +43 -0
- package/dist/bin/cli.js +118 -89
- package/dist/bin/cli.js.map +1 -1
- package/dist/bin/cli.mjs +74 -45
- package/dist/bin/cli.mjs.map +1 -1
- package/dist/chunk-FLPD24HS.mjs +231 -0
- package/dist/chunk-FLPD24HS.mjs.map +1 -0
- package/dist/chunk-I2MMJRJN.mjs +1550 -0
- package/dist/chunk-I2MMJRJN.mjs.map +1 -0
- package/dist/{chunk-L4KELCY7.mjs → chunk-PP2PSSAG.mjs} +5241 -5571
- package/dist/chunk-PP2PSSAG.mjs.map +1 -0
- package/dist/chunk-QK5XXJUX.mjs +280 -0
- package/dist/chunk-QK5XXJUX.mjs.map +1 -0
- package/dist/chunk-U636LQJJ.js +231 -0
- package/dist/chunk-U636LQJJ.js.map +1 -0
- package/dist/chunk-W7KDVJLQ.js +280 -0
- package/dist/chunk-W7KDVJLQ.js.map +1 -0
- package/dist/{chunk-JOHF7PK4.js → chunk-XT6LHCX5.js} +5292 -5622
- package/dist/chunk-XT6LHCX5.js.map +1 -0
- package/dist/chunk-Z2P7XTXE.js +1550 -0
- package/dist/chunk-Z2P7XTXE.js.map +1 -0
- package/dist/database/drivers/knex/base-model.d.mts +16 -0
- package/dist/database/drivers/knex/base-model.d.ts +16 -0
- package/dist/database/drivers/knex/base-model.js +55 -0
- package/dist/database/drivers/knex/base-model.js.map +1 -0
- package/dist/database/drivers/knex/base-model.mjs +56 -0
- package/dist/database/drivers/knex/base-model.mjs.map +1 -0
- package/dist/database/drivers/kysely/base-model.d.mts +22 -0
- package/dist/database/drivers/kysely/base-model.d.ts +22 -0
- package/dist/database/drivers/kysely/base-model.js +64 -0
- package/dist/database/drivers/kysely/base-model.js.map +1 -0
- package/dist/database/drivers/kysely/base-model.mjs +65 -0
- package/dist/database/drivers/kysely/base-model.mjs.map +1 -0
- package/dist/index.d.mts +222 -928
- package/dist/index.d.ts +222 -928
- package/dist/index.js +13 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +18 -31
- package/dist/index.mjs.map +1 -1
- package/dist/model-CAH_4oQh.d.mts +1042 -0
- package/dist/model-CAH_4oQh.d.ts +1042 -0
- package/import-to-require.js +27 -0
- package/package.json +23 -2
- package/src/api/caster.ts +6 -0
- package/src/api/code-converters.ts +3 -1
- package/src/api/sonamu.ts +41 -22
- package/src/bin/cli.ts +78 -46
- package/src/database/_batch_update.ts +16 -11
- package/src/database/base-model.abstract.ts +97 -0
- package/src/database/base-model.ts +214 -280
- package/src/database/code-generator.ts +72 -0
- package/src/database/db.abstract.ts +75 -0
- package/src/database/db.ts +21 -82
- package/src/database/drivers/knex/base-model.ts +55 -0
- package/src/database/drivers/knex/client.ts +209 -0
- package/src/database/drivers/knex/db.ts +227 -0
- package/src/database/drivers/knex/generator.ts +659 -0
- package/src/database/drivers/kysely/base-model.ts +89 -0
- package/src/database/drivers/kysely/client.ts +309 -0
- package/src/database/drivers/kysely/db.ts +238 -0
- package/src/database/drivers/kysely/generator.ts +714 -0
- package/src/database/types.ts +117 -0
- package/src/database/upsert-builder.ts +31 -18
- package/src/entity/entity-manager.ts +9 -5
- package/src/entity/entity-utils.ts +1 -1
- package/src/entity/entity.ts +9 -13
- package/src/entity/migrator.ts +98 -693
- package/src/index.ts +1 -1
- package/src/syncer/syncer.ts +103 -56
- package/src/templates/generated_http.template.ts +14 -0
- package/src/templates/kysely_types.template.ts +205 -0
- package/src/templates/model.template.ts +2 -139
- package/src/templates/service.template.ts +3 -1
- package/src/testing/_relation-graph.ts +111 -0
- package/src/testing/fixture-manager.ts +216 -332
- package/src/types/types.ts +56 -6
- package/src/utils/utils.ts +56 -4
- package/src/utils/zod-error.ts +189 -0
- package/tsconfig.json +2 -2
- package/tsup.config.js +11 -10
- package/dist/chunk-JOHF7PK4.js.map +0 -1
- package/dist/chunk-L4KELCY7.mjs.map +0 -1
- /package/src/database/{knex-plugins → drivers/knex/plugins}/knex-on-duplicate-update.ts +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Knex } from "knex";
|
|
2
|
+
import {
|
|
3
|
+
FileMigrationProviderProps,
|
|
4
|
+
Kysely,
|
|
5
|
+
MysqlDialectConfig,
|
|
6
|
+
ReferenceExpression,
|
|
7
|
+
SelectQueryBuilder,
|
|
8
|
+
} from "kysely";
|
|
9
|
+
import { KnexClient } from "./drivers/knex/client";
|
|
10
|
+
import { KyselyClient } from "./drivers/kysely/client";
|
|
11
|
+
import { PoolOptions } from "mysql2";
|
|
12
|
+
|
|
13
|
+
export type DBPreset = "w" | "r";
|
|
14
|
+
|
|
15
|
+
export type DatabaseDriver = keyof DriverSpec;
|
|
16
|
+
/**
|
|
17
|
+
* core: 실제 데이터베이스 라이브러리 인스턴스
|
|
18
|
+
* adapter: Sonamu의 래퍼 클라이언트 구현체
|
|
19
|
+
* queryBuilder: 쿼리빌더 인스턴스
|
|
20
|
+
* table/column: 테이블과 컬럼 타입 정보
|
|
21
|
+
*/
|
|
22
|
+
export interface DriverSpec {
|
|
23
|
+
knex: {
|
|
24
|
+
core: Knex;
|
|
25
|
+
adapter: KnexClient;
|
|
26
|
+
queryBuilder: Knex.QueryBuilder;
|
|
27
|
+
table: string;
|
|
28
|
+
column: string;
|
|
29
|
+
};
|
|
30
|
+
kysely: {
|
|
31
|
+
core: Kysely<Database>;
|
|
32
|
+
adapter: KyselyClient;
|
|
33
|
+
queryBuilder: SelectQueryBuilder<Database, keyof Database, {}>;
|
|
34
|
+
table: keyof Database;
|
|
35
|
+
column: ReferenceExpression<Database, keyof Database>;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type WhereClause = [string, string, any];
|
|
40
|
+
|
|
41
|
+
export interface DatabaseClient<T extends DatabaseDriver> {
|
|
42
|
+
from(table: string): DriverSpec[T]["adapter"];
|
|
43
|
+
innerJoin(table: string, k1: string, k2: string): DriverSpec[T]["adapter"];
|
|
44
|
+
leftJoin(table: string, k1: string, k2: string): DriverSpec[T]["adapter"];
|
|
45
|
+
select(columns: string | string[]): DriverSpec[T]["adapter"];
|
|
46
|
+
where(o: WhereClause): DriverSpec[T]["adapter"];
|
|
47
|
+
orWhere(o: WhereClause | WhereClause[]): DriverSpec[T]["adapter"];
|
|
48
|
+
insert(table: string, data: Record<string, any>): Promise<void>;
|
|
49
|
+
first(): DriverSpec[T]["adapter"];
|
|
50
|
+
execute(): Promise<any[]>;
|
|
51
|
+
|
|
52
|
+
raw<R>(query: string, bindings?: any[]): Promise<R[]>;
|
|
53
|
+
truncate(table: string): Promise<void>;
|
|
54
|
+
trx<T>(callback: (trx: any) => Promise<T>): Promise<T>;
|
|
55
|
+
destroy(): Promise<void>;
|
|
56
|
+
|
|
57
|
+
getMigrations(): Promise<string[]>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//
|
|
61
|
+
|
|
62
|
+
export type Environment =
|
|
63
|
+
| "development"
|
|
64
|
+
| "development_slave"
|
|
65
|
+
| "production"
|
|
66
|
+
| "production_slave";
|
|
67
|
+
type EnvironmentConfigs<T> = {
|
|
68
|
+
[K in Environment]?: Partial<T>;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Knex 설정을 위한 타입
|
|
72
|
+
export type KnexConfig = Knex.Config & {
|
|
73
|
+
connection: Knex.MySql2ConnectionConfig;
|
|
74
|
+
};
|
|
75
|
+
export type KnexBaseConfig = {
|
|
76
|
+
client: "knex";
|
|
77
|
+
database: string;
|
|
78
|
+
defaultOptions?: Partial<KnexConfig>;
|
|
79
|
+
environments?: EnvironmentConfigs<KnexConfig>;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Kysely 설정을 위한 타입
|
|
83
|
+
export type KyselyConfig = PoolOptions &
|
|
84
|
+
Pick<MysqlDialectConfig, "onCreateConnection"> & {
|
|
85
|
+
migration?: FileMigrationProviderProps;
|
|
86
|
+
};
|
|
87
|
+
export type KyselyBaseConfig = {
|
|
88
|
+
client: "kysely";
|
|
89
|
+
database: string;
|
|
90
|
+
defaultOptions: KyselyConfig;
|
|
91
|
+
environments?: EnvironmentConfigs<KyselyConfig>;
|
|
92
|
+
types?: {
|
|
93
|
+
enabled?: boolean; // 인터페이스 자동 생성 활성화 (기본값: true)
|
|
94
|
+
outDir?: string; // 생성될 파일 경로 (기본값: src/typings)
|
|
95
|
+
fileName?: string; // 생성될 파일명 (기본값: database.types.ts)
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
// export type SonamuDBBaseConfig = KnexBaseConfig | KyselyBaseConfig;
|
|
99
|
+
export type SonamuDBBaseConfig<
|
|
100
|
+
T extends "knex" | "kysely" = "knex" | "kysely",
|
|
101
|
+
> = T extends "knex" ? KnexBaseConfig : KyselyBaseConfig;
|
|
102
|
+
|
|
103
|
+
export type SonamuDBFullConfig<T extends KnexConfig | KyselyConfig> = {
|
|
104
|
+
development_master: T;
|
|
105
|
+
development_slave: T;
|
|
106
|
+
test: T;
|
|
107
|
+
fixture_local: T;
|
|
108
|
+
fixture_remote: T;
|
|
109
|
+
production_master: T;
|
|
110
|
+
production_slave: T;
|
|
111
|
+
};
|
|
112
|
+
export type SonamuKnexDBConfig = SonamuDBFullConfig<KnexConfig>;
|
|
113
|
+
export type SonamuKyselyDBConfig = SonamuDBFullConfig<KyselyConfig>;
|
|
114
|
+
export type SonamuDBConfig = SonamuKnexDBConfig | SonamuKyselyDBConfig;
|
|
115
|
+
|
|
116
|
+
export interface DatabaseExtend {}
|
|
117
|
+
export type Database = DatabaseExtend & {};
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { v4 as uuidv4 } from "uuid";
|
|
2
2
|
import _ from "lodash";
|
|
3
3
|
import { Knex } from "knex";
|
|
4
|
+
import { Kysely } from "kysely";
|
|
4
5
|
import { EntityManager } from "../entity/entity-manager";
|
|
5
6
|
import { nonNullable } from "../utils/utils";
|
|
6
7
|
import { RowWithId, batchUpdate } from "./_batch_update";
|
|
8
|
+
import { Database, DatabaseDriver, DriverSpec } from "./types";
|
|
9
|
+
import { DB } from "./db";
|
|
7
10
|
|
|
8
11
|
type TableData = {
|
|
9
12
|
references: Set<string>;
|
|
@@ -25,7 +28,7 @@ export function isRefField(field: any): field is UBRef {
|
|
|
25
28
|
);
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
export class UpsertBuilder {
|
|
31
|
+
export class UpsertBuilder<D extends DatabaseDriver> {
|
|
29
32
|
tables: Map<string, TableData>;
|
|
30
33
|
constructor() {
|
|
31
34
|
this.tables = new Map();
|
|
@@ -58,7 +61,7 @@ export class UpsertBuilder {
|
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
register<T extends string>(
|
|
61
|
-
tableName:
|
|
64
|
+
tableName: DriverSpec[D]["table"],
|
|
62
65
|
row: {
|
|
63
66
|
[key in T]?:
|
|
64
67
|
| UBRef
|
|
@@ -145,23 +148,23 @@ export class UpsertBuilder {
|
|
|
145
148
|
}
|
|
146
149
|
|
|
147
150
|
async upsert(
|
|
148
|
-
wdb:
|
|
149
|
-
tableName:
|
|
151
|
+
wdb: DriverSpec[D]["core"],
|
|
152
|
+
tableName: DriverSpec[D]["table"],
|
|
150
153
|
chunkSize?: number
|
|
151
154
|
): Promise<number[]> {
|
|
152
155
|
return this.upsertOrInsert(wdb, tableName, "upsert", chunkSize);
|
|
153
156
|
}
|
|
154
157
|
async insertOnly(
|
|
155
|
-
wdb:
|
|
156
|
-
tableName:
|
|
158
|
+
wdb: DriverSpec[D]["core"],
|
|
159
|
+
tableName: DriverSpec[D]["table"],
|
|
157
160
|
chunkSize?: number
|
|
158
161
|
): Promise<number[]> {
|
|
159
162
|
return this.upsertOrInsert(wdb, tableName, "insert", chunkSize);
|
|
160
163
|
}
|
|
161
164
|
|
|
162
165
|
async upsertOrInsert(
|
|
163
|
-
|
|
164
|
-
tableName:
|
|
166
|
+
_wdb: DriverSpec[D]["core"],
|
|
167
|
+
tableName: DriverSpec[D]["table"],
|
|
165
168
|
mode: "upsert" | "insert",
|
|
166
169
|
chunkSize?: number
|
|
167
170
|
): Promise<number[]> {
|
|
@@ -186,6 +189,8 @@ export class UpsertBuilder {
|
|
|
186
189
|
throw new Error(`${tableName} 해결되지 않은 참조가 있습니다.`);
|
|
187
190
|
}
|
|
188
191
|
|
|
192
|
+
const wdb = DB.toClient(_wdb);
|
|
193
|
+
|
|
189
194
|
// 전체 테이블 순회하여 현재 테이블 참조하는 모든 테이블 추출
|
|
190
195
|
const { references, refTables } = Array.from(this.tables).reduce(
|
|
191
196
|
(r, [, table]) => {
|
|
@@ -221,18 +226,20 @@ export class UpsertBuilder {
|
|
|
221
226
|
const uuidMap = new Map<string, any>();
|
|
222
227
|
|
|
223
228
|
for (const chunk of chunks) {
|
|
224
|
-
const q = wdb.insert(chunk).into(tableName);
|
|
225
229
|
if (mode === "insert") {
|
|
226
|
-
await
|
|
230
|
+
await wdb.insert(tableName, chunk);
|
|
227
231
|
} else if (mode === "upsert") {
|
|
228
|
-
await
|
|
232
|
+
await wdb.upsert(tableName, chunk);
|
|
233
|
+
// await q.onDuplicateUpdate.apply(q, Object.keys(normalRows[0]));
|
|
229
234
|
}
|
|
230
235
|
|
|
231
236
|
// upsert된 row들을 다시 조회하여 uuidMap에 저장
|
|
232
237
|
const uuids = chunk.map((row) => row.uuid);
|
|
233
|
-
const upsertedRows = await wdb
|
|
238
|
+
const upsertedRows = await wdb
|
|
239
|
+
.from(tableName)
|
|
234
240
|
.select(_.uniq(["uuid", "id", ...extractFields]))
|
|
235
|
-
.
|
|
241
|
+
.where(["uuid", "in", uuids])
|
|
242
|
+
.execute();
|
|
236
243
|
upsertedRows.forEach((row: any) => {
|
|
237
244
|
uuidMap.set(row.uuid, row);
|
|
238
245
|
});
|
|
@@ -264,7 +271,7 @@ export class UpsertBuilder {
|
|
|
264
271
|
if (selfRefRows.length > 0) {
|
|
265
272
|
// 처리된 데이터를 제외하고 다시 upsert
|
|
266
273
|
table.rows = selfRefRows;
|
|
267
|
-
const selfRefIds = await this.upsert(
|
|
274
|
+
const selfRefIds = await this.upsert(_wdb, tableName, chunkSize);
|
|
268
275
|
allIds.push(...selfRefIds);
|
|
269
276
|
}
|
|
270
277
|
|
|
@@ -272,11 +279,11 @@ export class UpsertBuilder {
|
|
|
272
279
|
}
|
|
273
280
|
|
|
274
281
|
async updateBatch(
|
|
275
|
-
wdb: Knex
|
|
276
|
-
tableName:
|
|
282
|
+
wdb: Knex | Kysely<Database>,
|
|
283
|
+
tableName: DriverSpec[D]["table"],
|
|
277
284
|
options?: {
|
|
278
285
|
chunkSize?: number;
|
|
279
|
-
where?:
|
|
286
|
+
where?: DriverSpec[D]["column"] | DriverSpec[D]["column"][];
|
|
280
287
|
}
|
|
281
288
|
): Promise<void> {
|
|
282
289
|
options = _.defaults(options, {
|
|
@@ -300,6 +307,12 @@ export class UpsertBuilder {
|
|
|
300
307
|
return row as RowWithId<string>;
|
|
301
308
|
});
|
|
302
309
|
|
|
303
|
-
await batchUpdate(
|
|
310
|
+
await batchUpdate(
|
|
311
|
+
DB.toClient(wdb),
|
|
312
|
+
tableName,
|
|
313
|
+
whereColumns as string[],
|
|
314
|
+
rows,
|
|
315
|
+
options.chunkSize
|
|
316
|
+
);
|
|
304
317
|
}
|
|
305
318
|
}
|
|
@@ -44,7 +44,7 @@ class EntityManagerClass {
|
|
|
44
44
|
glob.glob(path.resolve(pathPattern!), (_err, files) => {
|
|
45
45
|
Promise.all(
|
|
46
46
|
files.map(async (file) => {
|
|
47
|
-
this.register(JSON.parse(fs.readFileSync(file).toString()));
|
|
47
|
+
await this.register(JSON.parse(fs.readFileSync(file).toString()));
|
|
48
48
|
})
|
|
49
49
|
).then(() => {
|
|
50
50
|
resolve("ok");
|
|
@@ -63,18 +63,22 @@ class EntityManagerClass {
|
|
|
63
63
|
|
|
64
64
|
const sonamuPath = path.join(
|
|
65
65
|
Sonamu.apiRootPath,
|
|
66
|
-
|
|
66
|
+
`dist/application/sonamu.generated.js?t=${Date.now()}`
|
|
67
67
|
);
|
|
68
|
-
|
|
68
|
+
// CJS
|
|
69
|
+
if (require?.cache && require.cache[sonamuPath]) {
|
|
69
70
|
delete require.cache[sonamuPath];
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
return this.autoload(doSilent);
|
|
73
|
+
return await this.autoload(doSilent);
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
register(json: EntityJson): void {
|
|
76
|
+
async register(json: EntityJson): Promise<void> {
|
|
76
77
|
const entity = new Entity(json);
|
|
78
|
+
await entity.registerModulePaths();
|
|
79
|
+
entity.registerTableSpecs();
|
|
77
80
|
this.entities.set(json.id, entity);
|
|
81
|
+
// console.debug(chalk.cyan(`register :: ${entity.id}`));
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
get(entityId: string): Entity {
|
package/src/entity/entity.ts
CHANGED
|
@@ -133,9 +133,6 @@ export class Entity {
|
|
|
133
133
|
fs: inflection.dasherize(inflection.underscore(id)).toLowerCase(),
|
|
134
134
|
module: id,
|
|
135
135
|
};
|
|
136
|
-
|
|
137
|
-
this.registerModulePaths();
|
|
138
|
-
this.registerTableSpecs();
|
|
139
136
|
}
|
|
140
137
|
|
|
141
138
|
/*
|
|
@@ -544,7 +541,7 @@ export class Entity {
|
|
|
544
541
|
.filter(nonNullable);
|
|
545
542
|
}
|
|
546
543
|
|
|
547
|
-
registerModulePaths() {
|
|
544
|
+
async registerModulePaths() {
|
|
548
545
|
const basePath = `${this.names.parentFs}`;
|
|
549
546
|
|
|
550
547
|
// base-scheme
|
|
@@ -579,15 +576,14 @@ export class Entity {
|
|
|
579
576
|
|
|
580
577
|
if (fs.existsSync(typesFileDistPath)) {
|
|
581
578
|
const importPath = path.relative(__dirname, typesFileDistPath);
|
|
582
|
-
import(importPath)
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
});
|
|
579
|
+
const t = await import(importPath);
|
|
580
|
+
this.types = Object.keys(t).reduce((result, key) => {
|
|
581
|
+
EntityManager.setModulePath(key, typesModulePath);
|
|
582
|
+
return {
|
|
583
|
+
...result,
|
|
584
|
+
[key]: t[key],
|
|
585
|
+
};
|
|
586
|
+
}, {});
|
|
591
587
|
}
|
|
592
588
|
}
|
|
593
589
|
|