@thinkdata/cli 0.1.0 → 0.2.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.
Files changed (44) hide show
  1. package/dist/bin/thinkdata.js +24 -4
  2. package/dist/bin/thinkdata.js.map +1 -1
  3. package/dist/commands/auth.d.ts +9 -0
  4. package/dist/commands/auth.d.ts.map +1 -0
  5. package/dist/commands/auth.js +61 -0
  6. package/dist/commands/auth.js.map +1 -0
  7. package/dist/commands/compare.d.ts +1 -0
  8. package/dist/commands/compare.d.ts.map +1 -1
  9. package/dist/commands/compare.js +2 -2
  10. package/dist/commands/compare.js.map +1 -1
  11. package/dist/commands/extract.d.ts +1 -0
  12. package/dist/commands/extract.d.ts.map +1 -1
  13. package/dist/commands/extract.js +40 -10
  14. package/dist/commands/extract.js.map +1 -1
  15. package/dist/commands/serve.d.ts +9 -0
  16. package/dist/commands/serve.d.ts.map +1 -0
  17. package/dist/commands/serve.js +348 -0
  18. package/dist/commands/serve.js.map +1 -0
  19. package/dist/extractors/index.d.ts.map +1 -1
  20. package/dist/extractors/index.js +7 -2
  21. package/dist/extractors/index.js.map +1 -1
  22. package/dist/extractors/mssql.d.ts +14 -0
  23. package/dist/extractors/mssql.d.ts.map +1 -0
  24. package/dist/extractors/mssql.js +234 -0
  25. package/dist/extractors/mssql.js.map +1 -0
  26. package/dist/extractors/mysql.d.ts +4 -2
  27. package/dist/extractors/mysql.d.ts.map +1 -1
  28. package/dist/extractors/mysql.js +30 -8
  29. package/dist/extractors/mysql.js.map +1 -1
  30. package/dist/extractors/oracle.d.ts +13 -0
  31. package/dist/extractors/oracle.d.ts.map +1 -0
  32. package/dist/extractors/oracle.js +189 -0
  33. package/dist/extractors/oracle.js.map +1 -0
  34. package/dist/extractors/postgres.d.ts +6 -3
  35. package/dist/extractors/postgres.d.ts.map +1 -1
  36. package/dist/extractors/postgres.js +63 -13
  37. package/dist/extractors/postgres.js.map +1 -1
  38. package/dist/index.d.ts +1 -0
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +1 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/types.d.ts +16 -1
  43. package/dist/types.d.ts.map +1 -1
  44. package/package.json +19 -6
@@ -0,0 +1,348 @@
1
+ /**
2
+ * serve 명령 — CLI Agent HTTP 서버
3
+ *
4
+ * 브라우저(ThinkERD)에서 직접 접근하는 로컬 HTTP 서버.
5
+ * 데이터가 외부 서버를 경유하지 않고 localhost에서만 처리됩니다.
6
+ *
7
+ * 사용법:
8
+ * npx @thinkdata/cli serve
9
+ * npx @thinkdata/cli serve --port 3100
10
+ */
11
+ import { createServer } from 'node:http';
12
+ import { createExtractor } from '../extractors/index.js';
13
+ import { convertToERD } from '../converter/schemaToERD.js';
14
+ import { compareSchemas } from './compare.js';
15
+ const DEFAULT_PORT = 3100;
16
+ /**
17
+ * JSON 바디 파서
18
+ */
19
+ async function parseBody(req) {
20
+ return new Promise((resolve, reject) => {
21
+ let body = '';
22
+ req.on('data', chunk => { body += chunk; });
23
+ req.on('end', () => {
24
+ try {
25
+ resolve(body ? JSON.parse(body) : {});
26
+ }
27
+ catch (e) {
28
+ reject(new Error('Invalid JSON body'));
29
+ }
30
+ });
31
+ req.on('error', reject);
32
+ });
33
+ }
34
+ /**
35
+ * CORS 헤더 설정
36
+ */
37
+ function setCorsHeaders(res) {
38
+ res.setHeader('Access-Control-Allow-Origin', '*');
39
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
40
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
41
+ res.setHeader('Access-Control-Max-Age', '86400');
42
+ // Private Network Access(PNA): 공개 출처(https 배포 앱) → localhost 요청 시
43
+ // Chrome이 프리플라이트에서 이 헤더를 요구한다. 없으면 연결이 차단된다.
44
+ res.setHeader('Access-Control-Allow-Private-Network', 'true');
45
+ }
46
+ /**
47
+ * JSON 응답 헬퍼
48
+ */
49
+ function jsonResponse(res, status, data) {
50
+ res.writeHead(status, { 'Content-Type': 'application/json' });
51
+ res.end(JSON.stringify(data));
52
+ }
53
+ /**
54
+ * ConnectionConfig 변환 (요청 바디 → CLI 타입)
55
+ */
56
+ function toConnectionConfig(body) {
57
+ const engineMap = {
58
+ postgresql: 'postgres',
59
+ postgres: 'postgres',
60
+ mysql: 'mysql',
61
+ mariadb: 'mysql',
62
+ };
63
+ return {
64
+ type: engineMap[body.engine] || body.engine || 'postgres',
65
+ host: body.host || 'localhost',
66
+ port: body.port || 5432,
67
+ database: body.database,
68
+ user: body.username || body.user,
69
+ password: body.password || '',
70
+ schema: body.schema,
71
+ ssl: body.ssl || false,
72
+ };
73
+ }
74
+ /**
75
+ * 라우트 핸들러
76
+ */
77
+ const routes = {
78
+ // ── Health Check ──
79
+ 'GET /health': async (_req, res) => {
80
+ jsonResponse(res, 200, {
81
+ status: 'ok',
82
+ agent: '@thinkdata/cli',
83
+ version: '0.2.1',
84
+ timestamp: new Date().toISOString(),
85
+ });
86
+ },
87
+ // ── 연결 테스트 ──
88
+ 'POST /test-connection': async (req, res) => {
89
+ const body = await parseBody(req);
90
+ if (!body.database || !body.username) {
91
+ jsonResponse(res, 400, { error: 'database, username은 필수입니다.' });
92
+ return;
93
+ }
94
+ const config = toConnectionConfig(body);
95
+ try {
96
+ const extractor = createExtractor(config.type);
97
+ const version = await extractor.testConnection(config);
98
+ // 비밀번호 즉시 폐기
99
+ config.password = '';
100
+ jsonResponse(res, 200, {
101
+ success: true,
102
+ engine: config.type,
103
+ version,
104
+ host: config.host,
105
+ database: config.database,
106
+ });
107
+ }
108
+ catch (err) {
109
+ config.password = '';
110
+ jsonResponse(res, 400, {
111
+ success: false,
112
+ error: err.message || 'Connection failed',
113
+ });
114
+ }
115
+ },
116
+ // ── 스키마 목록 조회 ──
117
+ 'POST /list-schemas': async (req, res) => {
118
+ const body = await parseBody(req);
119
+ if (!body.database || !body.username) {
120
+ jsonResponse(res, 400, { error: 'database, username은 필수입니다.' });
121
+ return;
122
+ }
123
+ const config = toConnectionConfig(body);
124
+ try {
125
+ const extractor = createExtractor(config.type);
126
+ const schemas = extractor.listSchemas ? await extractor.listSchemas(config) : [];
127
+ config.password = '';
128
+ jsonResponse(res, 200, { success: true, schemas });
129
+ }
130
+ catch (err) {
131
+ config.password = '';
132
+ jsonResponse(res, 400, { success: false, error: err.message || 'Failed to list schemas' });
133
+ }
134
+ },
135
+ // ── 스키마 추출 ──
136
+ 'POST /extract-schema': async (req, res) => {
137
+ const body = await parseBody(req);
138
+ if (!body.database || !body.username) {
139
+ jsonResponse(res, 400, { error: 'database, username은 필수입니다.' });
140
+ return;
141
+ }
142
+ const config = toConnectionConfig(body);
143
+ try {
144
+ const extractor = createExtractor(config.type);
145
+ const schema = await extractor.extract(config);
146
+ // 비밀번호 즉시 폐기
147
+ config.password = '';
148
+ // ERD 포맷으로 변환 (옵션)
149
+ if (body.format === 'erd') {
150
+ const erd = convertToERD(schema);
151
+ jsonResponse(res, 200, {
152
+ success: true,
153
+ format: 'erd',
154
+ data: erd,
155
+ });
156
+ }
157
+ else {
158
+ jsonResponse(res, 200, {
159
+ success: true,
160
+ format: 'raw',
161
+ data: schema,
162
+ });
163
+ }
164
+ }
165
+ catch (err) {
166
+ config.password = '';
167
+ jsonResponse(res, 500, {
168
+ success: false,
169
+ error: err.message || 'Schema extraction failed',
170
+ });
171
+ }
172
+ },
173
+ // ── 스키마 추출 (Streaming SSE) ──
174
+ 'POST /extract-stream': async (req, res) => {
175
+ const body = await parseBody(req);
176
+ if (!body.database || !body.username) {
177
+ jsonResponse(res, 400, { error: 'database, username은 필수입니다.' });
178
+ return;
179
+ }
180
+ const config = toConnectionConfig(body);
181
+ // SSE 헤더 설정
182
+ res.writeHead(200, {
183
+ 'Content-Type': 'text/event-stream',
184
+ 'Cache-Control': 'no-cache',
185
+ 'Connection': 'keep-alive',
186
+ });
187
+ const sendEvent = (event, data) => {
188
+ res.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
189
+ };
190
+ try {
191
+ const extractor = createExtractor(config.type);
192
+ const schema = await extractor.extract(config, (msg) => {
193
+ sendEvent('progress', { message: msg });
194
+ });
195
+ config.password = '';
196
+ let responseData = schema;
197
+ if (body.format === 'erd') {
198
+ responseData = convertToERD(schema);
199
+ }
200
+ sendEvent('complete', {
201
+ success: true,
202
+ format: body.format === 'erd' ? 'erd' : 'raw',
203
+ data: responseData,
204
+ });
205
+ }
206
+ catch (err) {
207
+ config.password = '';
208
+ sendEvent('error', {
209
+ success: false,
210
+ error: err.message || 'Schema extraction failed',
211
+ });
212
+ }
213
+ finally {
214
+ res.end();
215
+ }
216
+ },
217
+ // ── 스키마 비교 (드리프트 감지) ──
218
+ 'POST /compare-schema': async (req, res) => {
219
+ const body = await parseBody(req);
220
+ if (!body.database || !body.username) {
221
+ jsonResponse(res, 400, { error: 'database, username은 필수입니다.' });
222
+ return;
223
+ }
224
+ if (!body.model) {
225
+ jsonResponse(res, 400, { error: 'model (ERD 스키마)은 필수입니다.' });
226
+ return;
227
+ }
228
+ const config = toConnectionConfig(body);
229
+ try {
230
+ const extractor = createExtractor(config.type);
231
+ const dbSchema = await extractor.extract(config);
232
+ // 비밀번호 즉시 폐기
233
+ config.password = '';
234
+ // 모델 스키마와 비교
235
+ const modelSchema = body.model;
236
+ const diff = compareSchemas(dbSchema, modelSchema);
237
+ jsonResponse(res, 200, {
238
+ success: true,
239
+ diff,
240
+ summary: {
241
+ added: diff.added.length,
242
+ removed: diff.removed.length,
243
+ modified: diff.modified.length,
244
+ fkDiffs: diff.fkDiffs.length,
245
+ hasChanges: diff.added.length > 0 || diff.removed.length > 0 ||
246
+ diff.modified.length > 0 || diff.fkDiffs.length > 0,
247
+ },
248
+ });
249
+ }
250
+ catch (err) {
251
+ config.password = '';
252
+ jsonResponse(res, 500, {
253
+ success: false,
254
+ error: err.message || 'Schema comparison failed',
255
+ });
256
+ }
257
+ },
258
+ // ── DataOps 통계 지표 수집 ──
259
+ 'POST /dataops-stats': async (req, res) => {
260
+ const body = await parseBody(req);
261
+ if (!body.database || !body.username) {
262
+ jsonResponse(res, 400, { error: 'database, username은 필수입니다.' });
263
+ return;
264
+ }
265
+ const config = toConnectionConfig(body);
266
+ try {
267
+ const extractor = createExtractor(config.type);
268
+ if (!extractor.getStats) {
269
+ jsonResponse(res, 400, {
270
+ success: false,
271
+ error: `DataOps 통계 수집은 현재 ${config.type} 엔진을 지원하지 않습니다.`,
272
+ });
273
+ return;
274
+ }
275
+ const stats = await extractor.getStats(config);
276
+ // 비밀번호 즉시 폐기
277
+ config.password = '';
278
+ jsonResponse(res, 200, {
279
+ success: true,
280
+ data: stats,
281
+ });
282
+ }
283
+ catch (err) {
284
+ config.password = '';
285
+ jsonResponse(res, 500, {
286
+ success: false,
287
+ error: err.message || 'Failed to fetch DataOps stats',
288
+ });
289
+ }
290
+ },
291
+ };
292
+ /**
293
+ * HTTP 서버 시작
294
+ */
295
+ export async function serveCommand(options) {
296
+ const port = options.port || DEFAULT_PORT;
297
+ const server = createServer(async (req, res) => {
298
+ setCorsHeaders(res);
299
+ // CORS Preflight
300
+ if (req.method === 'OPTIONS') {
301
+ res.writeHead(204);
302
+ res.end();
303
+ return;
304
+ }
305
+ const routeKey = `${req.method} ${req.url?.split('?')[0]}`;
306
+ const handler = routes[routeKey];
307
+ if (handler) {
308
+ try {
309
+ await handler(req, res);
310
+ }
311
+ catch (err) {
312
+ console.error(`[Agent] Error handling ${routeKey}:`, err);
313
+ jsonResponse(res, 500, { error: 'Internal server error' });
314
+ }
315
+ }
316
+ else {
317
+ jsonResponse(res, 404, { error: `Not found: ${routeKey}` });
318
+ }
319
+ });
320
+ server.listen(port, '127.0.0.1', () => {
321
+ console.log();
322
+ console.log(' ┌─────────────────────────────────────────────┐');
323
+ console.log(' │ │');
324
+ console.log(` │ 🚀 ThinkERD Agent running on port ${port} │`);
325
+ console.log(' │ │');
326
+ console.log(` │ Health: http://localhost:${port}/health │`);
327
+ console.log(' │ Bound: 127.0.0.1 (local only) │');
328
+ console.log(' │ │');
329
+ console.log(' │ ✅ Data never leaves your machine │');
330
+ console.log(' │ ✅ Passwords stay in process memory only │');
331
+ console.log(' │ │');
332
+ console.log(' │ Press Ctrl+C to stop │');
333
+ console.log(' │ │');
334
+ console.log(' └─────────────────────────────────────────────┘');
335
+ console.log();
336
+ });
337
+ // Graceful shutdown
338
+ process.on('SIGINT', () => {
339
+ console.log('\n 👋 Agent stopped.');
340
+ server.close();
341
+ process.exit(0);
342
+ });
343
+ process.on('SIGTERM', () => {
344
+ server.close();
345
+ process.exit(0);
346
+ });
347
+ }
348
+ //# sourceMappingURL=serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extractors/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAI5C,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAWrE;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAY7D;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAwBvE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extractors/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAM5C,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAWrE;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAe7D;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAwBvE"}
@@ -5,6 +5,8 @@ export { PostgresExtractor } from './postgres.js';
5
5
  export { MySQLExtractor } from './mysql.js';
6
6
  import { PostgresExtractor } from './postgres.js';
7
7
  import { MySQLExtractor } from './mysql.js';
8
+ import { OracleExtractor } from './oracle.js';
9
+ import { MSSQLExtractor } from './mssql.js';
8
10
  /** DBMS별 기본 포트 */
9
11
  const DEFAULT_PORTS = {
10
12
  postgres: 5432,
@@ -24,9 +26,12 @@ export function createExtractor(type) {
24
26
  case 'mysql':
25
27
  case 'mariadb':
26
28
  return new MySQLExtractor();
27
- // TODO: oracle, mssql, sqlite
29
+ case 'oracle':
30
+ return new OracleExtractor();
31
+ case 'mssql':
32
+ return new MSSQLExtractor();
28
33
  default:
29
- throw new Error(`지원하지 않는 DBMS: ${type}. 현재 지원: postgres, mysql`);
34
+ throw new Error(`지원하지 않는 DBMS 입니다: ${type}. 현재 지원: postgres, mysql, oracle, mssql`);
30
35
  }
31
36
  }
32
37
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/extractors/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5C,kBAAkB;AAClB,MAAM,aAAa,GAA2B;IAC1C,QAAQ,EAAE,IAAI;IACd,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,IAAI;IACZ,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,CAAC;CACZ,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IACxC,QAAQ,IAAI,EAAE,CAAC;QACX,KAAK,UAAU,CAAC;QAChB,KAAK,YAAY;YACb,OAAO,IAAI,iBAAiB,EAAE,CAAC;QACnC,KAAK,OAAO,CAAC;QACb,KAAK,SAAS;YACV,OAAO,IAAI,cAAc,EAAE,CAAC;QAChC,8BAA8B;QAC9B;YACI,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,0BAA0B,CAAC,CAAC;IACzE,CAAC;AACL,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAe;IACjD,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACD,kCAAkC;QAClC,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;QACtE,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACL,MAAM,IAAI,KAAK,CAAC,eAAe,OAAO,0CAA0C,CAAC,CAAC;IACtF,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;IAE7E,OAAO;QACH,IAAI,EAAE,IAAgC;QACtC,IAAI,EAAE,GAAG,CAAC,QAAQ;QAClB,IAAI;QACJ,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QACzC,IAAI,EAAE,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC;QACtC,QAAQ,EAAE,kBAAkB,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC;QAChD,MAAM,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,SAAS;QACnD,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,MAAM,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,SAAS;KAC/F,CAAC;AACN,CAAC;AAED,SAAS,eAAe,CAAC,QAAgB;IACrC,QAAQ,QAAQ,EAAE,CAAC;QACf,KAAK,UAAU,CAAC;QAChB,KAAK,YAAY;YACb,OAAO,UAAU,CAAC;QACtB,KAAK,OAAO,CAAC;QACb,KAAK,SAAS;YACV,OAAO,OAAO,CAAC;QACnB,KAAK,QAAQ;YACT,OAAO,QAAQ,CAAC;QACpB,KAAK,OAAO,CAAC;QACb,KAAK,WAAW;YACZ,OAAO,OAAO,CAAC;QACnB;YACI,OAAO,QAAQ,CAAC;IACxB,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/extractors/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { SchemaExtractor, ConnectionConfig, ExtractedSchema, ExtractProgressCallback } from '../types.js';
2
+ export declare class MSSQLExtractor implements SchemaExtractor {
3
+ testConnection(config: ConnectionConfig): Promise<string>;
4
+ extract(config: ConnectionConfig, onProgress?: ExtractProgressCallback): Promise<ExtractedSchema>;
5
+ private createConnection;
6
+ private getVersion;
7
+ private getTables;
8
+ private getPrimaryKeys;
9
+ private getForeignKeys;
10
+ private getIndexes;
11
+ private buildPKMap;
12
+ private buildIndexMap;
13
+ }
14
+ //# sourceMappingURL=mssql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mssql.d.ts","sourceRoot":"","sources":["../../src/extractors/mssql.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACR,eAAe,EACf,gBAAgB,EAChB,eAAe,EAKf,uBAAuB,EAC1B,MAAM,aAAa,CAAC;AAErB,qBAAa,cAAe,YAAW,eAAe;IAC5C,cAAc,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IAUzD,OAAO,CAAC,MAAM,EAAE,gBAAgB,EAAE,UAAU,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,eAAe,CAAC;YA8DzF,gBAAgB;YAehB,UAAU;YAKV,SAAS;YAiDT,cAAc;YAYd,cAAc;YA6Cd,UAAU;IAoBxB,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,aAAa;CAmBxB"}
@@ -0,0 +1,234 @@
1
+ /**
2
+ * SQL Server (MSSQL) 스키마 추출기
3
+ */
4
+ import sql from 'mssql';
5
+ export class MSSQLExtractor {
6
+ async testConnection(config) {
7
+ const pool = await this.createConnection(config);
8
+ try {
9
+ const result = await pool.request().query('SELECT @@version AS ver');
10
+ return result.recordset[0].ver;
11
+ }
12
+ finally {
13
+ await pool.close();
14
+ }
15
+ }
16
+ async extract(config, onProgress) {
17
+ const pool = await this.createConnection(config);
18
+ const schema = config.schema || 'dbo';
19
+ try {
20
+ onProgress?.(`Connecting to SQL Server at ${config.host}...`);
21
+ onProgress?.(`Fetching server version...`);
22
+ const version = await this.getVersion(pool);
23
+ onProgress?.(`Fetching tables and columns from schema '${schema}'...`);
24
+ const tables = await this.getTables(pool, schema);
25
+ onProgress?.(`Found ${tables.length} tables.`);
26
+ onProgress?.(`Fetching primary keys...`);
27
+ const pks = await this.getPrimaryKeys(pool, schema);
28
+ onProgress?.(`Fetching foreign keys...`);
29
+ const fks = await this.getForeignKeys(pool, schema);
30
+ onProgress?.(`Fetching indexes...`);
31
+ const indexes = await this.getIndexes(pool, schema);
32
+ onProgress?.(`Building relationships and formatting output...`);
33
+ const pkMap = this.buildPKMap(pks);
34
+ const indexMap = this.buildIndexMap(indexes);
35
+ const extractedTables = tables.map((t) => ({
36
+ name: t.table_name,
37
+ schema: schema,
38
+ comment: t.table_comment || undefined,
39
+ columns: t.columns.map((c) => ({
40
+ name: c.COLUMN_NAME,
41
+ type: c.COLUMN_TYPE,
42
+ nullable: c.IS_NULLABLE === 'YES',
43
+ isPK: pkMap.get(t.table_name)?.has(c.COLUMN_NAME) ?? false,
44
+ defaultValue: c.COLUMN_DEFAULT || undefined,
45
+ comment: c.COLUMN_COMMENT || undefined,
46
+ ordinalPosition: c.ORDINAL_POSITION,
47
+ maxLength: c.CHARACTER_MAXIMUM_LENGTH || undefined,
48
+ numericPrecision: c.NUMERIC_PRECISION || undefined,
49
+ numericScale: c.NUMERIC_SCALE || undefined,
50
+ })),
51
+ indexes: indexMap.get(t.table_name) || [],
52
+ }));
53
+ return {
54
+ dbmsType: 'mssql',
55
+ dbmsVersion: version,
56
+ extractedAt: new Date().toISOString(),
57
+ schemaName: schema,
58
+ tables: extractedTables,
59
+ foreignKeys: fks,
60
+ };
61
+ }
62
+ finally {
63
+ await pool.close();
64
+ }
65
+ }
66
+ // ─── Private ─────────────────────────────────────────
67
+ async createConnection(config) {
68
+ return sql.connect({
69
+ server: config.host,
70
+ port: config.port,
71
+ database: config.database,
72
+ user: config.user,
73
+ password: config.password,
74
+ options: {
75
+ encrypt: config.ssl || false,
76
+ trustServerCertificate: !config.ssl,
77
+ },
78
+ connectionTimeout: 10000,
79
+ });
80
+ }
81
+ async getVersion(pool) {
82
+ const result = await pool.request().query('SELECT @@version AS ver');
83
+ return result.recordset[0].ver;
84
+ }
85
+ async getTables(pool, schema) {
86
+ const result = await pool.request()
87
+ .input('schema', sql.NVarChar, schema)
88
+ .query(`
89
+ SELECT
90
+ t.TABLE_NAME, c.COLUMN_NAME, c.ORDINAL_POSITION, c.DATA_TYPE as COLUMN_TYPE,
91
+ c.IS_NULLABLE, c.COLUMN_DEFAULT, c.CHARACTER_MAXIMUM_LENGTH,
92
+ c.NUMERIC_PRECISION, c.NUMERIC_SCALE,
93
+ CAST(ep.value AS NVARCHAR(MAX)) AS COLUMN_COMMENT
94
+ FROM information_schema.TABLES t
95
+ JOIN information_schema.COLUMNS c ON t.TABLE_NAME = c.TABLE_NAME AND t.TABLE_SCHEMA = c.TABLE_SCHEMA
96
+ LEFT JOIN sys.extended_properties ep ON ep.major_id = OBJECT_ID(t.TABLE_SCHEMA + '.' + t.TABLE_NAME)
97
+ AND ep.minor_id = c.ORDINAL_POSITION AND ep.name = 'MS_Description'
98
+ WHERE t.TABLE_SCHEMA = @schema AND t.TABLE_TYPE = 'BASE TABLE'
99
+ ORDER BY t.TABLE_NAME, c.ORDINAL_POSITION
100
+ `);
101
+ // 테이블 코멘트 가져오기
102
+ const tableComments = await pool.request()
103
+ .input('schema', sql.NVarChar, schema)
104
+ .query(`
105
+ SELECT
106
+ t.TABLE_NAME,
107
+ CAST(ep.value AS NVARCHAR(MAX)) AS TABLE_COMMENT
108
+ FROM information_schema.TABLES t
109
+ JOIN sys.extended_properties ep ON ep.major_id = OBJECT_ID(t.TABLE_SCHEMA + '.' + t.TABLE_NAME)
110
+ WHERE t.TABLE_SCHEMA = @schema AND ep.minor_id = 0 AND ep.name = 'MS_Description'
111
+ `);
112
+ const commentMap = new Map();
113
+ for (const tc of tableComments.recordset) {
114
+ commentMap.set(tc.TABLE_NAME, tc.TABLE_COMMENT);
115
+ }
116
+ const tableMap = new Map();
117
+ for (const col of result.recordset) {
118
+ if (!tableMap.has(col.TABLE_NAME)) {
119
+ tableMap.set(col.TABLE_NAME, {
120
+ table_name: col.TABLE_NAME,
121
+ table_comment: commentMap.get(col.TABLE_NAME) || null,
122
+ columns: [],
123
+ });
124
+ }
125
+ tableMap.get(col.TABLE_NAME).columns.push(col);
126
+ }
127
+ return Array.from(tableMap.values());
128
+ }
129
+ async getPrimaryKeys(pool, schema) {
130
+ const result = await pool.request()
131
+ .input('schema', sql.NVarChar, schema)
132
+ .query(`
133
+ SELECT c.TABLE_NAME, c.COLUMN_NAME
134
+ FROM information_schema.KEY_COLUMN_USAGE c
135
+ JOIN information_schema.TABLE_CONSTRAINTS t ON c.CONSTRAINT_NAME = t.CONSTRAINT_NAME
136
+ WHERE t.CONSTRAINT_TYPE = 'PRIMARY KEY' AND t.TABLE_SCHEMA = @schema
137
+ `);
138
+ return result.recordset;
139
+ }
140
+ async getForeignKeys(pool, schema) {
141
+ const result = await pool.request()
142
+ .input('schema', sql.NVarChar, schema)
143
+ .query(`
144
+ SELECT
145
+ fk.name AS CONSTRAINT_NAME,
146
+ tp.name AS source_table,
147
+ cp.name AS source_column,
148
+ tr.name AS target_table,
149
+ cr.name AS target_column,
150
+ fk.delete_referential_action_desc AS DELETE_RULE,
151
+ fk.update_referential_action_desc AS UPDATE_RULE
152
+ FROM sys.foreign_keys fk
153
+ INNER JOIN sys.tables tp ON fk.parent_object_id = tp.object_id
154
+ INNER JOIN sys.columns cp ON fk.parent_object_id = cp.object_id
155
+ INNER JOIN sys.foreign_key_columns fkc ON fk.object_id = fkc.constraint_object_id AND cp.column_id = fkc.parent_column_id
156
+ INNER JOIN sys.tables tr ON fk.referenced_object_id = tr.object_id
157
+ INNER JOIN sys.columns cr ON fk.referenced_object_id = cr.object_id AND cr.column_id = fkc.referenced_column_id
158
+ INNER JOIN sys.schemas s ON tp.schema_id = s.schema_id
159
+ WHERE s.name = @schema
160
+ `);
161
+ return result.recordset.map((r) => {
162
+ const mapRule = (rule) => {
163
+ if (rule === 'NO_ACTION')
164
+ return 'NO ACTION';
165
+ if (rule === 'CASCADE')
166
+ return 'CASCADE';
167
+ if (rule === 'SET_NULL')
168
+ return 'SET NULL';
169
+ if (rule === 'SET_DEFAULT')
170
+ return 'SET DEFAULT';
171
+ return undefined;
172
+ };
173
+ return {
174
+ name: r.CONSTRAINT_NAME,
175
+ sourceTable: r.source_table,
176
+ sourceSchema: schema,
177
+ sourceColumn: r.source_column,
178
+ targetTable: r.target_table,
179
+ targetSchema: schema,
180
+ targetColumn: r.target_column,
181
+ onDelete: mapRule(r.DELETE_RULE),
182
+ onUpdate: mapRule(r.UPDATE_RULE),
183
+ };
184
+ });
185
+ }
186
+ async getIndexes(pool, schema) {
187
+ const result = await pool.request()
188
+ .input('schema', sql.NVarChar, schema)
189
+ .query(`
190
+ SELECT
191
+ t.name AS TABLE_NAME,
192
+ i.name AS INDEX_NAME,
193
+ c.name AS COLUMN_NAME,
194
+ i.is_unique AS UNIQUE_INDEX
195
+ FROM sys.indexes i
196
+ INNER JOIN sys.tables t ON i.object_id = t.object_id
197
+ INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
198
+ INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
199
+ INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
200
+ WHERE s.name = @schema AND i.is_primary_key = 0 AND i.type > 0
201
+ ORDER BY t.name, i.name, ic.key_ordinal
202
+ `);
203
+ return result.recordset;
204
+ }
205
+ buildPKMap(pks) {
206
+ const map = new Map();
207
+ for (const pk of pks) {
208
+ if (!map.has(pk.TABLE_NAME))
209
+ map.set(pk.TABLE_NAME, new Set());
210
+ map.get(pk.TABLE_NAME).add(pk.COLUMN_NAME);
211
+ }
212
+ return map;
213
+ }
214
+ buildIndexMap(indexes) {
215
+ const map = new Map();
216
+ for (const idx of indexes) {
217
+ const key = `${idx.TABLE_NAME}.${idx.INDEX_NAME}`;
218
+ if (!map.has(idx.TABLE_NAME))
219
+ map.set(idx.TABLE_NAME, []);
220
+ let indexObj = map.get(idx.TABLE_NAME).find(i => i.name === idx.INDEX_NAME);
221
+ if (!indexObj) {
222
+ indexObj = {
223
+ name: idx.INDEX_NAME,
224
+ columns: [],
225
+ unique: idx.UNIQUE_INDEX === true || idx.UNIQUE_INDEX === 1,
226
+ };
227
+ map.get(idx.TABLE_NAME).push(indexObj);
228
+ }
229
+ indexObj.columns.push(idx.COLUMN_NAME);
230
+ }
231
+ return map;
232
+ }
233
+ }
234
+ //# sourceMappingURL=mssql.js.map