sonamu 0.7.0 → 0.7.2
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/ai/agents/types.d.ts +4 -3
- package/dist/ai/agents/types.d.ts.map +1 -1
- package/dist/ai/agents/types.js +1 -1
- package/dist/api/config.d.ts +4 -2
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +5 -2
- package/dist/api/decorators.d.ts.map +1 -1
- package/dist/api/decorators.js +3 -2
- package/dist/bin/cli.js +13 -13
- package/dist/bin/{hot-hook-register.d.ts → hmr-hook-register.d.ts} +3 -3
- package/dist/bin/hmr-hook-register.d.ts.map +1 -0
- package/dist/bin/{hot-hook-register.js → hmr-hook-register.js} +5 -5
- package/dist/bin/ts-loader-register.d.ts +2 -0
- package/dist/bin/ts-loader-register.d.ts.map +1 -0
- package/dist/bin/ts-loader-register.js +34 -0
- package/dist/database/base-model.d.ts +2 -34
- package/dist/database/base-model.d.ts.map +1 -1
- package/dist/database/base-model.js +4 -171
- package/dist/database/upsert-builder.d.ts.map +1 -1
- package/dist/database/upsert-builder.js +3 -4
- package/dist/syncer/syncer.js +3 -3
- package/dist/template/zod-converter.d.ts.map +1 -1
- package/dist/template/zod-converter.js +50 -3
- package/dist/types/types.d.ts +2 -2
- package/package.json +9 -9
- package/src/ai/agents/types.ts +6 -3
- package/src/api/config.ts +16 -5
- package/src/api/decorators.ts +2 -1
- package/src/bin/cli.ts +13 -13
- package/src/bin/{hot-hook-register.ts → hmr-hook-register.ts} +4 -4
- package/src/bin/{loader-register.ts → ts-loader-register.ts} +2 -2
- package/src/database/base-model.ts +6 -237
- package/src/database/upsert-builder.ts +2 -3
- package/src/syncer/syncer.ts +2 -2
- package/src/template/zod-converter.ts +57 -3
- package/dist/bin/hot-hook-register.d.ts.map +0 -1
- package/dist/bin/loader-register.d.ts +0 -2
- package/dist/bin/loader-register.d.ts.map +0 -1
- package/dist/bin/loader-register.js +0 -34
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/** biome-ignore-all lint/suspicious/noExplicitAny: Puri의 타입은 개별 모델에서 확정되므로 BaseModel에서는 any를 허용함 */
|
|
2
|
+
|
|
3
3
|
import type { Knex } from "knex";
|
|
4
|
-
import { group, isObject, omit, set
|
|
4
|
+
import { group, isObject, omit, set } from "radashi";
|
|
5
5
|
import { Sonamu } from "../api";
|
|
6
|
-
import {
|
|
7
|
-
import type { BaseListParams } from "../utils/model";
|
|
6
|
+
import type { DatabaseSchemaExtend } from "../types/types";
|
|
8
7
|
import { getJoinTables, getTableNamesFromWhere } from "../utils/sql-parser";
|
|
9
8
|
import { chunk } from "../utils/utils";
|
|
10
9
|
import type {
|
|
@@ -56,7 +55,7 @@ export class BaseModelClass<
|
|
|
56
55
|
|
|
57
56
|
// 트랜잭션이 없으면 새로운 PuriWrapper 반환
|
|
58
57
|
const db = this.getDB(which);
|
|
59
|
-
return new PuriWrapper(db,
|
|
58
|
+
return new PuriWrapper(db, new UpsertBuilder());
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
async destroy() {
|
|
@@ -229,7 +228,7 @@ export class BaseModelClass<
|
|
|
229
228
|
// TODO: qb의 DISTINCT가 있는 경우 처리해야 함
|
|
230
229
|
const countResult: { total?: number } = await countPuri
|
|
231
230
|
.clear("select")
|
|
232
|
-
.select({ total: Puri.rawNumber(`COUNT(*)`) })
|
|
231
|
+
.select({ total: Puri.rawNumber(`COUNT(*)::integer`) })
|
|
233
232
|
.first();
|
|
234
233
|
|
|
235
234
|
if (debug) {
|
|
@@ -365,236 +364,6 @@ export class BaseModelClass<
|
|
|
365
364
|
return hydrated;
|
|
366
365
|
}) as T[];
|
|
367
366
|
}
|
|
368
|
-
|
|
369
|
-
// Legacy SubsetQuery 실행 (Puri 도입 전 호환용)
|
|
370
|
-
async runSubsetQuery<T extends BaseListParams, U extends string>({
|
|
371
|
-
params,
|
|
372
|
-
baseTable,
|
|
373
|
-
subset,
|
|
374
|
-
subsetQuery,
|
|
375
|
-
build,
|
|
376
|
-
afterBuild,
|
|
377
|
-
debug,
|
|
378
|
-
db: _db,
|
|
379
|
-
optimizeCountQuery,
|
|
380
|
-
}: {
|
|
381
|
-
subset: U;
|
|
382
|
-
params: T;
|
|
383
|
-
subsetQuery: SubsetQuery;
|
|
384
|
-
build: (buildParams: {
|
|
385
|
-
qb: Knex.QueryBuilder;
|
|
386
|
-
db: Knex;
|
|
387
|
-
select: (string | Knex.Raw)[];
|
|
388
|
-
joins: SubsetQuery["joins"];
|
|
389
|
-
virtual: string[];
|
|
390
|
-
}) => Knex.QueryBuilder;
|
|
391
|
-
afterBuild?: (buildParams: {
|
|
392
|
-
qb: Knex.QueryBuilder;
|
|
393
|
-
db: Knex;
|
|
394
|
-
select: (string | Knex.Raw)[];
|
|
395
|
-
joins: SubsetQuery["joins"];
|
|
396
|
-
virtual: string[];
|
|
397
|
-
}) => Knex.QueryBuilder;
|
|
398
|
-
baseTable?: string;
|
|
399
|
-
debug?: boolean | "list" | "count";
|
|
400
|
-
db?: Knex;
|
|
401
|
-
optimizeCountQuery?: boolean;
|
|
402
|
-
}): Promise<{
|
|
403
|
-
// biome-ignore lint/suspicious/noExplicitAny: Puri 도입 전까지 any로 유지
|
|
404
|
-
rows: any[];
|
|
405
|
-
total?: number | undefined;
|
|
406
|
-
subsetQuery: SubsetQuery;
|
|
407
|
-
qb: Knex.QueryBuilder;
|
|
408
|
-
}> {
|
|
409
|
-
const chalk = (await import("chalk")).default;
|
|
410
|
-
const SqlParser = (await import("node-sql-parser")).default;
|
|
411
|
-
const { getTableName, getTableNamesFromWhere } = await import("../utils/sql-parser");
|
|
412
|
-
|
|
413
|
-
const db = _db ?? this.getDB(subset.startsWith("A") ? "w" : "r");
|
|
414
|
-
baseTable = baseTable ?? inflection.pluralize(inflection.underscore(this.modelName));
|
|
415
|
-
const queryMode = params.queryMode ?? (params.id !== undefined ? "list" : "both");
|
|
416
|
-
|
|
417
|
-
const { select, virtual, joins, loaders } = subsetQuery;
|
|
418
|
-
const qb = build({
|
|
419
|
-
qb: db.from(baseTable),
|
|
420
|
-
db,
|
|
421
|
-
select,
|
|
422
|
-
joins,
|
|
423
|
-
virtual,
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
const applyJoinClause = (qb: Knex.QueryBuilder, joins: SubsetQuery["joins"]) => {
|
|
427
|
-
joins.forEach((join) => {
|
|
428
|
-
if (join.join === "inner") {
|
|
429
|
-
qb.innerJoin(`${join.table} as ${join.as}`, this.getJoinClause(db, join));
|
|
430
|
-
} else if (join.join === "outer") {
|
|
431
|
-
qb.leftOuterJoin(`${join.table} as ${join.as}`, this.getJoinClause(db, join));
|
|
432
|
-
}
|
|
433
|
-
});
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
// countQuery
|
|
437
|
-
const total = await (async () => {
|
|
438
|
-
if (queryMode === "list") {
|
|
439
|
-
return undefined;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
const clonedQb = qb.clone().clear("order").clear("offset").clear("limit");
|
|
443
|
-
const parser = new SqlParser.Parser();
|
|
444
|
-
|
|
445
|
-
if (optimizeCountQuery) {
|
|
446
|
-
const parsedQuery = parser.astify(clonedQb.toQuery(), {
|
|
447
|
-
database: Sonamu.config.database.database,
|
|
448
|
-
});
|
|
449
|
-
const tables = getTableNamesFromWhere(parsedQuery);
|
|
450
|
-
const needToJoin = unique(
|
|
451
|
-
tables.flatMap((table) => table.split("__").map((t) => inflection.pluralize(t))),
|
|
452
|
-
);
|
|
453
|
-
applyJoinClause(
|
|
454
|
-
clonedQb,
|
|
455
|
-
joins.filter((j) => needToJoin.includes(j.table)),
|
|
456
|
-
);
|
|
457
|
-
} else {
|
|
458
|
-
applyJoinClause(clonedQb, joins);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
const processedQb = afterBuild?.({ qb: clonedQb, db, select, joins, virtual }) ?? clonedQb;
|
|
462
|
-
|
|
463
|
-
const parsedQuery = parser.astify(processedQb.toQuery(), {
|
|
464
|
-
database: Sonamu.config.database.database,
|
|
465
|
-
});
|
|
466
|
-
const q = Array.isArray(parsedQuery) ? parsedQuery[0] : parsedQuery;
|
|
467
|
-
if (q.type !== "select") {
|
|
468
|
-
throw new Error("Invalid query");
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
const countQuery =
|
|
472
|
-
q.distinct !== null
|
|
473
|
-
? clonedQb
|
|
474
|
-
.clear("select")
|
|
475
|
-
.select(
|
|
476
|
-
db.raw(
|
|
477
|
-
`COUNT(DISTINCT \`${getTableName(q.columns[0].expr)}\`.\`${q.columns[0].expr.column}\`) as total`,
|
|
478
|
-
),
|
|
479
|
-
)
|
|
480
|
-
.first()
|
|
481
|
-
: clonedQb.clear("select").count("*", { as: "total" }).first();
|
|
482
|
-
const countRow: { total?: number } = await countQuery;
|
|
483
|
-
|
|
484
|
-
if (debug === true || debug === "count") {
|
|
485
|
-
console.debug("DEBUG: count query", chalk.blue(countQuery.toQuery().toString()));
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
return countRow?.total ?? 0;
|
|
489
|
-
})();
|
|
490
|
-
|
|
491
|
-
// listQuery
|
|
492
|
-
const rows = await (async () => {
|
|
493
|
-
if (queryMode === "count") {
|
|
494
|
-
return [];
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
if (params.num !== 0) {
|
|
498
|
-
assert(params.num);
|
|
499
|
-
qb.limit(params.num);
|
|
500
|
-
qb.offset(params.num * ((params.page ?? 1) - 1));
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
const clonedQb = qb.clone().select(select);
|
|
504
|
-
applyJoinClause(clonedQb, joins);
|
|
505
|
-
|
|
506
|
-
const listQuery = afterBuild?.({ qb: clonedQb, db, select, joins, virtual }) ?? clonedQb;
|
|
507
|
-
|
|
508
|
-
let rows = await listQuery;
|
|
509
|
-
if (debug === true || debug === "list") {
|
|
510
|
-
console.debug("DEBUG: list query", chalk.blue(listQuery.toQuery().toString()));
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
rows = await this.useLoaders(db, rows, loaders);
|
|
514
|
-
rows = this.hydrate(rows);
|
|
515
|
-
return rows;
|
|
516
|
-
})();
|
|
517
|
-
|
|
518
|
-
return { rows, total, subsetQuery, qb };
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// Legacy Loader 처리 (Puri 도입 전 호환용)
|
|
522
|
-
async useLoaders(db: Knex, rows: UnknownDBRecord[], loaders: SubsetQuery["loaders"]) {
|
|
523
|
-
if (loaders.length === 0) {
|
|
524
|
-
return rows;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
for (const loader of loaders) {
|
|
528
|
-
let subQ: Knex.QueryBuilder;
|
|
529
|
-
let subRows: UnknownDBRecord[];
|
|
530
|
-
let toCol: string;
|
|
531
|
-
|
|
532
|
-
const fromIds = rows.map((row) => row[loader.manyJoin.idField]);
|
|
533
|
-
|
|
534
|
-
if (loader.manyJoin.through === undefined) {
|
|
535
|
-
// HasMany
|
|
536
|
-
const idColumn = `${loader.manyJoin.toTable}.${loader.manyJoin.toCol}`;
|
|
537
|
-
subQ = db(loader.manyJoin.toTable)
|
|
538
|
-
.whereIn(idColumn as string, fromIds as string[])
|
|
539
|
-
.select([...loader.select, idColumn]);
|
|
540
|
-
|
|
541
|
-
loader.oneJoins.forEach((join) => {
|
|
542
|
-
if (join.join === "inner") {
|
|
543
|
-
subQ.innerJoin(`${join.table} as ${join.as}`, this.getJoinClause(db, join));
|
|
544
|
-
} else if (join.join === "outer") {
|
|
545
|
-
subQ.leftOuterJoin(`${join.table} as ${join.as}`, this.getJoinClause(db, join));
|
|
546
|
-
}
|
|
547
|
-
});
|
|
548
|
-
toCol = loader.manyJoin.toCol;
|
|
549
|
-
} else {
|
|
550
|
-
// ManyToMany
|
|
551
|
-
const idColumn = `${loader.manyJoin.through.table}.${loader.manyJoin.through.fromCol}`;
|
|
552
|
-
subQ = db(loader.manyJoin.through.table)
|
|
553
|
-
.join(
|
|
554
|
-
loader.manyJoin.toTable,
|
|
555
|
-
`${loader.manyJoin.through.table}.${loader.manyJoin.through.toCol}`,
|
|
556
|
-
`${loader.manyJoin.toTable}.${loader.manyJoin.toCol}`,
|
|
557
|
-
)
|
|
558
|
-
.whereIn(idColumn as string, fromIds as string[])
|
|
559
|
-
.select(unique([...loader.select, idColumn]));
|
|
560
|
-
|
|
561
|
-
loader.oneJoins.forEach((join) => {
|
|
562
|
-
if (join.join === "inner") {
|
|
563
|
-
subQ.innerJoin(`${join.table} as ${join.as}`, this.getJoinClause(db, join));
|
|
564
|
-
} else if (join.join === "outer") {
|
|
565
|
-
subQ.leftOuterJoin(`${join.table} as ${join.as}`, this.getJoinClause(db, join));
|
|
566
|
-
}
|
|
567
|
-
});
|
|
568
|
-
toCol = loader.manyJoin.through.fromCol;
|
|
569
|
-
}
|
|
570
|
-
subRows = await subQ;
|
|
571
|
-
|
|
572
|
-
if (loader.loaders) {
|
|
573
|
-
subRows = await this.useLoaders(db, subRows, loader.loaders);
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
const subRowGroups = group(subRows, (row) => row[toCol] as string);
|
|
577
|
-
rows = rows.map((row) => {
|
|
578
|
-
row[loader.as] = (subRowGroups[row[loader.manyJoin.idField] as string] ?? []).map((r) =>
|
|
579
|
-
omit(r, [toCol]),
|
|
580
|
-
);
|
|
581
|
-
return row;
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
return rows;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
getJoinClause(db: Knex<any, unknown>, join: SubsetQuery["joins"][number]): Knex.Raw<any> {
|
|
588
|
-
if (!isCustomJoinClause(join)) {
|
|
589
|
-
return db.raw(`${join.from} = ${join.to}`);
|
|
590
|
-
} else {
|
|
591
|
-
return db.raw(join.custom);
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
getUpsertBuilder(): UpsertBuilder {
|
|
596
|
-
return new UpsertBuilder();
|
|
597
|
-
}
|
|
598
367
|
}
|
|
599
368
|
|
|
600
369
|
/**
|
|
@@ -261,16 +261,15 @@ export class UpsertBuilder {
|
|
|
261
261
|
.select(selectFields)
|
|
262
262
|
.whereIn("uuid", uuids as readonly string[]);
|
|
263
263
|
} else {
|
|
264
|
-
// UPSERT
|
|
264
|
+
// UPSERT 모드: onConflict로 중복 처리
|
|
265
265
|
const conflictColumns = table.uniqueIndexes[0].columns;
|
|
266
266
|
const updateColumns = Object.keys(dataChunk[0]).filter(
|
|
267
267
|
(col) => col !== "uuid" && !conflictColumns.includes(col),
|
|
268
268
|
);
|
|
269
269
|
|
|
270
|
-
// RETURNING으로 결과 받기
|
|
271
270
|
const query = wdb.insert(dataChunk).into(tableName).onConflict(conflictColumns);
|
|
272
271
|
|
|
273
|
-
// updateColumns
|
|
272
|
+
// updateColumns 유무에 따라 ignore/merge 선택하고 RETURNING으로 결과 받기
|
|
274
273
|
if (updateColumns.length === 0) {
|
|
275
274
|
resultRows = await query.ignore().returning(selectFields);
|
|
276
275
|
} else {
|
package/src/syncer/syncer.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { hot } from "@sonamu-kit/
|
|
1
|
+
import { hot } from "@sonamu-kit/hmr-hook";
|
|
2
2
|
import assert from "assert";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
@@ -122,7 +122,7 @@ export class Syncer {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
// 싱크 작업이 끝나면 모든 모듈을 로드합니다.
|
|
125
|
-
//
|
|
125
|
+
// hmr-hook에 의해 invalidate된 부분들이 아니라면 캐시 그대로 유지합니다.
|
|
126
126
|
await this.autoloadTypes();
|
|
127
127
|
await this.autoloadModels();
|
|
128
128
|
await this.autoloadApis();
|
|
@@ -62,7 +62,7 @@ type AnyZodDefault = z.ZodDefault<z.ZodType>;
|
|
|
62
62
|
type AnyZodUnion = z.ZodUnion<z.ZodType[]>;
|
|
63
63
|
type AnyZodArray = z.ZodArray<z.ZodType>;
|
|
64
64
|
type AnyZodOptional = z.ZodOptional<z.ZodType>;
|
|
65
|
-
|
|
65
|
+
type AnyZodTemplateLiteral = z.ZodTemplateLiteral<string>;
|
|
66
66
|
/**
|
|
67
67
|
* Zod 타입 ID로부터 동적으로 Zod 스키마를 로드합니다.
|
|
68
68
|
* dist 디렉토리에서 ESM으로 import하여 가져옵니다.
|
|
@@ -261,7 +261,6 @@ export function propNodeToZodTypeDef(propNode: EntityPropNode, injectImportKeys:
|
|
|
261
261
|
}
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
// TODO(Haze, 251031): "template_literal", "file"에 대한 지원이 필요함.
|
|
265
264
|
export function zodTypeToTsTypeDef(zt: z.ZodType): string {
|
|
266
265
|
switch (zt.def.type) {
|
|
267
266
|
case "string":
|
|
@@ -325,12 +324,40 @@ export function zodTypeToTsTypeDef(zt: z.ZodType): string {
|
|
|
325
324
|
}
|
|
326
325
|
case "optional":
|
|
327
326
|
return `${zodTypeToTsTypeDef((zt as AnyZodOptional).def.innerType)} | undefined`;
|
|
327
|
+
case "template_literal": {
|
|
328
|
+
const def = (zt as AnyZodTemplateLiteral).def;
|
|
329
|
+
|
|
330
|
+
// 빈 template literal은 string으로 폴백
|
|
331
|
+
if (!def.parts || def.parts.length === 0) {
|
|
332
|
+
return "string";
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// 각 part를 TypeScript 타입 문자열로 변환
|
|
336
|
+
const parts = def.parts.map((part: unknown) => {
|
|
337
|
+
// 리터럴 값 (string, number, boolean, null, undefined)
|
|
338
|
+
if (typeof part === "string") {
|
|
339
|
+
return `${part}`;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ZodType - 재귀적으로 변환
|
|
343
|
+
if (part && typeof part === "object" && (part as z.ZodType)._zod) {
|
|
344
|
+
const innerType = zodTypeToTsTypeDef(part as z.ZodType);
|
|
345
|
+
return `$\{${innerType}}`;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// 폴백
|
|
349
|
+
return `\${string}`;
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
return `\`${parts.join("")}\``;
|
|
353
|
+
}
|
|
354
|
+
case "file":
|
|
355
|
+
return "File";
|
|
328
356
|
default:
|
|
329
357
|
throw new Error(`처리되지 않은 ZodType ${zt.def.type}`);
|
|
330
358
|
}
|
|
331
359
|
}
|
|
332
360
|
|
|
333
|
-
// TODO(Haze, 251031): "template_literal", "file"에 대한 지원이 필요함.
|
|
334
361
|
/**
|
|
335
362
|
* Zod 타입 인스턴스를 해당하는 Zod 코드 문자열로 변환합니다.
|
|
336
363
|
*/
|
|
@@ -413,6 +440,31 @@ export function zodTypeToZodCode(zt: z.ZodType): string {
|
|
|
413
440
|
return `${zodTypeToZodCode((zt as z.ZodOptional<z.ZodType>).def.innerType)}.optional()`;
|
|
414
441
|
case "file":
|
|
415
442
|
return `z.file()`;
|
|
443
|
+
case "template_literal": {
|
|
444
|
+
const def = (zt as AnyZodTemplateLiteral).def;
|
|
445
|
+
|
|
446
|
+
// 빈 template literal
|
|
447
|
+
if (!def.parts || def.parts.length === 0) {
|
|
448
|
+
return "z.templateLiteral([])";
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// 각 part를 Zod 코드 문자열로 변환
|
|
452
|
+
const parts = def.parts.map((part: unknown) => {
|
|
453
|
+
// 문자열 리터럴
|
|
454
|
+
if (typeof part === "string") {
|
|
455
|
+
return `"${part}"`;
|
|
456
|
+
}
|
|
457
|
+
// ZodType - 재귀적으로 변환
|
|
458
|
+
if (part && typeof part === "object" && (part as z.ZodType)._zod) {
|
|
459
|
+
return zodTypeToZodCode(part as z.ZodType);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// 폴백
|
|
463
|
+
return "z.string()";
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
return `z.templateLiteral([${parts.join(", ")}])`;
|
|
467
|
+
}
|
|
416
468
|
case "intersection": {
|
|
417
469
|
const zIntersectionDef = (zt as z.ZodIntersection<z.ZodType, z.ZodType>).def;
|
|
418
470
|
return `z.intersection(${zodTypeToZodCode(zIntersectionDef.left)}, ${zodTypeToZodCode(zIntersectionDef.right)})`;
|
|
@@ -519,6 +571,8 @@ function resolveRenderType(key: string, zodType: z.ZodTypeAny): RenderingNode["r
|
|
|
519
571
|
return "string-plain";
|
|
520
572
|
} else if (zodType instanceof z.ZodLiteral) {
|
|
521
573
|
return "string-plain";
|
|
574
|
+
} else if (zodType instanceof z.ZodTemplateLiteral) {
|
|
575
|
+
return "string-plain";
|
|
522
576
|
} else {
|
|
523
577
|
throw new Error(`타입 파싱 불가 ${key} ${zodType.def.type}`);
|
|
524
578
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hot-hook-register.d.ts","sourceRoot":"","sources":["../../src/bin/hot-hook-register.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,OAAO,EAAE,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"loader-register.d.ts","sourceRoot":"","sources":["../../src/bin/loader-register.ts"],"names":[],"mappings":""}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { register } from "node:module";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import { exists } from "../utils/fs-utils.js";
|
|
4
|
-
import { findApiRootPath } from "../utils/utils.js";
|
|
5
|
-
/**
|
|
6
|
-
* @sonamu-kit/loader/loader를 등록하는 스크립트입니다.
|
|
7
|
-
* 이 스크립트는 sonamu cli로 dev 실행할 때 --import로 실행됩니다.
|
|
8
|
-
*/ async function setupSwcConfig() {
|
|
9
|
-
try {
|
|
10
|
-
const apiRoot = findApiRootPath();
|
|
11
|
-
// 프로젝트 루트에서 .swcrc 찾기
|
|
12
|
-
const projectSwcrcPath = path.join(apiRoot, ".swcrc");
|
|
13
|
-
if (await exists(projectSwcrcPath)) {
|
|
14
|
-
// 사용자 프로젝트에 .swcrc가 있으면 우선으로 사용합니다.
|
|
15
|
-
process.env.SWCRC_PATH = projectSwcrcPath;
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
// 아니라면 sonamu가 관리하는 .swcrc.project-default를 가져다 씁니다.
|
|
19
|
-
const sonamuSwcrcPath = path.join(import.meta.dirname, "..", "..", ".swcrc.project-default");
|
|
20
|
-
if (await exists(sonamuSwcrcPath)) {
|
|
21
|
-
process.env.SWCRC_PATH = sonamuSwcrcPath;
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
} catch {
|
|
25
|
-
// 환경 변수 설정 실패는 무시 (loader가 기본 설정 사용)
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
// swc 설정 파일 경로를 환경 변수로 설정
|
|
29
|
-
await setupSwcConfig();
|
|
30
|
-
register("@sonamu-kit/loader/loader", {
|
|
31
|
-
parentURL: import.meta.url
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9iaW4vbG9hZGVyLXJlZ2lzdGVyLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHJlZ2lzdGVyIH0gZnJvbSBcIm5vZGU6bW9kdWxlXCI7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gXCJub2RlOnBhdGhcIjtcbmltcG9ydCB7IGV4aXN0cyB9IGZyb20gXCIuLi91dGlscy9mcy11dGlscy5qc1wiO1xuaW1wb3J0IHsgZmluZEFwaVJvb3RQYXRoIH0gZnJvbSBcIi4uL3V0aWxzL3V0aWxzLmpzXCI7XG5cbi8qKlxuICogQHNvbmFtdS1raXQvbG9hZGVyL2xvYWRlcuulvCDrk7HroZ3tlZjripQg7Iqk7YGs66a97Yq47J6F64uI64ukLlxuICog7J20IOyKpO2BrOumve2KuOuKlCBzb25hbXUgY2xp66GcIGRldiDsi6TtlontlaAg65WMIC0taW1wb3J066GcIOyLpO2WieuQqeuLiOuLpC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gc2V0dXBTd2NDb25maWcoKSB7XG4gIHRyeSB7XG4gICAgY29uc3QgYXBpUm9vdCA9IGZpbmRBcGlSb290UGF0aCgpO1xuXG4gICAgLy8g7ZSE66Gc7KCd7Yq4IOujqO2KuOyXkOyEnCAuc3djcmMg7LC+6riwXG4gICAgY29uc3QgcHJvamVjdFN3Y3JjUGF0aCA9IHBhdGguam9pbihhcGlSb290LCBcIi5zd2NyY1wiKTtcbiAgICBpZiAoYXdhaXQgZXhpc3RzKHByb2plY3RTd2NyY1BhdGgpKSB7XG4gICAgICAvLyDsgqzsmqnsnpAg7ZSE66Gc7KCd7Yq47JeQIC5zd2NyY+qwgCDsnojsnLzrqbQg7Jqw7ISg7Jy866GcIOyCrOyaqe2VqeuLiOuLpC5cbiAgICAgIHByb2Nlc3MuZW52LlNXQ1JDX1BBVEggPSBwcm9qZWN0U3djcmNQYXRoO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIOyVhOuLiOudvOuptCBzb25hbXXqsIAg6rSA66as7ZWY64qUIC5zd2NyYy5wcm9qZWN0LWRlZmF1bHTrpbwg6rCA7KC464ukIOyUgeuLiOuLpC5cbiAgICBjb25zdCBzb25hbXVTd2NyY1BhdGggPSBwYXRoLmpvaW4oaW1wb3J0Lm1ldGEuZGlybmFtZSwgXCIuLlwiLCBcIi4uXCIsIFwiLnN3Y3JjLnByb2plY3QtZGVmYXVsdFwiKTtcbiAgICBpZiAoYXdhaXQgZXhpc3RzKHNvbmFtdVN3Y3JjUGF0aCkpIHtcbiAgICAgIHByb2Nlc3MuZW52LlNXQ1JDX1BBVEggPSBzb25hbXVTd2NyY1BhdGg7XG4gICAgICByZXR1cm47XG4gICAgfVxuICB9IGNhdGNoIHtcbiAgICAvLyDtmZjqsr0g67OA7IiYIOyEpOyglSDsi6TtjKjripQg66y07IucIChsb2FkZXLqsIAg6riw67O4IOyEpOyglSDsgqzsmqkpXG4gIH1cbn1cblxuLy8gc3djIOyEpOyglSDtjIzsnbwg6rK966Gc66W8IO2ZmOqyvSDrs4DsiJjroZwg7ISk7KCVXG5hd2FpdCBzZXR1cFN3Y0NvbmZpZygpO1xuXG5yZWdpc3RlcihcIkBzb25hbXUta2l0L2xvYWRlci9sb2FkZXJcIiwge1xuICBwYXJlbnRVUkw6IGltcG9ydC5tZXRhLnVybCxcbn0pO1xuIl0sIm5hbWVzIjpbInJlZ2lzdGVyIiwicGF0aCIsImV4aXN0cyIsImZpbmRBcGlSb290UGF0aCIsInNldHVwU3djQ29uZmlnIiwiYXBpUm9vdCIsInByb2plY3RTd2NyY1BhdGgiLCJqb2luIiwicHJvY2VzcyIsImVudiIsIlNXQ1JDX1BBVEgiLCJzb25hbXVTd2NyY1BhdGgiLCJkaXJuYW1lIiwicGFyZW50VVJMIiwidXJsIl0sIm1hcHBpbmdzIjoiQUFBQSxTQUFTQSxRQUFRLFFBQVEsY0FBYztBQUN2QyxZQUFZQyxVQUFVLFlBQVk7QUFDbEMsU0FBU0MsTUFBTSxRQUFRLHVCQUF1QjtBQUM5QyxTQUFTQyxlQUFlLFFBQVEsb0JBQW9CO0FBRXBEOzs7Q0FHQyxHQUNELGVBQWVDO0lBQ2IsSUFBSTtRQUNGLE1BQU1DLFVBQVVGO1FBRWhCLHNCQUFzQjtRQUN0QixNQUFNRyxtQkFBbUJMLEtBQUtNLElBQUksQ0FBQ0YsU0FBUztRQUM1QyxJQUFJLE1BQU1ILE9BQU9JLG1CQUFtQjtZQUNsQyxvQ0FBb0M7WUFDcENFLFFBQVFDLEdBQUcsQ0FBQ0MsVUFBVSxHQUFHSjtZQUN6QjtRQUNGO1FBRUEscURBQXFEO1FBQ3JELE1BQU1LLGtCQUFrQlYsS0FBS00sSUFBSSxDQUFDLFlBQVlLLE9BQU8sRUFBRSxNQUFNLE1BQU07UUFDbkUsSUFBSSxNQUFNVixPQUFPUyxrQkFBa0I7WUFDakNILFFBQVFDLEdBQUcsQ0FBQ0MsVUFBVSxHQUFHQztZQUN6QjtRQUNGO0lBQ0YsRUFBRSxPQUFNO0lBQ04scUNBQXFDO0lBQ3ZDO0FBQ0Y7QUFFQSwwQkFBMEI7QUFDMUIsTUFBTVA7QUFFTkosU0FBUyw2QkFBNkI7SUFDcENhLFdBQVcsWUFBWUMsR0FBRztBQUM1QiJ9
|