@spfn/core 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +580 -0
- package/dist/auto-loader-C44TcLmM.d.ts +125 -0
- package/dist/bind-pssq1NRT.d.ts +34 -0
- package/dist/client/index.d.ts +174 -0
- package/dist/client/index.js +179 -0
- package/dist/client/index.js.map +1 -0
- package/dist/codegen/index.d.ts +126 -0
- package/dist/codegen/index.js +970 -0
- package/dist/codegen/index.js.map +1 -0
- package/dist/db/index.d.ts +83 -0
- package/dist/db/index.js +2099 -0
- package/dist/db/index.js.map +1 -0
- package/dist/index.d.ts +379 -0
- package/dist/index.js +13042 -0
- package/dist/index.js.map +1 -0
- package/dist/postgres-errors-CY_Es8EJ.d.ts +1703 -0
- package/dist/route/index.d.ts +72 -0
- package/dist/route/index.js +442 -0
- package/dist/route/index.js.map +1 -0
- package/dist/scripts/index.d.ts +24 -0
- package/dist/scripts/index.js +1157 -0
- package/dist/scripts/index.js.map +1 -0
- package/dist/scripts/templates/api-index.template.txt +10 -0
- package/dist/scripts/templates/api-tag.template.txt +11 -0
- package/dist/scripts/templates/contract.template.txt +87 -0
- package/dist/scripts/templates/entity-type.template.txt +31 -0
- package/dist/scripts/templates/entity.template.txt +19 -0
- package/dist/scripts/templates/index.template.txt +10 -0
- package/dist/scripts/templates/repository.template.txt +37 -0
- package/dist/scripts/templates/routes-id.template.txt +59 -0
- package/dist/scripts/templates/routes-index.template.txt +44 -0
- package/dist/server/index.d.ts +303 -0
- package/dist/server/index.js +12923 -0
- package/dist/server/index.js.map +1 -0
- package/dist/types-SlzTr8ZO.d.ts +143 -0
- package/package.json +119 -0
|
@@ -0,0 +1,970 @@
|
|
|
1
|
+
import { mkdir, writeFile, readdir, stat } from 'fs/promises';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import * as ts from 'typescript';
|
|
4
|
+
import { readFileSync, existsSync, mkdirSync, createWriteStream } from 'fs';
|
|
5
|
+
import pino from 'pino';
|
|
6
|
+
|
|
7
|
+
// src/codegen/contract-scanner.ts
|
|
8
|
+
async function scanContracts(routesDir) {
|
|
9
|
+
const contractFiles = await scanContractFiles(routesDir);
|
|
10
|
+
const mappings = [];
|
|
11
|
+
for (let i = 0; i < contractFiles.length; i++) {
|
|
12
|
+
const filePath = contractFiles[i];
|
|
13
|
+
const exports = extractContractExports(filePath);
|
|
14
|
+
const basePath = getBasePathFromFile(filePath, routesDir);
|
|
15
|
+
for (let j = 0; j < exports.length; j++) {
|
|
16
|
+
const contractExport = exports[j];
|
|
17
|
+
const fullPath = combinePaths(basePath, contractExport.path);
|
|
18
|
+
mappings.push({
|
|
19
|
+
method: contractExport.method,
|
|
20
|
+
path: fullPath,
|
|
21
|
+
contractName: contractExport.name,
|
|
22
|
+
contractImportPath: getImportPathFromRoutes(filePath, routesDir),
|
|
23
|
+
routeFile: "",
|
|
24
|
+
// Not needed anymore
|
|
25
|
+
contractFile: filePath
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return mappings;
|
|
30
|
+
}
|
|
31
|
+
async function scanContractFiles(dir, files = []) {
|
|
32
|
+
try {
|
|
33
|
+
const entries = await readdir(dir);
|
|
34
|
+
for (let i = 0; i < entries.length; i++) {
|
|
35
|
+
const entry = entries[i];
|
|
36
|
+
const fullPath = join(dir, entry);
|
|
37
|
+
const fileStat = await stat(fullPath);
|
|
38
|
+
if (fileStat.isDirectory()) {
|
|
39
|
+
await scanContractFiles(fullPath, files);
|
|
40
|
+
} else if (entry === "contract.ts") {
|
|
41
|
+
files.push(fullPath);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
} catch (error) {
|
|
45
|
+
}
|
|
46
|
+
return files;
|
|
47
|
+
}
|
|
48
|
+
function extractContractExports(filePath) {
|
|
49
|
+
const sourceCode = readFileSync(filePath, "utf-8");
|
|
50
|
+
const sourceFile = ts.createSourceFile(
|
|
51
|
+
filePath,
|
|
52
|
+
sourceCode,
|
|
53
|
+
ts.ScriptTarget.Latest,
|
|
54
|
+
true
|
|
55
|
+
);
|
|
56
|
+
const exports = [];
|
|
57
|
+
function visit(node) {
|
|
58
|
+
if (ts.isVariableStatement(node)) {
|
|
59
|
+
const hasExport = node.modifiers?.some(
|
|
60
|
+
(m) => m.kind === ts.SyntaxKind.ExportKeyword
|
|
61
|
+
);
|
|
62
|
+
if (hasExport && node.declarationList.declarations.length > 0) {
|
|
63
|
+
const declaration = node.declarationList.declarations[0];
|
|
64
|
+
if (ts.isVariableDeclaration(declaration) && ts.isIdentifier(declaration.name) && declaration.initializer && ts.isObjectLiteralExpression(declaration.initializer)) {
|
|
65
|
+
const name = declaration.name.text;
|
|
66
|
+
if (isContractName(name)) {
|
|
67
|
+
const contractData = extractContractData(declaration.initializer);
|
|
68
|
+
if (contractData.method && contractData.path) {
|
|
69
|
+
exports.push({
|
|
70
|
+
name,
|
|
71
|
+
method: contractData.method,
|
|
72
|
+
path: contractData.path
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
ts.forEachChild(node, visit);
|
|
80
|
+
}
|
|
81
|
+
visit(sourceFile);
|
|
82
|
+
return exports;
|
|
83
|
+
}
|
|
84
|
+
function extractContractData(objectLiteral) {
|
|
85
|
+
const result = {};
|
|
86
|
+
for (let i = 0; i < objectLiteral.properties.length; i++) {
|
|
87
|
+
const prop = objectLiteral.properties[i];
|
|
88
|
+
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
|
89
|
+
const propName = prop.name.text;
|
|
90
|
+
if (propName === "method") {
|
|
91
|
+
let value;
|
|
92
|
+
if (ts.isStringLiteral(prop.initializer)) {
|
|
93
|
+
value = prop.initializer.text;
|
|
94
|
+
} else if (ts.isAsExpression(prop.initializer) && ts.isStringLiteral(prop.initializer.expression)) {
|
|
95
|
+
value = prop.initializer.expression.text;
|
|
96
|
+
}
|
|
97
|
+
if (value) result.method = value;
|
|
98
|
+
} else if (propName === "path") {
|
|
99
|
+
let value;
|
|
100
|
+
if (ts.isStringLiteral(prop.initializer)) {
|
|
101
|
+
value = prop.initializer.text;
|
|
102
|
+
} else if (ts.isAsExpression(prop.initializer) && ts.isStringLiteral(prop.initializer.expression)) {
|
|
103
|
+
value = prop.initializer.expression.text;
|
|
104
|
+
}
|
|
105
|
+
if (value) result.path = value;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
function isContractName(name) {
|
|
112
|
+
return name.indexOf("Contract") !== -1 || name.indexOf("contract") !== -1 || name.endsWith("Schema") || name.endsWith("schema");
|
|
113
|
+
}
|
|
114
|
+
function getBasePathFromFile(filePath, routesDir) {
|
|
115
|
+
let relativePath = filePath.replace(routesDir, "");
|
|
116
|
+
if (relativePath.startsWith("/")) {
|
|
117
|
+
relativePath = relativePath.slice(1);
|
|
118
|
+
}
|
|
119
|
+
relativePath = relativePath.replace("/contract.ts", "");
|
|
120
|
+
if (relativePath === "index" || relativePath === "") {
|
|
121
|
+
return "/";
|
|
122
|
+
}
|
|
123
|
+
const segments = relativePath.split("/");
|
|
124
|
+
const transformed = [];
|
|
125
|
+
for (let i = 0; i < segments.length; i++) {
|
|
126
|
+
const seg = segments[i];
|
|
127
|
+
if (seg === "index") {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (seg.startsWith("[") && seg.endsWith("]")) {
|
|
131
|
+
transformed.push(":" + seg.slice(1, -1));
|
|
132
|
+
} else {
|
|
133
|
+
transformed.push(seg);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (transformed.length === 0) {
|
|
137
|
+
return "/";
|
|
138
|
+
}
|
|
139
|
+
return "/" + transformed.join("/");
|
|
140
|
+
}
|
|
141
|
+
function combinePaths(basePath, contractPath) {
|
|
142
|
+
basePath = basePath || "/";
|
|
143
|
+
contractPath = contractPath || "/";
|
|
144
|
+
if (basePath.endsWith("/") && basePath !== "/") {
|
|
145
|
+
basePath = basePath.slice(0, -1);
|
|
146
|
+
}
|
|
147
|
+
if (contractPath.startsWith("/") && contractPath !== "/") {
|
|
148
|
+
if (basePath === "/") {
|
|
149
|
+
return contractPath;
|
|
150
|
+
}
|
|
151
|
+
return basePath + contractPath;
|
|
152
|
+
}
|
|
153
|
+
if (contractPath === "/") {
|
|
154
|
+
return basePath;
|
|
155
|
+
}
|
|
156
|
+
return basePath + "/" + contractPath;
|
|
157
|
+
}
|
|
158
|
+
function getImportPathFromRoutes(filePath, routesDir) {
|
|
159
|
+
let relativePath = filePath.replace(routesDir, "");
|
|
160
|
+
if (relativePath.startsWith("/")) {
|
|
161
|
+
relativePath = relativePath.slice(1);
|
|
162
|
+
}
|
|
163
|
+
if (relativePath.endsWith(".ts")) {
|
|
164
|
+
relativePath = relativePath.slice(0, -3);
|
|
165
|
+
}
|
|
166
|
+
return "@/server/routes/" + relativePath;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/codegen/route-scanner.ts
|
|
170
|
+
function groupByResource(mappings) {
|
|
171
|
+
const grouped = {};
|
|
172
|
+
for (let i = 0; i < mappings.length; i++) {
|
|
173
|
+
const mapping = mappings[i];
|
|
174
|
+
const resource = extractResourceName(mapping.path);
|
|
175
|
+
if (!grouped[resource]) {
|
|
176
|
+
grouped[resource] = [];
|
|
177
|
+
}
|
|
178
|
+
grouped[resource].push(mapping);
|
|
179
|
+
}
|
|
180
|
+
return grouped;
|
|
181
|
+
}
|
|
182
|
+
function extractResourceName(path) {
|
|
183
|
+
const segments = path.slice(1).split("/").filter((s) => s && s !== "*");
|
|
184
|
+
const staticSegments = [];
|
|
185
|
+
for (let i = 0; i < segments.length; i++) {
|
|
186
|
+
const seg = segments[i];
|
|
187
|
+
if (!seg.startsWith(":")) {
|
|
188
|
+
staticSegments.push(seg);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (staticSegments.length === 0) {
|
|
192
|
+
return "root";
|
|
193
|
+
}
|
|
194
|
+
if (staticSegments.length === 1) {
|
|
195
|
+
return staticSegments[0];
|
|
196
|
+
}
|
|
197
|
+
const result = [staticSegments[0]];
|
|
198
|
+
for (let i = 1; i < staticSegments.length; i++) {
|
|
199
|
+
const seg = staticSegments[i];
|
|
200
|
+
result.push(seg.charAt(0).toUpperCase() + seg.slice(1));
|
|
201
|
+
}
|
|
202
|
+
return result.join("");
|
|
203
|
+
}
|
|
204
|
+
async function generateClient(mappings, options) {
|
|
205
|
+
const startTime = Date.now();
|
|
206
|
+
const grouped = groupByResource(mappings);
|
|
207
|
+
const resourceNames = Object.keys(grouped);
|
|
208
|
+
const code = generateClientCode(mappings, grouped, options);
|
|
209
|
+
await mkdir(dirname(options.outputPath), { recursive: true });
|
|
210
|
+
await writeFile(options.outputPath, code, "utf-8");
|
|
211
|
+
const stats = {
|
|
212
|
+
routesScanned: mappings.length,
|
|
213
|
+
contractsFound: mappings.length,
|
|
214
|
+
contractFiles: countUniqueContractFiles(mappings),
|
|
215
|
+
resourcesGenerated: resourceNames.length,
|
|
216
|
+
methodsGenerated: mappings.length,
|
|
217
|
+
duration: Date.now() - startTime
|
|
218
|
+
};
|
|
219
|
+
return stats;
|
|
220
|
+
}
|
|
221
|
+
function generateClientCode(mappings, grouped, options) {
|
|
222
|
+
let code = "";
|
|
223
|
+
code += generateHeader();
|
|
224
|
+
code += generateImports(mappings, options);
|
|
225
|
+
code += generateApiObject(grouped, options);
|
|
226
|
+
code += generateFooter();
|
|
227
|
+
return code;
|
|
228
|
+
}
|
|
229
|
+
function generateHeader() {
|
|
230
|
+
return `/**
|
|
231
|
+
* Auto-generated API Client
|
|
232
|
+
*
|
|
233
|
+
* Generated by @spfn/core codegen
|
|
234
|
+
* DO NOT EDIT MANUALLY
|
|
235
|
+
*
|
|
236
|
+
* @generated ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
237
|
+
*/
|
|
238
|
+
|
|
239
|
+
`;
|
|
240
|
+
}
|
|
241
|
+
function generateImports(mappings, options) {
|
|
242
|
+
let code = "";
|
|
243
|
+
code += `import { client } from '@spfn/core/client';
|
|
244
|
+
`;
|
|
245
|
+
if (options.includeTypes !== false) {
|
|
246
|
+
code += `import type { InferContract } from '@spfn/core';
|
|
247
|
+
`;
|
|
248
|
+
}
|
|
249
|
+
code += `
|
|
250
|
+
`;
|
|
251
|
+
const importGroups = groupContractsByImportPath(mappings);
|
|
252
|
+
const importPaths = Object.keys(importGroups);
|
|
253
|
+
for (let i = 0; i < importPaths.length; i++) {
|
|
254
|
+
const importPath = importPaths[i];
|
|
255
|
+
const contracts = importGroups[importPath];
|
|
256
|
+
code += `import { ${contracts.join(", ")} } from '${importPath}';
|
|
257
|
+
`;
|
|
258
|
+
}
|
|
259
|
+
code += `
|
|
260
|
+
`;
|
|
261
|
+
return code;
|
|
262
|
+
}
|
|
263
|
+
function groupContractsByImportPath(mappings) {
|
|
264
|
+
const groups = {};
|
|
265
|
+
for (let i = 0; i < mappings.length; i++) {
|
|
266
|
+
const mapping = mappings[i];
|
|
267
|
+
const path = mapping.contractImportPath;
|
|
268
|
+
if (!groups[path]) {
|
|
269
|
+
groups[path] = /* @__PURE__ */ new Set();
|
|
270
|
+
}
|
|
271
|
+
groups[path].add(mapping.contractName);
|
|
272
|
+
}
|
|
273
|
+
const result = {};
|
|
274
|
+
const keys = Object.keys(groups);
|
|
275
|
+
for (let i = 0; i < keys.length; i++) {
|
|
276
|
+
const key = keys[i];
|
|
277
|
+
result[key] = Array.from(groups[key]);
|
|
278
|
+
}
|
|
279
|
+
return result;
|
|
280
|
+
}
|
|
281
|
+
function generateApiObject(grouped, options) {
|
|
282
|
+
let code = "";
|
|
283
|
+
code += `/**
|
|
284
|
+
* Type-safe API client
|
|
285
|
+
*/
|
|
286
|
+
export const api = {
|
|
287
|
+
`;
|
|
288
|
+
const resourceNames = Object.keys(grouped);
|
|
289
|
+
for (let i = 0; i < resourceNames.length; i++) {
|
|
290
|
+
const resourceName = resourceNames[i];
|
|
291
|
+
const routes = grouped[resourceName];
|
|
292
|
+
code += ` ${resourceName}: {
|
|
293
|
+
`;
|
|
294
|
+
for (let j = 0; j < routes.length; j++) {
|
|
295
|
+
const route = routes[j];
|
|
296
|
+
code += generateMethodCode(route, options);
|
|
297
|
+
}
|
|
298
|
+
code += ` }`;
|
|
299
|
+
if (i < resourceNames.length - 1) {
|
|
300
|
+
code += `,`;
|
|
301
|
+
}
|
|
302
|
+
code += `
|
|
303
|
+
`;
|
|
304
|
+
}
|
|
305
|
+
code += `} as const;
|
|
306
|
+
|
|
307
|
+
`;
|
|
308
|
+
return code;
|
|
309
|
+
}
|
|
310
|
+
function generateMethodCode(mapping, options) {
|
|
311
|
+
const methodName = generateMethodName(mapping);
|
|
312
|
+
const contractType = `typeof ${mapping.contractName}`;
|
|
313
|
+
const hasParams = mapping.path.includes(":");
|
|
314
|
+
const hasBody = ["POST", "PUT", "PATCH"].indexOf(mapping.method) !== -1;
|
|
315
|
+
let code = "";
|
|
316
|
+
if (options.includeJsDoc !== false) {
|
|
317
|
+
code += ` /**
|
|
318
|
+
`;
|
|
319
|
+
code += ` * ${mapping.method} ${mapping.path}
|
|
320
|
+
`;
|
|
321
|
+
code += ` */
|
|
322
|
+
`;
|
|
323
|
+
}
|
|
324
|
+
code += ` ${methodName}: (`;
|
|
325
|
+
const params = [];
|
|
326
|
+
if (hasParams) {
|
|
327
|
+
params.push(`params: InferContract<${contractType}>['params']`);
|
|
328
|
+
}
|
|
329
|
+
if (hasBody) {
|
|
330
|
+
params.push(`body: InferContract<${contractType}>['body']`);
|
|
331
|
+
}
|
|
332
|
+
if (params.length > 0) {
|
|
333
|
+
code += `options: { ${params.join(", ")} }`;
|
|
334
|
+
}
|
|
335
|
+
code += `) => `;
|
|
336
|
+
code += `client.call('${mapping.path}', ${mapping.contractName}, `;
|
|
337
|
+
if (params.length > 0) {
|
|
338
|
+
code += `options`;
|
|
339
|
+
} else {
|
|
340
|
+
code += `{}`;
|
|
341
|
+
}
|
|
342
|
+
code += `),
|
|
343
|
+
`;
|
|
344
|
+
return code;
|
|
345
|
+
}
|
|
346
|
+
function generateMethodName(mapping) {
|
|
347
|
+
const method = mapping.method.toLowerCase();
|
|
348
|
+
if (mapping.path === "/" || mapping.path.match(/^\/[\w-]+$/)) {
|
|
349
|
+
if (method === "get") {
|
|
350
|
+
return "list";
|
|
351
|
+
}
|
|
352
|
+
if (method === "post") {
|
|
353
|
+
return "create";
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (mapping.path.includes(":")) {
|
|
357
|
+
if (method === "get") {
|
|
358
|
+
return "getById";
|
|
359
|
+
}
|
|
360
|
+
if (method === "put" || method === "patch") {
|
|
361
|
+
return "update";
|
|
362
|
+
}
|
|
363
|
+
if (method === "delete") {
|
|
364
|
+
return "delete";
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return method;
|
|
368
|
+
}
|
|
369
|
+
function generateFooter() {
|
|
370
|
+
return `/**
|
|
371
|
+
* Export client instance for advanced usage
|
|
372
|
+
*
|
|
373
|
+
* Use this to add interceptors or customize the client:
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* \`\`\`ts
|
|
377
|
+
* import { client } from './api';
|
|
378
|
+
* import { createAuthInterceptor } from '@spfn/auth/nextjs';
|
|
379
|
+
* import { NextJSCookieProvider } from '@spfn/auth/nextjs';
|
|
380
|
+
*
|
|
381
|
+
* client.use(createAuthInterceptor({
|
|
382
|
+
* cookieProvider: new NextJSCookieProvider(),
|
|
383
|
+
* encryptionKey: process.env.ENCRYPTION_KEY!
|
|
384
|
+
* }));
|
|
385
|
+
* \`\`\`
|
|
386
|
+
*/
|
|
387
|
+
export { client };
|
|
388
|
+
`;
|
|
389
|
+
}
|
|
390
|
+
function countUniqueContractFiles(mappings) {
|
|
391
|
+
const files = /* @__PURE__ */ new Set();
|
|
392
|
+
for (let i = 0; i < mappings.length; i++) {
|
|
393
|
+
if (mappings[i].contractFile) {
|
|
394
|
+
files.add(mappings[i].contractFile);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return files.size;
|
|
398
|
+
}
|
|
399
|
+
var PinoAdapter = class _PinoAdapter {
|
|
400
|
+
logger;
|
|
401
|
+
constructor(config) {
|
|
402
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
403
|
+
const isDevelopment = process.env.NODE_ENV === "development";
|
|
404
|
+
const fileLoggingEnabled = process.env.LOGGER_FILE_ENABLED === "true";
|
|
405
|
+
const targets = [];
|
|
406
|
+
if (!isProduction && isDevelopment) {
|
|
407
|
+
targets.push({
|
|
408
|
+
target: "pino-pretty",
|
|
409
|
+
level: "debug",
|
|
410
|
+
options: {
|
|
411
|
+
colorize: true,
|
|
412
|
+
translateTime: "SYS:yyyy-mm-dd HH:MM:ss.l",
|
|
413
|
+
ignore: "pid,hostname"
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
if (fileLoggingEnabled && isProduction) {
|
|
418
|
+
const logDir = process.env.LOG_DIR || "./logs";
|
|
419
|
+
const maxFileSize = process.env.LOG_MAX_FILE_SIZE || "10M";
|
|
420
|
+
const maxFiles = parseInt(process.env.LOG_MAX_FILES || "10", 10);
|
|
421
|
+
targets.push({
|
|
422
|
+
target: "pino-roll",
|
|
423
|
+
level: "info",
|
|
424
|
+
options: {
|
|
425
|
+
file: `${logDir}/app.log`,
|
|
426
|
+
frequency: "daily",
|
|
427
|
+
size: maxFileSize,
|
|
428
|
+
limit: { count: maxFiles },
|
|
429
|
+
mkdir: true
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
this.logger = pino({
|
|
434
|
+
level: config.level,
|
|
435
|
+
// Transport 설정 (targets가 있으면 사용, 없으면 기본 stdout)
|
|
436
|
+
transport: targets.length > 0 ? { targets } : void 0,
|
|
437
|
+
// 기본 필드
|
|
438
|
+
base: config.module ? { module: config.module } : void 0
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
child(module) {
|
|
442
|
+
const childLogger = new _PinoAdapter({ level: this.logger.level, module });
|
|
443
|
+
childLogger.logger = this.logger.child({ module });
|
|
444
|
+
return childLogger;
|
|
445
|
+
}
|
|
446
|
+
debug(message, context) {
|
|
447
|
+
this.logger.debug(context || {}, message);
|
|
448
|
+
}
|
|
449
|
+
info(message, context) {
|
|
450
|
+
this.logger.info(context || {}, message);
|
|
451
|
+
}
|
|
452
|
+
warn(message, errorOrContext, context) {
|
|
453
|
+
if (errorOrContext instanceof Error) {
|
|
454
|
+
this.logger.warn({ err: errorOrContext, ...context }, message);
|
|
455
|
+
} else {
|
|
456
|
+
this.logger.warn(errorOrContext || {}, message);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
error(message, errorOrContext, context) {
|
|
460
|
+
if (errorOrContext instanceof Error) {
|
|
461
|
+
this.logger.error({ err: errorOrContext, ...context }, message);
|
|
462
|
+
} else {
|
|
463
|
+
this.logger.error(errorOrContext || {}, message);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
fatal(message, errorOrContext, context) {
|
|
467
|
+
if (errorOrContext instanceof Error) {
|
|
468
|
+
this.logger.fatal({ err: errorOrContext, ...context }, message);
|
|
469
|
+
} else {
|
|
470
|
+
this.logger.fatal(errorOrContext || {}, message);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
async close() {
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
// src/logger/logger.ts
|
|
478
|
+
var Logger = class _Logger {
|
|
479
|
+
config;
|
|
480
|
+
module;
|
|
481
|
+
constructor(config) {
|
|
482
|
+
this.config = config;
|
|
483
|
+
this.module = config.module;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Get current log level
|
|
487
|
+
*/
|
|
488
|
+
get level() {
|
|
489
|
+
return this.config.level;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Create child logger (per module)
|
|
493
|
+
*/
|
|
494
|
+
child(module) {
|
|
495
|
+
return new _Logger({
|
|
496
|
+
...this.config,
|
|
497
|
+
module
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Debug log
|
|
502
|
+
*/
|
|
503
|
+
debug(message, context) {
|
|
504
|
+
this.log("debug", message, void 0, context);
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Info log
|
|
508
|
+
*/
|
|
509
|
+
info(message, context) {
|
|
510
|
+
this.log("info", message, void 0, context);
|
|
511
|
+
}
|
|
512
|
+
warn(message, errorOrContext, context) {
|
|
513
|
+
if (errorOrContext instanceof Error) {
|
|
514
|
+
this.log("warn", message, errorOrContext, context);
|
|
515
|
+
} else {
|
|
516
|
+
this.log("warn", message, void 0, errorOrContext);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
error(message, errorOrContext, context) {
|
|
520
|
+
if (errorOrContext instanceof Error) {
|
|
521
|
+
this.log("error", message, errorOrContext, context);
|
|
522
|
+
} else {
|
|
523
|
+
this.log("error", message, void 0, errorOrContext);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
fatal(message, errorOrContext, context) {
|
|
527
|
+
if (errorOrContext instanceof Error) {
|
|
528
|
+
this.log("fatal", message, errorOrContext, context);
|
|
529
|
+
} else {
|
|
530
|
+
this.log("fatal", message, void 0, errorOrContext);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Log processing (internal)
|
|
535
|
+
*/
|
|
536
|
+
log(level, message, error, context) {
|
|
537
|
+
const metadata = {
|
|
538
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
539
|
+
level,
|
|
540
|
+
message,
|
|
541
|
+
module: this.module,
|
|
542
|
+
error,
|
|
543
|
+
context
|
|
544
|
+
};
|
|
545
|
+
this.processTransports(metadata);
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Process Transports
|
|
549
|
+
*/
|
|
550
|
+
processTransports(metadata) {
|
|
551
|
+
const promises = this.config.transports.filter((transport) => transport.enabled).map((transport) => this.safeTransportLog(transport, metadata));
|
|
552
|
+
Promise.all(promises).catch((error) => {
|
|
553
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
554
|
+
process.stderr.write(`[Logger] Transport error: ${errorMessage}
|
|
555
|
+
`);
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Transport log (error-safe)
|
|
560
|
+
*/
|
|
561
|
+
async safeTransportLog(transport, metadata) {
|
|
562
|
+
try {
|
|
563
|
+
await transport.log(metadata);
|
|
564
|
+
} catch (error) {
|
|
565
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
566
|
+
process.stderr.write(`[Logger] Transport "${transport.name}" failed: ${errorMessage}
|
|
567
|
+
`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Close all Transports
|
|
572
|
+
*/
|
|
573
|
+
async close() {
|
|
574
|
+
const closePromises = this.config.transports.filter((transport) => transport.close).map((transport) => transport.close());
|
|
575
|
+
await Promise.all(closePromises);
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
// src/logger/types.ts
|
|
580
|
+
var LOG_LEVEL_PRIORITY = {
|
|
581
|
+
debug: 0,
|
|
582
|
+
info: 1,
|
|
583
|
+
warn: 2,
|
|
584
|
+
error: 3,
|
|
585
|
+
fatal: 4
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
// src/logger/formatters.ts
|
|
589
|
+
var COLORS = {
|
|
590
|
+
reset: "\x1B[0m",
|
|
591
|
+
bright: "\x1B[1m",
|
|
592
|
+
dim: "\x1B[2m",
|
|
593
|
+
// 로그 레벨 컬러
|
|
594
|
+
debug: "\x1B[36m",
|
|
595
|
+
// cyan
|
|
596
|
+
info: "\x1B[32m",
|
|
597
|
+
// green
|
|
598
|
+
warn: "\x1B[33m",
|
|
599
|
+
// yellow
|
|
600
|
+
error: "\x1B[31m",
|
|
601
|
+
// red
|
|
602
|
+
fatal: "\x1B[35m",
|
|
603
|
+
// magenta
|
|
604
|
+
// 추가 컬러
|
|
605
|
+
gray: "\x1B[90m"
|
|
606
|
+
};
|
|
607
|
+
function colorizeLevel(level) {
|
|
608
|
+
const color = COLORS[level];
|
|
609
|
+
const levelStr = level.toUpperCase().padEnd(5);
|
|
610
|
+
return `${color}${levelStr}${COLORS.reset}`;
|
|
611
|
+
}
|
|
612
|
+
function formatTimestamp(date) {
|
|
613
|
+
return date.toISOString();
|
|
614
|
+
}
|
|
615
|
+
function formatTimestampHuman(date) {
|
|
616
|
+
const year = date.getFullYear();
|
|
617
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
618
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
619
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
620
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
621
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
622
|
+
const ms = String(date.getMilliseconds()).padStart(3, "0");
|
|
623
|
+
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${ms}`;
|
|
624
|
+
}
|
|
625
|
+
function formatError(error) {
|
|
626
|
+
const lines = [];
|
|
627
|
+
lines.push(`${error.name}: ${error.message}`);
|
|
628
|
+
if (error.stack) {
|
|
629
|
+
const stackLines = error.stack.split("\n").slice(1);
|
|
630
|
+
lines.push(...stackLines);
|
|
631
|
+
}
|
|
632
|
+
return lines.join("\n");
|
|
633
|
+
}
|
|
634
|
+
function formatContext(context) {
|
|
635
|
+
try {
|
|
636
|
+
return JSON.stringify(context, null, 2);
|
|
637
|
+
} catch (error) {
|
|
638
|
+
return "[Context serialization failed]";
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
function formatConsole(metadata, colorize = true) {
|
|
642
|
+
const parts = [];
|
|
643
|
+
const timestamp = formatTimestampHuman(metadata.timestamp);
|
|
644
|
+
if (colorize) {
|
|
645
|
+
parts.push(`${COLORS.gray}${timestamp}${COLORS.reset}`);
|
|
646
|
+
} else {
|
|
647
|
+
parts.push(timestamp);
|
|
648
|
+
}
|
|
649
|
+
if (colorize) {
|
|
650
|
+
parts.push(colorizeLevel(metadata.level));
|
|
651
|
+
} else {
|
|
652
|
+
parts.push(metadata.level.toUpperCase().padEnd(5));
|
|
653
|
+
}
|
|
654
|
+
if (metadata.module) {
|
|
655
|
+
if (colorize) {
|
|
656
|
+
parts.push(`${COLORS.dim}[${metadata.module}]${COLORS.reset}`);
|
|
657
|
+
} else {
|
|
658
|
+
parts.push(`[${metadata.module}]`);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
parts.push(metadata.message);
|
|
662
|
+
let output = parts.join(" ");
|
|
663
|
+
if (metadata.context && Object.keys(metadata.context).length > 0) {
|
|
664
|
+
output += "\n" + formatContext(metadata.context);
|
|
665
|
+
}
|
|
666
|
+
if (metadata.error) {
|
|
667
|
+
output += "\n" + formatError(metadata.error);
|
|
668
|
+
}
|
|
669
|
+
return output;
|
|
670
|
+
}
|
|
671
|
+
function formatJSON(metadata) {
|
|
672
|
+
const obj = {
|
|
673
|
+
timestamp: formatTimestamp(metadata.timestamp),
|
|
674
|
+
level: metadata.level,
|
|
675
|
+
message: metadata.message
|
|
676
|
+
};
|
|
677
|
+
if (metadata.module) {
|
|
678
|
+
obj.module = metadata.module;
|
|
679
|
+
}
|
|
680
|
+
if (metadata.context) {
|
|
681
|
+
obj.context = metadata.context;
|
|
682
|
+
}
|
|
683
|
+
if (metadata.error) {
|
|
684
|
+
obj.error = {
|
|
685
|
+
name: metadata.error.name,
|
|
686
|
+
message: metadata.error.message,
|
|
687
|
+
stack: metadata.error.stack
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
return JSON.stringify(obj);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// src/logger/transports/console.ts
|
|
694
|
+
var ConsoleTransport = class {
|
|
695
|
+
name = "console";
|
|
696
|
+
level;
|
|
697
|
+
enabled;
|
|
698
|
+
colorize;
|
|
699
|
+
constructor(config) {
|
|
700
|
+
this.level = config.level;
|
|
701
|
+
this.enabled = config.enabled;
|
|
702
|
+
this.colorize = config.colorize ?? true;
|
|
703
|
+
}
|
|
704
|
+
async log(metadata) {
|
|
705
|
+
if (!this.enabled) {
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
if (LOG_LEVEL_PRIORITY[metadata.level] < LOG_LEVEL_PRIORITY[this.level]) {
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
const message = formatConsole(metadata, this.colorize);
|
|
712
|
+
if (metadata.level === "warn" || metadata.level === "error" || metadata.level === "fatal") {
|
|
713
|
+
console.error(message);
|
|
714
|
+
} else {
|
|
715
|
+
console.log(message);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
var FileTransport = class {
|
|
720
|
+
name = "file";
|
|
721
|
+
level;
|
|
722
|
+
enabled;
|
|
723
|
+
logDir;
|
|
724
|
+
currentStream = null;
|
|
725
|
+
currentFilename = null;
|
|
726
|
+
constructor(config) {
|
|
727
|
+
this.level = config.level;
|
|
728
|
+
this.enabled = config.enabled;
|
|
729
|
+
this.logDir = config.logDir;
|
|
730
|
+
if (!existsSync(this.logDir)) {
|
|
731
|
+
mkdirSync(this.logDir, { recursive: true });
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
async log(metadata) {
|
|
735
|
+
if (!this.enabled) {
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
if (LOG_LEVEL_PRIORITY[metadata.level] < LOG_LEVEL_PRIORITY[this.level]) {
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
const message = formatJSON(metadata);
|
|
742
|
+
const filename = this.getLogFilename(metadata.timestamp);
|
|
743
|
+
if (this.currentFilename !== filename) {
|
|
744
|
+
await this.rotateStream(filename);
|
|
745
|
+
}
|
|
746
|
+
if (this.currentStream) {
|
|
747
|
+
return new Promise((resolve, reject) => {
|
|
748
|
+
this.currentStream.write(message + "\n", "utf-8", (error) => {
|
|
749
|
+
if (error) {
|
|
750
|
+
process.stderr.write(`[FileTransport] Failed to write log: ${error.message}
|
|
751
|
+
`);
|
|
752
|
+
reject(error);
|
|
753
|
+
} else {
|
|
754
|
+
resolve();
|
|
755
|
+
}
|
|
756
|
+
});
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* 스트림 교체 (날짜 변경 시)
|
|
762
|
+
*/
|
|
763
|
+
async rotateStream(filename) {
|
|
764
|
+
if (this.currentStream) {
|
|
765
|
+
await this.closeStream();
|
|
766
|
+
}
|
|
767
|
+
const filepath = join(this.logDir, filename);
|
|
768
|
+
this.currentStream = createWriteStream(filepath, {
|
|
769
|
+
flags: "a",
|
|
770
|
+
// append mode
|
|
771
|
+
encoding: "utf-8"
|
|
772
|
+
});
|
|
773
|
+
this.currentFilename = filename;
|
|
774
|
+
this.currentStream.on("error", (error) => {
|
|
775
|
+
process.stderr.write(`[FileTransport] Stream error: ${error.message}
|
|
776
|
+
`);
|
|
777
|
+
this.currentStream = null;
|
|
778
|
+
this.currentFilename = null;
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* 현재 스트림 닫기
|
|
783
|
+
*/
|
|
784
|
+
async closeStream() {
|
|
785
|
+
if (!this.currentStream) {
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
return new Promise((resolve, reject) => {
|
|
789
|
+
this.currentStream.end((error) => {
|
|
790
|
+
if (error) {
|
|
791
|
+
reject(error);
|
|
792
|
+
} else {
|
|
793
|
+
this.currentStream = null;
|
|
794
|
+
this.currentFilename = null;
|
|
795
|
+
resolve();
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* 날짜별 로그 파일명 생성
|
|
802
|
+
*/
|
|
803
|
+
getLogFilename(date) {
|
|
804
|
+
const year = date.getFullYear();
|
|
805
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
806
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
807
|
+
return `${year}-${month}-${day}.log`;
|
|
808
|
+
}
|
|
809
|
+
async close() {
|
|
810
|
+
await this.closeStream();
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
// src/logger/config.ts
|
|
815
|
+
function getDefaultLogLevel() {
|
|
816
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
817
|
+
const isDevelopment = process.env.NODE_ENV === "development";
|
|
818
|
+
if (isDevelopment) {
|
|
819
|
+
return "debug";
|
|
820
|
+
}
|
|
821
|
+
if (isProduction) {
|
|
822
|
+
return "info";
|
|
823
|
+
}
|
|
824
|
+
return "warn";
|
|
825
|
+
}
|
|
826
|
+
function getConsoleConfig() {
|
|
827
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
828
|
+
return {
|
|
829
|
+
level: "debug",
|
|
830
|
+
enabled: true,
|
|
831
|
+
colorize: !isProduction
|
|
832
|
+
// Dev: colored output, Production: plain text
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
function getFileConfig() {
|
|
836
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
837
|
+
return {
|
|
838
|
+
level: "info",
|
|
839
|
+
enabled: isProduction,
|
|
840
|
+
// File logging in production only
|
|
841
|
+
logDir: process.env.LOG_DIR || "./logs",
|
|
842
|
+
maxFileSize: 10 * 1024 * 1024,
|
|
843
|
+
// 10MB
|
|
844
|
+
maxFiles: 10
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// src/logger/adapters/custom.ts
|
|
849
|
+
function initializeTransports() {
|
|
850
|
+
const transports = [];
|
|
851
|
+
const consoleConfig = getConsoleConfig();
|
|
852
|
+
transports.push(new ConsoleTransport(consoleConfig));
|
|
853
|
+
const fileConfig = getFileConfig();
|
|
854
|
+
if (fileConfig.enabled) {
|
|
855
|
+
transports.push(new FileTransport(fileConfig));
|
|
856
|
+
}
|
|
857
|
+
return transports;
|
|
858
|
+
}
|
|
859
|
+
var CustomAdapter = class _CustomAdapter {
|
|
860
|
+
logger;
|
|
861
|
+
constructor(config) {
|
|
862
|
+
this.logger = new Logger({
|
|
863
|
+
level: config.level,
|
|
864
|
+
module: config.module,
|
|
865
|
+
transports: initializeTransports()
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
child(module) {
|
|
869
|
+
const adapter = new _CustomAdapter({ level: this.logger.level, module });
|
|
870
|
+
adapter.logger = this.logger.child(module);
|
|
871
|
+
return adapter;
|
|
872
|
+
}
|
|
873
|
+
debug(message, context) {
|
|
874
|
+
this.logger.debug(message, context);
|
|
875
|
+
}
|
|
876
|
+
info(message, context) {
|
|
877
|
+
this.logger.info(message, context);
|
|
878
|
+
}
|
|
879
|
+
warn(message, errorOrContext, context) {
|
|
880
|
+
if (errorOrContext instanceof Error) {
|
|
881
|
+
this.logger.warn(message, errorOrContext, context);
|
|
882
|
+
} else {
|
|
883
|
+
this.logger.warn(message, errorOrContext);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
error(message, errorOrContext, context) {
|
|
887
|
+
if (errorOrContext instanceof Error) {
|
|
888
|
+
this.logger.error(message, errorOrContext, context);
|
|
889
|
+
} else {
|
|
890
|
+
this.logger.error(message, errorOrContext);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
fatal(message, errorOrContext, context) {
|
|
894
|
+
if (errorOrContext instanceof Error) {
|
|
895
|
+
this.logger.fatal(message, errorOrContext, context);
|
|
896
|
+
} else {
|
|
897
|
+
this.logger.fatal(message, errorOrContext);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
async close() {
|
|
901
|
+
await this.logger.close();
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
|
|
905
|
+
// src/logger/adapter-factory.ts
|
|
906
|
+
function createAdapter(type) {
|
|
907
|
+
const level = getDefaultLogLevel();
|
|
908
|
+
switch (type) {
|
|
909
|
+
case "pino":
|
|
910
|
+
return new PinoAdapter({ level });
|
|
911
|
+
case "custom":
|
|
912
|
+
return new CustomAdapter({ level });
|
|
913
|
+
default:
|
|
914
|
+
return new PinoAdapter({ level });
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
function getAdapterType() {
|
|
918
|
+
const adapterEnv = process.env.LOGGER_ADAPTER;
|
|
919
|
+
if (adapterEnv === "custom" || adapterEnv === "pino") {
|
|
920
|
+
return adapterEnv;
|
|
921
|
+
}
|
|
922
|
+
return "pino";
|
|
923
|
+
}
|
|
924
|
+
var logger = createAdapter(getAdapterType());
|
|
925
|
+
|
|
926
|
+
// src/codegen/watch-generate.ts
|
|
927
|
+
var codegenLogger = logger.child("codegen");
|
|
928
|
+
async function watchAndGenerate(options = {}) {
|
|
929
|
+
const cwd = process.cwd();
|
|
930
|
+
const routesDir = options.routesDir ?? join(cwd, "src", "server", "routes");
|
|
931
|
+
const outputPath = options.outputPath ?? join(cwd, "src", "lib", "api", "client.ts");
|
|
932
|
+
const debug = options.debug ?? false;
|
|
933
|
+
if (debug) {
|
|
934
|
+
codegenLogger.info("Contract Watcher Started", { routesDir, outputPath });
|
|
935
|
+
}
|
|
936
|
+
try {
|
|
937
|
+
const contracts = await scanContracts(routesDir);
|
|
938
|
+
if (contracts.length === 0) {
|
|
939
|
+
if (debug) {
|
|
940
|
+
codegenLogger.warn("No contracts found");
|
|
941
|
+
}
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
const stats = await generateClient(contracts, {
|
|
945
|
+
outputPath,
|
|
946
|
+
includeTypes: true,
|
|
947
|
+
includeJsDoc: true
|
|
948
|
+
});
|
|
949
|
+
if (debug) {
|
|
950
|
+
codegenLogger.info("Client generated", {
|
|
951
|
+
endpoints: stats.methodsGenerated,
|
|
952
|
+
resources: stats.resourcesGenerated,
|
|
953
|
+
duration: stats.duration
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
} catch (error) {
|
|
957
|
+
codegenLogger.error(
|
|
958
|
+
"Generation failed",
|
|
959
|
+
error instanceof Error ? error : new Error(String(error))
|
|
960
|
+
);
|
|
961
|
+
throw error;
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
965
|
+
watchAndGenerate({ debug: true });
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
export { generateClient, groupByResource, scanContracts, watchAndGenerate };
|
|
969
|
+
//# sourceMappingURL=index.js.map
|
|
970
|
+
//# sourceMappingURL=index.js.map
|