sonamu 0.7.16 → 0.7.18
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/providers/rtzr/error.d.ts +1 -1
- package/dist/ai/providers/rtzr/error.d.ts.map +1 -1
- package/dist/api/config.d.ts +1 -0
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +1 -1
- package/dist/api/decorators.d.ts +1 -1
- package/dist/api/decorators.d.ts.map +1 -1
- package/dist/api/decorators.js +1 -1
- package/dist/api/sonamu.d.ts +3 -1
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +48 -38
- package/dist/syncer/checksum.d.ts +8 -3
- package/dist/syncer/checksum.d.ts.map +1 -1
- package/dist/syncer/checksum.js +17 -9
- package/dist/syncer/code-generator.js +7 -2
- package/dist/syncer/syncer.d.ts +6 -6
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/syncer/syncer.js +27 -13
- package/dist/template/implementations/model.template.js +5 -5
- package/dist/template/implementations/services.template.d.ts +17 -0
- package/dist/template/implementations/services.template.d.ts.map +1 -0
- package/dist/template/implementations/services.template.js +180 -0
- package/dist/template/implementations/view_form.template.js +2 -2
- package/dist/template/implementations/view_id_async_select.template.js +2 -2
- package/dist/template/implementations/view_list.template.js +5 -5
- package/dist/types/types.d.ts +2 -14
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +3 -15
- package/dist/ui/ai-api.d.ts +2 -0
- package/dist/ui/ai-api.d.ts.map +1 -1
- package/dist/ui/ai-api.js +43 -49
- package/dist/ui/ai-client.d.ts +10 -0
- package/dist/ui/ai-client.d.ts.map +1 -1
- package/dist/ui/ai-client.js +457 -437
- package/dist/ui/api.d.ts.map +1 -1
- package/dist/ui/api.js +3 -1
- package/dist/ui-web/assets/index-DFqVuxOB.js +92 -0
- package/dist/ui-web/index.html +1 -1
- package/package.json +9 -5
- package/src/api/config.ts +3 -0
- package/src/api/decorators.ts +6 -1
- package/src/api/sonamu.ts +68 -50
- package/src/shared/app.shared.ts.txt +1 -1
- package/src/shared/web.shared.ts.txt +0 -43
- package/src/syncer/checksum.ts +31 -9
- package/src/syncer/code-generator.ts +8 -1
- package/src/syncer/syncer.ts +38 -26
- package/src/template/implementations/model.template.ts +4 -4
- package/src/template/implementations/services.template.ts +265 -0
- package/src/template/implementations/view_form.template.ts +1 -1
- package/src/template/implementations/view_id_async_select.template.ts +1 -1
- package/src/template/implementations/view_list.template.ts +4 -4
- package/src/types/types.ts +2 -14
- package/src/ui/ai-api.ts +61 -60
- package/src/ui/ai-client.ts +535 -499
- package/src/ui/api.ts +3 -0
- package/src/ui/entity.instructions.md +536 -0
- package/dist/template/implementations/service.template.d.ts +0 -29
- package/dist/template/implementations/service.template.d.ts.map +0 -1
- package/dist/template/implementations/service.template.js +0 -202
- package/dist/ui-web/assets/index-BcbbB-BB.js +0 -95
- package/dist/ui-web/assets/provider-utils_false-BKJD46kk.js +0 -1
- package/dist/ui-web/assets/provider-utils_false-Bu5lmX18.js +0 -1
- package/src/template/implementations/service.template.ts +0 -328
package/dist/ui-web/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>{{projectName}}: Sonamu UI</title>
|
|
7
|
-
<script type="module" crossorigin src="/sonamu-ui/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/sonamu-ui/assets/index-DFqVuxOB.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="/sonamu-ui/assets/index-CpaB9P6g.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sonamu",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.18",
|
|
4
4
|
"description": "Sonamu — TypeScript Fullstack API Framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -100,12 +100,13 @@
|
|
|
100
100
|
"typescript": "^5.9.3"
|
|
101
101
|
},
|
|
102
102
|
"peerDependencies": {
|
|
103
|
-
"@ai-sdk/openai": "3.0.0
|
|
104
|
-
"@ai-sdk/provider": "3.0.0
|
|
105
|
-
"@ai-sdk/provider-utils": "4.0.0
|
|
103
|
+
"@ai-sdk/openai": "3.0.0",
|
|
104
|
+
"@ai-sdk/provider": "3.0.0",
|
|
105
|
+
"@ai-sdk/provider-utils": "4.0.0",
|
|
106
|
+
"@ai-sdk/anthropic": "3.0.0",
|
|
106
107
|
"@swc/cli": "^0.7.8",
|
|
107
108
|
"@swc/core": "^1.13.5",
|
|
108
|
-
"ai": "6.0.
|
|
109
|
+
"ai": "6.0.1",
|
|
109
110
|
"fastify": "^4.23.2",
|
|
110
111
|
"knex": "^3.1.0",
|
|
111
112
|
"pgvector": "^0.2.1",
|
|
@@ -122,6 +123,9 @@
|
|
|
122
123
|
"@ai-sdk/provider-utils": {
|
|
123
124
|
"optional": true
|
|
124
125
|
},
|
|
126
|
+
"@ai-sdk/anthropic": {
|
|
127
|
+
"optional": true
|
|
128
|
+
},
|
|
125
129
|
"ai": {
|
|
126
130
|
"optional": true
|
|
127
131
|
},
|
package/src/api/config.ts
CHANGED
package/src/api/decorators.ts
CHANGED
|
@@ -21,7 +21,12 @@ export interface GuardKeys {
|
|
|
21
21
|
user: true;
|
|
22
22
|
}
|
|
23
23
|
export type GuardKey = keyof GuardKeys;
|
|
24
|
-
export type ServiceClient =
|
|
24
|
+
export type ServiceClient =
|
|
25
|
+
| "axios"
|
|
26
|
+
| "axios-multipart"
|
|
27
|
+
| "tanstack-query"
|
|
28
|
+
| "tanstack-mutation"
|
|
29
|
+
| "window-fetch";
|
|
25
30
|
export type ApiDecoratorOptions = {
|
|
26
31
|
httpMethod?: HTTPMethods;
|
|
27
32
|
contentType?:
|
package/src/api/sonamu.ts
CHANGED
|
@@ -363,7 +363,7 @@ class SonamuClass {
|
|
|
363
363
|
(api.options.httpMethod ?? "GET") === request.method.toUpperCase(),
|
|
364
364
|
);
|
|
365
365
|
if (found) {
|
|
366
|
-
return this.
|
|
366
|
+
return this.createApiHandler(found, config)(request, reply);
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
if (request.url.startsWith("/api/")) {
|
|
@@ -385,13 +385,13 @@ class SonamuClass {
|
|
|
385
385
|
server.route({
|
|
386
386
|
method: api.options.httpMethod ?? "GET",
|
|
387
387
|
url: this.config.api.route.prefix + api.path,
|
|
388
|
-
handler: this.
|
|
388
|
+
handler: this.createApiHandler(api, config),
|
|
389
389
|
}); // END server.route
|
|
390
390
|
}
|
|
391
391
|
}
|
|
392
392
|
}
|
|
393
393
|
|
|
394
|
-
|
|
394
|
+
createApiHandler(
|
|
395
395
|
api: ExtendedApi,
|
|
396
396
|
config: SonamuFastifyConfig,
|
|
397
397
|
): (request: FastifyRequest, reply: FastifyReply) => Promise<unknown> {
|
|
@@ -429,56 +429,74 @@ class SonamuClass {
|
|
|
429
429
|
// Content-Type
|
|
430
430
|
reply.type(api.options.contentType ?? "application/json");
|
|
431
431
|
|
|
432
|
-
//
|
|
433
|
-
const
|
|
434
|
-
const createSSE = (<T extends ZodObject>(
|
|
435
|
-
_request: FastifyRequest,
|
|
436
|
-
_reply: FastifyReply,
|
|
437
|
-
_events: T,
|
|
438
|
-
) => createSSEFactory(_request.socket, _reply, _events)).bind(null, request, reply);
|
|
439
|
-
|
|
440
|
-
const context: Context = {
|
|
441
|
-
...(await Promise.resolve(
|
|
442
|
-
config.contextProvider(
|
|
443
|
-
{
|
|
444
|
-
request,
|
|
445
|
-
reply,
|
|
446
|
-
headers: request.headers,
|
|
447
|
-
createSSE,
|
|
448
|
-
naiteStore: Naite.createStore(),
|
|
449
|
-
// auth
|
|
450
|
-
user: request.user ?? null,
|
|
451
|
-
passport: {
|
|
452
|
-
login: request.login.bind(request) as AuthContext["passport"]["login"],
|
|
453
|
-
logout: request.logout.bind(request) as AuthContext["passport"]["logout"],
|
|
454
|
-
},
|
|
455
|
-
},
|
|
456
|
-
request,
|
|
457
|
-
reply,
|
|
458
|
-
),
|
|
459
|
-
)),
|
|
460
|
-
};
|
|
461
|
-
|
|
462
|
-
const model = this.syncer.models[api.modelName];
|
|
463
|
-
return this.asyncLocalStorage.run({ context }, async () => {
|
|
464
|
-
const { ApiParamType } = await import("../types/types");
|
|
465
|
-
// biome-ignore lint/suspicious/noExplicitAny: model은 모델 인스턴스이므로 메서드 호출 가능
|
|
466
|
-
const result = await (model as any)[api.methodName].apply(
|
|
467
|
-
model,
|
|
468
|
-
api.parameters.map((param) => {
|
|
469
|
-
// Context 인젝션
|
|
470
|
-
if (ApiParamType.isContext(param.type)) {
|
|
471
|
-
return context;
|
|
472
|
-
} else {
|
|
473
|
-
return reqBody[param.name];
|
|
474
|
-
}
|
|
475
|
-
}),
|
|
476
|
-
);
|
|
477
|
-
reply.type(api.options.contentType ?? "application/json");
|
|
432
|
+
// Context 생성
|
|
433
|
+
const context: Context = await this.createContext(config, request, reply);
|
|
478
434
|
|
|
479
|
-
|
|
435
|
+
// 모델 메소드 args 생성하여 호출
|
|
436
|
+
const { ApiParamType } = await import("../types/types");
|
|
437
|
+
const args = api.parameters.map((param) => {
|
|
438
|
+
// Context 인젝션
|
|
439
|
+
if (ApiParamType.isContext(param.type)) {
|
|
440
|
+
return context;
|
|
441
|
+
} else {
|
|
442
|
+
return reqBody[param.name];
|
|
443
|
+
}
|
|
480
444
|
});
|
|
445
|
+
return this.invokeModelMethod(api, args, context, reply);
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
async invokeModelMethod(
|
|
450
|
+
api: ExtendedApi,
|
|
451
|
+
args: unknown[],
|
|
452
|
+
context: Context,
|
|
453
|
+
reply: FastifyReply,
|
|
454
|
+
): Promise<unknown> {
|
|
455
|
+
const model = this.syncer.models[api.modelName];
|
|
456
|
+
return this.asyncLocalStorage.run({ context }, async () => {
|
|
457
|
+
// biome-ignore lint/suspicious/noExplicitAny: model은 모델 인스턴스이므로 메서드 호출 가능
|
|
458
|
+
const result = await (model as any)[api.methodName].apply(model, args);
|
|
459
|
+
reply.type(api.options.contentType ?? "application/json");
|
|
460
|
+
|
|
461
|
+
return result;
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
async createContext(
|
|
466
|
+
config: SonamuFastifyConfig,
|
|
467
|
+
request: FastifyRequest,
|
|
468
|
+
reply: FastifyReply,
|
|
469
|
+
): Promise<Context> {
|
|
470
|
+
// createSSEFactory 함수에 미리 request의 socket과 reply를 바인딩.
|
|
471
|
+
const { createSSEFactory } = await import("../stream/sse");
|
|
472
|
+
const createSSE = (<T extends ZodObject>(
|
|
473
|
+
_request: FastifyRequest,
|
|
474
|
+
_reply: FastifyReply,
|
|
475
|
+
_events: T,
|
|
476
|
+
) => createSSEFactory(_request.socket, _reply, _events)).bind(null, request, reply);
|
|
477
|
+
|
|
478
|
+
const context: Context = {
|
|
479
|
+
...(await Promise.resolve(
|
|
480
|
+
config.contextProvider(
|
|
481
|
+
{
|
|
482
|
+
request,
|
|
483
|
+
reply,
|
|
484
|
+
headers: request.headers,
|
|
485
|
+
createSSE,
|
|
486
|
+
naiteStore: Naite.createStore(),
|
|
487
|
+
// auth
|
|
488
|
+
user: request.user ?? null,
|
|
489
|
+
passport: {
|
|
490
|
+
login: request.login.bind(request) as AuthContext["passport"]["login"],
|
|
491
|
+
logout: request.logout.bind(request) as AuthContext["passport"]["logout"],
|
|
492
|
+
},
|
|
493
|
+
},
|
|
494
|
+
request,
|
|
495
|
+
reply,
|
|
496
|
+
),
|
|
497
|
+
)),
|
|
481
498
|
};
|
|
499
|
+
return context;
|
|
482
500
|
}
|
|
483
501
|
|
|
484
502
|
async startWatcher(): Promise<void> {
|
|
@@ -322,7 +322,7 @@ export function useSSEStream<T extends Record<string, any>>(
|
|
|
322
322
|
|
|
323
323
|
// URL에 파라미터 추가 - 절대 URL로 변환
|
|
324
324
|
const queryString = qs.stringify(params);
|
|
325
|
-
const baseUrl = url.startsWith("http") ? url :
|
|
325
|
+
const baseUrl = url.startsWith("http") ? url : `$[[baseUrl]]${url}`;
|
|
326
326
|
const fullUrl = queryString ? `${baseUrl}?${queryString}` : baseUrl;
|
|
327
327
|
|
|
328
328
|
const eventSource = new EventSource(fullUrl);
|
|
@@ -108,49 +108,6 @@ export const SonamuSemanticParams = z
|
|
|
108
108
|
.partial();
|
|
109
109
|
export type SonamuSemanticParams = z.infer<typeof SonamuSemanticParams>;
|
|
110
110
|
|
|
111
|
-
/*
|
|
112
|
-
SWR
|
|
113
|
-
*/
|
|
114
|
-
export type SwrOptions = {
|
|
115
|
-
conditional?: () => boolean;
|
|
116
|
-
};
|
|
117
|
-
export type SWRError = {
|
|
118
|
-
name: string;
|
|
119
|
-
message: string;
|
|
120
|
-
statusCode: number;
|
|
121
|
-
};
|
|
122
|
-
export async function swrFetcher(args: [string, object]): Promise<any> {
|
|
123
|
-
try {
|
|
124
|
-
const [url, params] = args;
|
|
125
|
-
const res = await axios.get(`${url}?${qs.stringify(params)}`);
|
|
126
|
-
return res.data;
|
|
127
|
-
} catch (e: any) {
|
|
128
|
-
const error: any = new Error(e.response.data.message ?? e.response.message ?? "Unknown");
|
|
129
|
-
error.statusCode = e.response?.data.statusCode ?? e.response.status;
|
|
130
|
-
throw error;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
export async function swrPostFetcher(args: [string, object]): Promise<any> {
|
|
134
|
-
try {
|
|
135
|
-
const [url, params] = args;
|
|
136
|
-
const res = await axios.post(url, params);
|
|
137
|
-
return res.data;
|
|
138
|
-
} catch (e: any) {
|
|
139
|
-
const error: any = new Error(e.response.data.message ?? e.response.message ?? "Unknown");
|
|
140
|
-
error.statusCode = e.response?.data.statusCode ?? e.response.status;
|
|
141
|
-
throw error;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
export function handleConditional(
|
|
145
|
-
args: [string, object],
|
|
146
|
-
conditional?: () => boolean,
|
|
147
|
-
): [string, object] | null {
|
|
148
|
-
if (conditional) {
|
|
149
|
-
return conditional() ? args : null;
|
|
150
|
-
}
|
|
151
|
-
return args;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
111
|
/*
|
|
155
112
|
Utils
|
|
156
113
|
*/
|
package/src/syncer/checksum.ts
CHANGED
|
@@ -48,22 +48,38 @@ export async function renewChecksums(): Promise<void> {
|
|
|
48
48
|
await saveChecksums(calculatedChecksums);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
export type FileOrData =
|
|
52
|
+
| {
|
|
53
|
+
path: PathLike;
|
|
54
|
+
}
|
|
55
|
+
| {
|
|
56
|
+
data: string;
|
|
57
|
+
};
|
|
58
|
+
|
|
51
59
|
/**
|
|
52
60
|
* 두 파일의 내용이 같은지 체크섬으로 비교합니다.
|
|
53
61
|
* 만약 파일이 둘 중 하나라도 없다면 비교 불가로 false 반환합니다.
|
|
54
|
-
* @param one 파일 경로
|
|
55
|
-
* @param two 파일 경로
|
|
62
|
+
* @param one 파일 경로 혹은 데이터
|
|
63
|
+
* @param two 파일 경로 혹은 데이터
|
|
56
64
|
* @returns boolean
|
|
57
65
|
*/
|
|
58
|
-
export async function areFilesSame(
|
|
59
|
-
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
66
|
+
export async function areFilesSame(...files: FileOrData[]): Promise<boolean> {
|
|
67
|
+
const checksums: string[] = [];
|
|
62
68
|
|
|
63
|
-
const
|
|
64
|
-
|
|
69
|
+
for (const file of files) {
|
|
70
|
+
if ("path" in file && !(await exists(file.path))) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
checksums.push(
|
|
75
|
+
"path" in file ? await getChecksumOfFile(file.path) : getChecksumOfData(file.data),
|
|
76
|
+
);
|
|
77
|
+
}
|
|
65
78
|
|
|
66
|
-
return
|
|
79
|
+
return checksums.every(
|
|
80
|
+
// 다음 체크섬과 비교, 만약 마지막 체크섬일 때는 첫 번째 체크섬과 비교
|
|
81
|
+
(checksum, index) => checksum === checksums[index === checksums.length - 1 ? 0 : index + 1],
|
|
82
|
+
);
|
|
67
83
|
}
|
|
68
84
|
|
|
69
85
|
async function getCurrentChecksums(): Promise<PathAndChecksum[]> {
|
|
@@ -125,6 +141,12 @@ async function saveChecksums(checksums: PathAndChecksum[]): Promise<void> {
|
|
|
125
141
|
console.log("checksum saved", checksumFilePath);
|
|
126
142
|
}
|
|
127
143
|
|
|
144
|
+
function getChecksumOfData(data: string): string {
|
|
145
|
+
const hash = crypto.createHash("sha1");
|
|
146
|
+
hash.update(data);
|
|
147
|
+
return hash.digest("hex");
|
|
148
|
+
}
|
|
149
|
+
|
|
128
150
|
async function getChecksumOfFile(filePath: PathLike): Promise<string> {
|
|
129
151
|
return new Promise<string>((resolve, reject) => {
|
|
130
152
|
const hash = crypto.createHash("sha1");
|
|
@@ -115,7 +115,14 @@ async function resolveRenderedTemplate(
|
|
|
115
115
|
const importDefs = importKeys
|
|
116
116
|
.reduce(
|
|
117
117
|
(r, importKey) => {
|
|
118
|
-
|
|
118
|
+
let modulePath = importKey;
|
|
119
|
+
try {
|
|
120
|
+
modulePath = EntityManager.getModulePath(importKey);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`[resolveRenderedTemplate:${key}] ${importKey} 모듈 경로 찾기 실패: ${error}`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
119
126
|
let importPath = modulePath;
|
|
120
127
|
if (modulePath.includes("/") || modulePath.includes(".")) {
|
|
121
128
|
importPath = wrapIf(path.relative(path.dirname(filePath), modulePath), (p) => [
|
package/src/syncer/syncer.ts
CHANGED
|
@@ -161,6 +161,13 @@ export class Syncer {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
async copySharedToTargets(targets: string[]): Promise<void> {
|
|
164
|
+
// 특정 변수 치환을 위해서 사용합니다.
|
|
165
|
+
const convertMap = {
|
|
166
|
+
baseUrl:
|
|
167
|
+
Sonamu.config.server.baseUrl ??
|
|
168
|
+
`http://${Sonamu.config.server.listen?.host ?? "localhost"}:${Sonamu.config.server.listen?.port ?? 3000}`,
|
|
169
|
+
};
|
|
170
|
+
|
|
164
171
|
for (const target of targets) {
|
|
165
172
|
// 지금 가져가려는 이 파일은 Sonamu 코드베이스의 일부입니다.
|
|
166
173
|
// 그런데 dist 속 빌드된 소스 코드 파일이 필요한 것이 아니고, src에만 있는 텍스트 파일이 필요합니다.
|
|
@@ -178,6 +185,12 @@ export class Syncer {
|
|
|
178
185
|
);
|
|
179
186
|
}
|
|
180
187
|
|
|
188
|
+
const fullText = await readFile(srcPath, "utf-8");
|
|
189
|
+
const convertedText = Object.entries(convertMap).reduce(
|
|
190
|
+
(acc, [key, value]) => acc.replace(`$[[${key}]]`, value),
|
|
191
|
+
fullText,
|
|
192
|
+
);
|
|
193
|
+
|
|
181
194
|
// 이건 프로젝트에 .ts 소스 코드 파일을 생성하는 것이므로 src의 .ts 경로로 갑니다.
|
|
182
195
|
const destPath = path.join(Sonamu.appRootPath, target, "src/services/sonamu.shared.ts");
|
|
183
196
|
|
|
@@ -187,12 +200,11 @@ export class Syncer {
|
|
|
187
200
|
console.warn(`Created directory '${path.dirname(destPath)}' because it did not exist.`);
|
|
188
201
|
}
|
|
189
202
|
|
|
190
|
-
if (await areFilesSame(
|
|
203
|
+
if (await areFilesSame({ data: convertedText }, { path: destPath })) {
|
|
191
204
|
continue;
|
|
192
205
|
}
|
|
193
206
|
|
|
194
|
-
await writeFile(destPath,
|
|
195
|
-
|
|
207
|
+
await writeFile(destPath, convertedText);
|
|
196
208
|
!isTest() &&
|
|
197
209
|
console.log(
|
|
198
210
|
chalk.bold("Copied: ") + chalk.blue(path.relative(Sonamu.appRootPath, destPath)),
|
|
@@ -375,22 +387,7 @@ export class Syncer {
|
|
|
375
387
|
}
|
|
376
388
|
|
|
377
389
|
/**
|
|
378
|
-
*
|
|
379
|
-
* @returns 생성된 파일 경로 배열.
|
|
380
|
-
*/
|
|
381
|
-
async actionGenerateSchemas(): Promise<AbsolutePath[]> {
|
|
382
|
-
return (
|
|
383
|
-
await Promise.all([
|
|
384
|
-
generateTemplate("generated_sso", {}, { overwrite: true }),
|
|
385
|
-
generateTemplate("generated", {}, { overwrite: true }),
|
|
386
|
-
])
|
|
387
|
-
)
|
|
388
|
-
.flat()
|
|
389
|
-
.flat();
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* *.service.ts를 생성합니다.
|
|
390
|
+
* services.generated.ts를 생성합니다.
|
|
394
391
|
* @param paramsArray
|
|
395
392
|
* @returns 생성된 파일 경로 배열.
|
|
396
393
|
*/
|
|
@@ -400,14 +397,29 @@ export class Syncer {
|
|
|
400
397
|
}[],
|
|
401
398
|
): Promise<string[]> {
|
|
402
399
|
Naite.t("actionGenerateServices", paramsArray);
|
|
400
|
+
|
|
401
|
+
// services.generated.ts 통합 파일 생성
|
|
402
|
+
const servicesFile = await generateTemplate(
|
|
403
|
+
"services",
|
|
404
|
+
{},
|
|
405
|
+
{
|
|
406
|
+
overwrite: true,
|
|
407
|
+
},
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
return [...servicesFile];
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* sonamu.generated.ts와 sonamu.generated.sso.ts를 생성합니다.
|
|
415
|
+
* @returns 생성된 파일 경로 배열.
|
|
416
|
+
*/
|
|
417
|
+
async actionGenerateSchemas(): Promise<AbsolutePath[]> {
|
|
403
418
|
return (
|
|
404
|
-
await Promise.all(
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
}),
|
|
409
|
-
),
|
|
410
|
-
)
|
|
419
|
+
await Promise.all([
|
|
420
|
+
generateTemplate("generated_sso", {}, { overwrite: true }),
|
|
421
|
+
generateTemplate("generated", {}, { overwrite: true }),
|
|
422
|
+
])
|
|
411
423
|
)
|
|
412
424
|
.flat()
|
|
413
425
|
.flat();
|
|
@@ -63,7 +63,7 @@ class ${entityId}ModelClass extends BaseModelClass<
|
|
|
63
63
|
> {
|
|
64
64
|
modelName = "${entityId}";
|
|
65
65
|
|
|
66
|
-
@api({ httpMethod: "GET", clients: ["axios", "
|
|
66
|
+
@api({ httpMethod: "GET", clients: ["axios", "tanstack-query"], resourceName: "${entityId}" })
|
|
67
67
|
async findById<T extends ${entityId}SubsetKey>(
|
|
68
68
|
subset: T,
|
|
69
69
|
id: number
|
|
@@ -93,7 +93,7 @@ class ${entityId}ModelClass extends BaseModelClass<
|
|
|
93
93
|
return rows[0] ?? null;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
@api({ httpMethod: "GET", clients: ["axios", "
|
|
96
|
+
@api({ httpMethod: "GET", clients: ["axios", "tanstack-query"], resourceName: "${names.capitalPlural}" })
|
|
97
97
|
async findMany<T extends ${entityId}SubsetKey, LP extends ${entityId}ListParams>(
|
|
98
98
|
subset: T,
|
|
99
99
|
rawParams?: LP,
|
|
@@ -156,7 +156,7 @@ class ${entityId}ModelClass extends BaseModelClass<
|
|
|
156
156
|
});
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
@api({ httpMethod: "POST" })
|
|
159
|
+
@api({ httpMethod: "POST", clients: ["axios", "tanstack-mutation"] })
|
|
160
160
|
async save(
|
|
161
161
|
spa: ${entityId}SaveParams[]
|
|
162
162
|
): Promise<number[]> {
|
|
@@ -175,7 +175,7 @@ class ${entityId}ModelClass extends BaseModelClass<
|
|
|
175
175
|
});
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
@api({ httpMethod: "POST", guards: [ "admin" ] })
|
|
178
|
+
@api({ httpMethod: "POST", clients: ["axios", "tanstack-mutation"], guards: [ "admin" ] })
|
|
179
179
|
async del(ids: number[]): Promise<number> {
|
|
180
180
|
const wdb = this.getPuri("w");
|
|
181
181
|
|