bjd-code 0.0.1 → 0.1.0
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 +116 -2
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +114 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/download.d.ts +12 -0
- package/dist/core/download.d.ts.map +1 -0
- package/dist/core/download.js +34 -0
- package/dist/core/download.js.map +1 -0
- package/dist/core/filter.d.ts +14 -0
- package/dist/core/filter.d.ts.map +1 -0
- package/dist/core/filter.js +30 -0
- package/dist/core/filter.js.map +1 -0
- package/dist/core/parser.d.ts +5 -0
- package/dist/core/parser.d.ts.map +1 -0
- package/dist/core/parser.js +70 -0
- package/dist/core/parser.js.map +1 -0
- package/dist/core/tree.d.ts +15 -0
- package/dist/core/tree.d.ts.map +1 -0
- package/dist/core/tree.js +95 -0
- package/dist/core/tree.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +70 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/code.d.ts +34 -0
- package/dist/utils/code.d.ts.map +1 -0
- package/dist/utils/code.js +68 -0
- package/dist/utils/code.js.map +1 -0
- package/dist/utils/normalize.d.ts +4 -0
- package/dist/utils/normalize.d.ts.map +1 -0
- package/dist/utils/normalize.js +21 -0
- package/dist/utils/normalize.js.map +1 -0
- package/dist/utils/paths.d.ts +13 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +24 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/worker/csv-worker.d.ts +2 -0
- package/dist/worker/csv-worker.d.ts.map +1 -0
- package/dist/worker/csv-worker.js +33 -0
- package/dist/worker/csv-worker.js.map +1 -0
- package/package.json +39 -9
- package/.env.example +0 -0
- package/.idea/bjd-code.iml +0 -12
- package/.idea/jsLibraryMappings.xml +0 -6
- package/.idea/misc.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/vcs.xml +0 -6
- package/index.ts +0 -122
- package/tsconfig.json +0 -100
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 song
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,5 +1,119 @@
|
|
|
1
1
|
# bjd-code (법정동 코드)
|
|
2
2
|
|
|
3
|
-
[국토교통부_전국 법정동
|
|
3
|
+
[국토교통부_전국 법정동 CSV](https://www.data.go.kr/tcs/dss/selectFileDataDetailView.do?publicDataPk=15063424) 데이터를 파싱, 필터링, 트리 구조화하는 Node.js/TypeScript 라이브러리.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## 기능
|
|
6
|
+
|
|
7
|
+
- CSV 파싱 (EUC-KR/UTF-8 자동 처리, worker_threads 지원)
|
|
8
|
+
- 시도/시군구/읍면동/리 레벨별 필터링
|
|
9
|
+
- 이름 검색
|
|
10
|
+
- 트리 구조 변환 (시도 → 시군구 → 읍면동 → 리)
|
|
11
|
+
- CLI 도구 (다운로드, 파싱, 필터링, 트리, 검색)
|
|
12
|
+
|
|
13
|
+
## 설치
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install bjd-code
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## 빠른 시작
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# 1. 데이터 다운로드 (~/.bjd-code/data.csv에 저장)
|
|
23
|
+
bjd download
|
|
24
|
+
|
|
25
|
+
# 2. 바로 사용 (파일 경로 생략 가능)
|
|
26
|
+
bjd parse --pretty
|
|
27
|
+
bjd filter --level sido --pretty
|
|
28
|
+
bjd tree --pretty
|
|
29
|
+
bjd search 강남구
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 사용법
|
|
33
|
+
|
|
34
|
+
### 라이브러리
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { parseCSV, filterSido, buildTree, loadTree, searchByName, downloadCSV } from 'bjd-code';
|
|
38
|
+
|
|
39
|
+
// 데이터 다운로드 (최초 1회)
|
|
40
|
+
await downloadCSV();
|
|
41
|
+
|
|
42
|
+
// CSV 파싱 (경로 생략 시 ~/.bjd-code/data.csv 사용)
|
|
43
|
+
const records = await parseCSV();
|
|
44
|
+
|
|
45
|
+
// 직접 파일 경로 지정도 가능
|
|
46
|
+
const records2 = await parseCSV({ filePath: './my-data.csv' });
|
|
47
|
+
|
|
48
|
+
// 시도만 추출
|
|
49
|
+
const sidos = filterSido(records);
|
|
50
|
+
|
|
51
|
+
// 트리 구조 빌드
|
|
52
|
+
const tree = buildTree(records.filter(r => r.isActive));
|
|
53
|
+
|
|
54
|
+
// 편의 함수 (파싱 + 트리 한번에)
|
|
55
|
+
const tree2 = await loadTree();
|
|
56
|
+
|
|
57
|
+
// 이름 검색
|
|
58
|
+
const results = searchByName(records, '종로구');
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### CLI
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# 데이터 다운로드
|
|
65
|
+
bjd download # ~/.bjd-code/data.csv에 저장
|
|
66
|
+
bjd download -o ./data/bjd.csv # 경로 지정
|
|
67
|
+
bjd download --url <url> # URL 직접 지정
|
|
68
|
+
|
|
69
|
+
# 전체 데이터 파싱
|
|
70
|
+
bjd parse # 다운로드된 데이터 사용
|
|
71
|
+
bjd parse ./my-data.csv # 파일 경로 직접 지정
|
|
72
|
+
|
|
73
|
+
# 레벨별 필터링
|
|
74
|
+
bjd filter --level sido --pretty
|
|
75
|
+
bjd filter ./data.csv --level sigungu
|
|
76
|
+
|
|
77
|
+
# 트리 구조
|
|
78
|
+
bjd tree --pretty -o tree.json
|
|
79
|
+
|
|
80
|
+
# 이름 검색
|
|
81
|
+
bjd search 강남구
|
|
82
|
+
|
|
83
|
+
# 공통 옵션
|
|
84
|
+
# --include-inactive 폐지된 코드 포함
|
|
85
|
+
# --pretty JSON 포맷팅
|
|
86
|
+
# -o, --output <file> 파일로 저장
|
|
87
|
+
# --encoding <enc> CSV 인코딩 (기본: euc-kr)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## 코드 구조
|
|
91
|
+
|
|
92
|
+
법정동 코드는 10자리 숫자로 구성:
|
|
93
|
+
|
|
94
|
+
| 위치 | 자릿수 | 레벨 | 예시 |
|
|
95
|
+
|------|--------|------|------|
|
|
96
|
+
| 1-2 | 시도 | `sido` | 11 (서울) |
|
|
97
|
+
| 3-5 | 시군구 | `sigungu` | 110 (종로구) |
|
|
98
|
+
| 6-8 | 읍면동 | `eupmyeondong` | 101 (청운동) |
|
|
99
|
+
| 9-10 | 리 | `ri` | 21 (송산리) |
|
|
100
|
+
|
|
101
|
+
뒷자리가 0으로 채워진 패턴으로 레벨을 판단합니다:
|
|
102
|
+
- `1100000000` → 시도 (서울특별시)
|
|
103
|
+
- `1111000000` → 시군구 (종로구)
|
|
104
|
+
- `1111010100` → 읍면동 (청운동)
|
|
105
|
+
- `4372025021` → 리 (송산리)
|
|
106
|
+
|
|
107
|
+
## 개발
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npm install
|
|
111
|
+
npm run build
|
|
112
|
+
npm test
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## 기술 스택
|
|
116
|
+
|
|
117
|
+
- Node.js 24+, ESM
|
|
118
|
+
- TypeScript, Vitest
|
|
119
|
+
- csv-parse, iconv-lite, commander
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { writeFileSync } from 'node:fs';
|
|
3
|
+
import { Command, InvalidArgumentError } from 'commander';
|
|
4
|
+
import { parseCSV } from '../core/parser.js';
|
|
5
|
+
import { filterByLevel, searchByName } from '../core/filter.js';
|
|
6
|
+
import { buildTree } from '../core/tree.js';
|
|
7
|
+
import { downloadCSV, MANUAL_INSTRUCTIONS } from '../core/download.js';
|
|
8
|
+
const VALID_LEVELS = ['sido', 'sigungu', 'eupmyeondong', 'ri'];
|
|
9
|
+
function parseLevel(value) {
|
|
10
|
+
if (!VALID_LEVELS.includes(value)) {
|
|
11
|
+
throw new InvalidArgumentError(`유효하지 않은 레벨: "${value}" (가능한 값: ${VALID_LEVELS.join(', ')})`);
|
|
12
|
+
}
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
const program = new Command();
|
|
16
|
+
program
|
|
17
|
+
.name('bjd')
|
|
18
|
+
.description('법정동 코드 파싱/필터링/트리 구조화 CLI')
|
|
19
|
+
.version('0.1.0');
|
|
20
|
+
function output(data, opts) {
|
|
21
|
+
const json = opts.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
|
|
22
|
+
if (opts.output) {
|
|
23
|
+
writeFileSync(opts.output, json, 'utf-8');
|
|
24
|
+
console.log(`결과가 ${opts.output}에 저장되었습니다.`);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
console.log(json);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function addCommonOptions(cmd) {
|
|
31
|
+
return cmd
|
|
32
|
+
.option('-f, --file <path>', 'CSV 파일 경로 (기본: ~/.bjd-code/data.csv)')
|
|
33
|
+
.option('--include-inactive', '폐지된 코드 포함', false)
|
|
34
|
+
.option('--pretty', 'JSON 예쁘게 출력', false)
|
|
35
|
+
.option('-o, --output <file>', '결과를 파일로 저장')
|
|
36
|
+
.option('--encoding <encoding>', 'CSV 인코딩', 'euc-kr');
|
|
37
|
+
}
|
|
38
|
+
function handleError(err) {
|
|
39
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
40
|
+
console.error(`오류: ${message}`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
program
|
|
44
|
+
.command('download')
|
|
45
|
+
.description('법정동 코드 CSV 다운로드')
|
|
46
|
+
.option('--url <url>', '다운로드 URL 직접 지정')
|
|
47
|
+
.option('-o, --output <path>', '저장 경로 (기본: ~/.bjd-code/data.csv)')
|
|
48
|
+
.action(async (opts) => {
|
|
49
|
+
try {
|
|
50
|
+
console.log('다운로드 중...');
|
|
51
|
+
const savedPath = await downloadCSV({ url: opts.url, outputPath: opts.output });
|
|
52
|
+
console.log(`저장 완료: ${savedPath}`);
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
56
|
+
console.error(`다운로드 실패: ${message}`);
|
|
57
|
+
console.error(`\n수동 다운로드 방법:\n${MANUAL_INSTRUCTIONS}`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
addCommonOptions(program
|
|
62
|
+
.command('parse')
|
|
63
|
+
.description('CSV 파일을 파싱하여 전체 데이터 JSON 출력')).action(async (opts) => {
|
|
64
|
+
try {
|
|
65
|
+
const records = await parseCSV({ filePath: opts.file, encoding: opts.encoding, useWorker: false });
|
|
66
|
+
const result = opts.includeInactive ? records : records.filter((r) => r.isActive);
|
|
67
|
+
output(result, opts);
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
handleError(err);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
addCommonOptions(program
|
|
74
|
+
.command('filter')
|
|
75
|
+
.description('레벨별 필터링')
|
|
76
|
+
.requiredOption('--level <level>', '필터 레벨 (sido, sigungu, eupmyeondong, ri)', parseLevel)).action(async (opts) => {
|
|
77
|
+
try {
|
|
78
|
+
const records = await parseCSV({ filePath: opts.file, encoding: opts.encoding, useWorker: false });
|
|
79
|
+
const filterOpts = { includeInactive: opts.includeInactive };
|
|
80
|
+
const result = filterByLevel(records, opts.level, filterOpts);
|
|
81
|
+
output(result, opts);
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
handleError(err);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
addCommonOptions(program
|
|
88
|
+
.command('tree')
|
|
89
|
+
.description('트리 구조 JSON 출력')).action(async (opts) => {
|
|
90
|
+
try {
|
|
91
|
+
const records = await parseCSV({ filePath: opts.file, encoding: opts.encoding, useWorker: false });
|
|
92
|
+
const active = opts.includeInactive ? records : records.filter((r) => r.isActive);
|
|
93
|
+
const tree = buildTree(active);
|
|
94
|
+
output(tree, opts);
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
handleError(err);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
addCommonOptions(program
|
|
101
|
+
.command('search <query>')
|
|
102
|
+
.description('이름으로 검색')).action(async (query, opts) => {
|
|
103
|
+
try {
|
|
104
|
+
const records = await parseCSV({ filePath: opts.file, encoding: opts.encoding, useWorker: false });
|
|
105
|
+
const filterOpts = { includeInactive: opts.includeInactive };
|
|
106
|
+
const result = searchByName(records, query, filterOpts);
|
|
107
|
+
output(result, opts);
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
handleError(err);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
program.parse();
|
|
114
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DATA_DIR } from '../utils/paths.js';
|
|
2
|
+
export interface DownloadOptions {
|
|
3
|
+
/** 다운로드 URL (기본: data.go.kr) */
|
|
4
|
+
url?: string;
|
|
5
|
+
/** 저장 경로 (기본: ~/.bjd-code/data.csv) */
|
|
6
|
+
outputPath?: string;
|
|
7
|
+
}
|
|
8
|
+
/** 법정동 코드 CSV 다운로드 */
|
|
9
|
+
export declare function downloadCSV(options?: DownloadOptions): Promise<string>;
|
|
10
|
+
declare const MANUAL_INSTRUCTIONS: string;
|
|
11
|
+
export { MANUAL_INSTRUCTIONS, DATA_DIR };
|
|
12
|
+
//# sourceMappingURL=download.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/core/download.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAoB,MAAM,mBAAmB,CAAC;AAK/D,MAAM,WAAW,eAAe;IAC9B,gCAAgC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,sBAAsB;AACtB,wBAAsB,WAAW,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAyB5E;AAED,QAAA,MAAM,mBAAmB,QAIgC,CAAC;AAE1D,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createWriteStream, mkdirSync } from 'node:fs';
|
|
2
|
+
import { pipeline } from 'node:stream/promises';
|
|
3
|
+
import { Readable } from 'node:stream';
|
|
4
|
+
import { dirname } from 'node:path';
|
|
5
|
+
import { DATA_DIR, DEFAULT_CSV_PATH } from '../utils/paths.js';
|
|
6
|
+
const DOWNLOAD_URL = 'https://www.data.go.kr/cmm/cmm/fileDownload.do?atchFileId=FILE_000000002647058&fileDetailSn=1';
|
|
7
|
+
/** 법정동 코드 CSV 다운로드 */
|
|
8
|
+
export async function downloadCSV(options) {
|
|
9
|
+
const url = options?.url ?? DOWNLOAD_URL;
|
|
10
|
+
const outputPath = options?.outputPath ?? DEFAULT_CSV_PATH;
|
|
11
|
+
mkdirSync(dirname(outputPath), { recursive: true });
|
|
12
|
+
const response = await fetch(url, {
|
|
13
|
+
headers: {
|
|
14
|
+
'User-Agent': 'bjd-code/0.1.0',
|
|
15
|
+
},
|
|
16
|
+
redirect: 'follow',
|
|
17
|
+
});
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
throw new Error(`다운로드 실패 (HTTP ${response.status}). 수동으로 다운로드하세요:\n${MANUAL_INSTRUCTIONS}`);
|
|
20
|
+
}
|
|
21
|
+
if (!response.body) {
|
|
22
|
+
throw new Error('응답 본문이 비어있습니다.');
|
|
23
|
+
}
|
|
24
|
+
const body = Readable.fromWeb(response.body);
|
|
25
|
+
await pipeline(body, createWriteStream(outputPath));
|
|
26
|
+
return outputPath;
|
|
27
|
+
}
|
|
28
|
+
const MANUAL_INSTRUCTIONS = `
|
|
29
|
+
1. https://www.data.go.kr/tcs/dss/selectFileDataDetailView.do?publicDataPk=15063424 접속
|
|
30
|
+
2. CSV 파일 다운로드
|
|
31
|
+
3. 다운로드한 파일을 다음 경로에 저장: ${DEFAULT_CSV_PATH}
|
|
32
|
+
또는 CLI 사용 시 파일 경로를 직접 지정: bjd parse ./파일경로.csv`.trim();
|
|
33
|
+
export { MANUAL_INSTRUCTIONS, DATA_DIR };
|
|
34
|
+
//# sourceMappingURL=download.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.js","sourceRoot":"","sources":["../../src/core/download.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE/D,MAAM,YAAY,GAChB,+FAA+F,CAAC;AASlG,sBAAsB;AACtB,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAyB;IACzD,MAAM,GAAG,GAAG,OAAO,EAAE,GAAG,IAAI,YAAY,CAAC;IACzC,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,gBAAgB,CAAC;IAE3D,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEpD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAChC,OAAO,EAAE;YACP,YAAY,EAAE,gBAAgB;SAC/B;QACD,QAAQ,EAAE,QAAQ;KACnB,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,iBAAiB,QAAQ,CAAC,MAAM,qBAAqB,mBAAmB,EAAE,CAAC,CAAC;IAC9F,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACpC,CAAC;IAED,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAgD,CAAC,CAAC;IACzF,MAAM,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC;IAEpD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,mBAAmB,GAAG;;;0BAGF,gBAAgB;kDACQ,CAAC,IAAI,EAAE,CAAC;AAE1D,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { BjdLevel, BjdRecord, FilterOptions } from '../types/index.js';
|
|
2
|
+
/** 시도 필터링 */
|
|
3
|
+
export declare function filterSido(records: BjdRecord[], options?: FilterOptions): BjdRecord[];
|
|
4
|
+
/** 시군구 필터링 */
|
|
5
|
+
export declare function filterSigungu(records: BjdRecord[], options?: FilterOptions): BjdRecord[];
|
|
6
|
+
/** 읍면동 필터링 */
|
|
7
|
+
export declare function filterEupmyeondong(records: BjdRecord[], options?: FilterOptions): BjdRecord[];
|
|
8
|
+
/** 리 필터링 */
|
|
9
|
+
export declare function filterRi(records: BjdRecord[], options?: FilterOptions): BjdRecord[];
|
|
10
|
+
/** 레벨별 필터링 (단일 순회) */
|
|
11
|
+
export declare function filterByLevel(records: BjdRecord[], level: BjdLevel, options?: FilterOptions): BjdRecord[];
|
|
12
|
+
/** 이름 부분 검색 (단일 순회) */
|
|
13
|
+
export declare function searchByName(records: BjdRecord[], query: string, options?: FilterOptions): BjdRecord[];
|
|
14
|
+
//# sourceMappingURL=filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../../src/core/filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAE5E,aAAa;AACb,wBAAgB,UAAU,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,SAAS,EAAE,CAErF;AAED,cAAc;AACd,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,SAAS,EAAE,CAExF;AAED,cAAc;AACd,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,SAAS,EAAE,CAE7F;AAED,YAAY;AACZ,wBAAgB,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,SAAS,EAAE,CAEnF;AAED,sBAAsB;AACtB,wBAAgB,aAAa,CAC3B,OAAO,EAAE,SAAS,EAAE,EACpB,KAAK,EAAE,QAAQ,EACf,OAAO,CAAC,EAAE,aAAa,GACtB,SAAS,EAAE,CAGb;AAED,uBAAuB;AACvB,wBAAgB,YAAY,CAC1B,OAAO,EAAE,SAAS,EAAE,EACpB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,aAAa,GACtB,SAAS,EAAE,CAKb"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** 시도 필터링 */
|
|
2
|
+
export function filterSido(records, options) {
|
|
3
|
+
return filterByLevel(records, 'sido', options);
|
|
4
|
+
}
|
|
5
|
+
/** 시군구 필터링 */
|
|
6
|
+
export function filterSigungu(records, options) {
|
|
7
|
+
return filterByLevel(records, 'sigungu', options);
|
|
8
|
+
}
|
|
9
|
+
/** 읍면동 필터링 */
|
|
10
|
+
export function filterEupmyeondong(records, options) {
|
|
11
|
+
return filterByLevel(records, 'eupmyeondong', options);
|
|
12
|
+
}
|
|
13
|
+
/** 리 필터링 */
|
|
14
|
+
export function filterRi(records, options) {
|
|
15
|
+
return filterByLevel(records, 'ri', options);
|
|
16
|
+
}
|
|
17
|
+
/** 레벨별 필터링 (단일 순회) */
|
|
18
|
+
export function filterByLevel(records, level, options) {
|
|
19
|
+
const includeInactive = options?.includeInactive ?? false;
|
|
20
|
+
return records.filter((r) => r.level === level && (includeInactive || r.isActive));
|
|
21
|
+
}
|
|
22
|
+
/** 이름 부분 검색 (단일 순회) */
|
|
23
|
+
export function searchByName(records, query, options) {
|
|
24
|
+
const q = query.trim();
|
|
25
|
+
if (!q)
|
|
26
|
+
return [];
|
|
27
|
+
const includeInactive = options?.includeInactive ?? false;
|
|
28
|
+
return records.filter((r) => r.name.includes(q) && (includeInactive || r.isActive));
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/core/filter.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,MAAM,UAAU,UAAU,CAAC,OAAoB,EAAE,OAAuB;IACtE,OAAO,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AACjD,CAAC;AAED,cAAc;AACd,MAAM,UAAU,aAAa,CAAC,OAAoB,EAAE,OAAuB;IACzE,OAAO,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AACpD,CAAC;AAED,cAAc;AACd,MAAM,UAAU,kBAAkB,CAAC,OAAoB,EAAE,OAAuB;IAC9E,OAAO,aAAa,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;AACzD,CAAC;AAED,YAAY;AACZ,MAAM,UAAU,QAAQ,CAAC,OAAoB,EAAE,OAAuB;IACpE,OAAO,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC;AAED,sBAAsB;AACtB,MAAM,UAAU,aAAa,CAC3B,OAAoB,EACpB,KAAe,EACf,OAAuB;IAEvB,MAAM,eAAe,GAAG,OAAO,EAAE,eAAe,IAAI,KAAK,CAAC;IAC1D,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,IAAI,CAAC,eAAe,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;AACrF,CAAC;AAED,uBAAuB;AACvB,MAAM,UAAU,YAAY,CAC1B,OAAoB,EACpB,KAAa,EACb,OAAuB;IAEvB,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACvB,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAClB,MAAM,eAAe,GAAG,OAAO,EAAE,eAAe,IAAI,KAAK,CAAC;IAC1D,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;AACtF,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { BjdRecord, ParseOptions } from '../types/index.js';
|
|
2
|
+
export { normalizeRow } from '../utils/normalize.js';
|
|
3
|
+
/** CSV 파싱 → BjdRecord[] */
|
|
4
|
+
export declare function parseCSV(options?: ParseOptions): Promise<BjdRecord[]>;
|
|
5
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/core/parser.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,SAAS,EACT,YAAY,EAIb,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAgErD,2BAA2B;AAC3B,wBAAsB,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAO3E"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Worker } from 'node:worker_threads';
|
|
2
|
+
import { createReadStream } from 'node:fs';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { parse } from 'csv-parse';
|
|
5
|
+
import iconv from 'iconv-lite';
|
|
6
|
+
import { normalizeRow } from '../utils/normalize.js';
|
|
7
|
+
import { resolveDataPath } from '../utils/paths.js';
|
|
8
|
+
export { normalizeRow } from '../utils/normalize.js';
|
|
9
|
+
// vitest에서는 .ts 소스를 실행하므로 dist/ 경로로 우회
|
|
10
|
+
const isSource = import.meta.url.endsWith('.ts');
|
|
11
|
+
const WORKER_PATH = isSource
|
|
12
|
+
? new URL('../../dist/worker/csv-worker.js', import.meta.url)
|
|
13
|
+
: new URL('../worker/csv-worker.js', import.meta.url);
|
|
14
|
+
/** 메인 스레드에서 CSV 파싱 (폴백) */
|
|
15
|
+
async function parseInMainThread(filePath, encoding) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const records = [];
|
|
18
|
+
const fileStream = createReadStream(filePath);
|
|
19
|
+
const decoder = encoding.toLowerCase() === 'utf-8' || encoding.toLowerCase() === 'utf8'
|
|
20
|
+
? fileStream
|
|
21
|
+
: fileStream.pipe(iconv.decodeStream(encoding));
|
|
22
|
+
const csvParser = parse({
|
|
23
|
+
columns: true,
|
|
24
|
+
skip_empty_lines: true,
|
|
25
|
+
trim: true,
|
|
26
|
+
bom: true,
|
|
27
|
+
});
|
|
28
|
+
decoder.pipe(csvParser);
|
|
29
|
+
csvParser.on('data', (row) => {
|
|
30
|
+
const record = normalizeRow(row);
|
|
31
|
+
if (record) {
|
|
32
|
+
records.push(record);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
csvParser.on('end', () => resolve(records));
|
|
36
|
+
csvParser.on('error', reject);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/** Worker를 사용하여 CSV 파싱 */
|
|
40
|
+
async function parseWithWorker(filePath, encoding) {
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
const workerDataPayload = { filePath, encoding };
|
|
43
|
+
const worker = new Worker(fileURLToPath(WORKER_PATH), {
|
|
44
|
+
workerData: workerDataPayload,
|
|
45
|
+
});
|
|
46
|
+
worker.on('message', (msg) => {
|
|
47
|
+
if (msg.type === 'result') {
|
|
48
|
+
resolve(msg.data);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
reject(new Error(msg.message));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
worker.on('error', reject);
|
|
55
|
+
worker.on('exit', (exitCode) => {
|
|
56
|
+
if (exitCode !== 0) {
|
|
57
|
+
reject(new Error(`Worker exited with code ${exitCode}`));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/** CSV 파싱 → BjdRecord[] */
|
|
63
|
+
export async function parseCSV(options) {
|
|
64
|
+
const { filePath, encoding = 'euc-kr', useWorker = true } = options ?? {};
|
|
65
|
+
const resolved = resolveDataPath(filePath);
|
|
66
|
+
return useWorker
|
|
67
|
+
? parseWithWorker(resolved, encoding)
|
|
68
|
+
: parseInMainThread(resolved, encoding);
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/core/parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { BjdRecord, BjdTreeNode } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* BjdRecord[] → 트리 구조 빌드 (O(n) 단일 패스)
|
|
4
|
+
* 시도 → 시군구 → 읍면동 → 리 계층
|
|
5
|
+
*/
|
|
6
|
+
export declare function buildTree(records: BjdRecord[]): BjdTreeNode[];
|
|
7
|
+
/**
|
|
8
|
+
* 특정 코드 프리픽스 기준 서브트리 빌드
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildSubTree(records: BjdRecord[], prefix: string): BjdTreeNode[];
|
|
11
|
+
/**
|
|
12
|
+
* 트리 → 배열 평탄화 (깊이 우선)
|
|
13
|
+
*/
|
|
14
|
+
export declare function flattenTree(nodes: BjdTreeNode[]): BjdTreeNode[];
|
|
15
|
+
//# sourceMappingURL=tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../src/core/tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhE;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,WAAW,EAAE,CAmC7D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE,CAGhF;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAc/D"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BjdRecord[] → 트리 구조 빌드 (O(n) 단일 패스)
|
|
3
|
+
* 시도 → 시군구 → 읍면동 → 리 계층
|
|
4
|
+
*/
|
|
5
|
+
export function buildTree(records) {
|
|
6
|
+
const roots = [];
|
|
7
|
+
const nodeMap = new Map();
|
|
8
|
+
// 이미 정렬되어 있으면 복사/정렬 skip
|
|
9
|
+
const sorted = isSorted(records)
|
|
10
|
+
? records
|
|
11
|
+
: [...records].sort((a, b) => a.code.localeCompare(b.code));
|
|
12
|
+
for (const record of sorted) {
|
|
13
|
+
const node = {
|
|
14
|
+
code: getCodeSegment(record),
|
|
15
|
+
fullCode: record.code,
|
|
16
|
+
name: record.names[record.names.length - 1],
|
|
17
|
+
level: record.level,
|
|
18
|
+
children: [],
|
|
19
|
+
};
|
|
20
|
+
nodeMap.set(record.code, node);
|
|
21
|
+
const parentKey = getParentKey(record);
|
|
22
|
+
if (parentKey) {
|
|
23
|
+
const parent = nodeMap.get(parentKey);
|
|
24
|
+
if (parent) {
|
|
25
|
+
parent.children.push(node);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
// 부모가 없으면 루트에 추가
|
|
29
|
+
roots.push(node);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
roots.push(node);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return roots;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 특정 코드 프리픽스 기준 서브트리 빌드
|
|
40
|
+
*/
|
|
41
|
+
export function buildSubTree(records, prefix) {
|
|
42
|
+
const filtered = records.filter((r) => r.code.startsWith(prefix));
|
|
43
|
+
return buildTree(filtered);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 트리 → 배열 평탄화 (깊이 우선)
|
|
47
|
+
*/
|
|
48
|
+
export function flattenTree(nodes) {
|
|
49
|
+
const result = [];
|
|
50
|
+
function walk(nodeList) {
|
|
51
|
+
for (const node of nodeList) {
|
|
52
|
+
result.push(node);
|
|
53
|
+
if (node.children.length > 0) {
|
|
54
|
+
walk(node.children);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
walk(nodes);
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
/** 해당 레벨의 코드 조각 추출 */
|
|
62
|
+
function getCodeSegment(record) {
|
|
63
|
+
switch (record.level) {
|
|
64
|
+
case 'sido':
|
|
65
|
+
return record.sidoCode;
|
|
66
|
+
case 'sigungu':
|
|
67
|
+
return record.sigunguCode;
|
|
68
|
+
case 'eupmyeondong':
|
|
69
|
+
return record.eupmyeondongCode;
|
|
70
|
+
case 'ri':
|
|
71
|
+
return record.riCode;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** 배열이 코드 기준 정렬 상태인지 확인 */
|
|
75
|
+
function isSorted(records) {
|
|
76
|
+
for (let i = 1; i < records.length; i++) {
|
|
77
|
+
if (records[i].code < records[i - 1].code)
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
/** 부모 노드의 전체 코드 계산 */
|
|
83
|
+
function getParentKey(record) {
|
|
84
|
+
switch (record.level) {
|
|
85
|
+
case 'sido':
|
|
86
|
+
return null;
|
|
87
|
+
case 'sigungu':
|
|
88
|
+
return record.sidoCode + '00000000';
|
|
89
|
+
case 'eupmyeondong':
|
|
90
|
+
return record.sidoCode + record.sigunguCode + '00000';
|
|
91
|
+
case 'ri':
|
|
92
|
+
return record.sidoCode + record.sigunguCode + record.eupmyeondongCode + '00';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree.js","sourceRoot":"","sources":["../../src/core/tree.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type { RawBjdRow, BjdRecord, BjdLevel, BjdTreeNode, FilterOptions, ParseOptions, } from './types/index.js';
|
|
2
|
+
export { extractSidoCode, extractSigunguCode, extractEupmyeondongCode, extractRiCode, determineLevel, isValidCode, getParentCode, } from './utils/code.js';
|
|
3
|
+
export { parseCSV, normalizeRow } from './core/parser.js';
|
|
4
|
+
export { filterSido, filterSigungu, filterEupmyeondong, filterRi, filterByLevel, searchByName, } from './core/filter.js';
|
|
5
|
+
export { buildTree, buildSubTree, flattenTree } from './core/tree.js';
|
|
6
|
+
export { downloadCSV } from './core/download.js';
|
|
7
|
+
export type { DownloadOptions } from './core/download.js';
|
|
8
|
+
export { DEFAULT_CSV_PATH, hasDefaultData, resolveDataPath } from './utils/paths.js';
|
|
9
|
+
import type { BjdRecord, BjdTreeNode, BjdLevel, ParseOptions, FilterOptions } from './types/index.js';
|
|
10
|
+
/** parse + buildTree 편의 함수 */
|
|
11
|
+
export declare function loadTree(options?: ParseOptions): Promise<BjdTreeNode[]>;
|
|
12
|
+
/** parse + filter 편의 함수 */
|
|
13
|
+
export declare function loadFiltered(options?: ParseOptions & {
|
|
14
|
+
level?: BjdLevel;
|
|
15
|
+
query?: string;
|
|
16
|
+
} & FilterOptions): Promise<BjdRecord[]>;
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,SAAS,EACT,SAAS,EACT,QAAQ,EACR,WAAW,EACX,aAAa,EACb,YAAY,GACb,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,uBAAuB,EACvB,aAAa,EACb,cAAc,EACd,WAAW,EACX,aAAa,GACd,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAG1D,OAAO,EACL,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,YAAY,GACb,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGtE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAG1D,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGrF,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAKtG,8BAA8B;AAC9B,wBAAsB,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAI7E;AAED,2BAA2B;AAC3B,wBAAsB,YAAY,CAChC,OAAO,CAAC,EAAE,YAAY,GAAG;IAAE,KAAK,CAAC,EAAE,QAAQ,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,aAAa,GAC5E,OAAO,CAAC,SAAS,EAAE,CAAC,CActB"}
|