@damarkuncoro/posindonesia 1.0.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 +89 -0
- package/lib/cjs/api.d.ts +8 -0
- package/lib/cjs/api.d.ts.map +1 -0
- package/lib/cjs/api.js +37 -0
- package/lib/cjs/api.js.map +1 -0
- package/lib/cjs/core.d.ts +25 -0
- package/lib/cjs/core.d.ts.map +1 -0
- package/lib/cjs/core.js +90 -0
- package/lib/cjs/core.js.map +1 -0
- package/lib/cjs/index.d.ts +3 -0
- package/lib/cjs/index.d.ts.map +1 -0
- package/lib/cjs/index.js +96 -0
- package/lib/cjs/index.js.map +1 -0
- package/lib/cjs/logger.d.ts +4 -0
- package/lib/cjs/logger.d.ts.map +1 -0
- package/lib/cjs/logger.js +22 -0
- package/lib/cjs/logger.js.map +1 -0
- package/lib/cjs/main.d.ts +5 -0
- package/lib/cjs/main.d.ts.map +1 -0
- package/lib/cjs/main.js +21 -0
- package/lib/cjs/main.js.map +1 -0
- package/lib/cjs/parser.d.ts +15 -0
- package/lib/cjs/parser.d.ts.map +1 -0
- package/lib/cjs/parser.js +61 -0
- package/lib/cjs/parser.js.map +1 -0
- package/lib/cjs/utils.d.ts +16 -0
- package/lib/cjs/utils.d.ts.map +1 -0
- package/lib/cjs/utils.js +59 -0
- package/lib/cjs/utils.js.map +1 -0
- package/lib/esm/api.d.ts +8 -0
- package/lib/esm/api.d.ts.map +1 -0
- package/lib/esm/api.js +31 -0
- package/lib/esm/api.js.map +1 -0
- package/lib/esm/core.d.ts +25 -0
- package/lib/esm/core.d.ts.map +1 -0
- package/lib/esm/core.js +54 -0
- package/lib/esm/core.js.map +1 -0
- package/lib/esm/index.d.ts +3 -0
- package/lib/esm/index.d.ts.map +1 -0
- package/lib/esm/index.js +58 -0
- package/lib/esm/index.js.map +1 -0
- package/lib/esm/logger.d.ts +4 -0
- package/lib/esm/logger.d.ts.map +1 -0
- package/lib/esm/logger.js +17 -0
- package/lib/esm/logger.js.map +1 -0
- package/lib/esm/main.d.ts +5 -0
- package/lib/esm/main.d.ts.map +1 -0
- package/lib/esm/main.js +5 -0
- package/lib/esm/main.js.map +1 -0
- package/lib/esm/parser.d.ts +15 -0
- package/lib/esm/parser.d.ts.map +1 -0
- package/lib/esm/parser.js +25 -0
- package/lib/esm/parser.js.map +1 -0
- package/lib/esm/utils.d.ts +16 -0
- package/lib/esm/utils.d.ts.map +1 -0
- package/lib/esm/utils.js +52 -0
- package/lib/esm/utils.js.map +1 -0
- package/package.json +77 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 damarkuncoro
|
|
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
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# @damarkuncoro/posindonesia
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@damarkuncoro/posindonesia)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
**Layanan Integrasi Data Kode Pos Indonesia**
|
|
7
|
+
|
|
8
|
+
Pustaka Node.js profesional yang menyediakan akses programatik ke database kode pos resmi Indonesia. Dirancang untuk keandalan tinggi, akurasi data, dan kemudahan integrasi ke dalam sistem skala perusahaan (*enterprise*).
|
|
9
|
+
|
|
10
|
+
## Ikhtisar Layanan
|
|
11
|
+
|
|
12
|
+
Paket ini memungkinkan pengembang untuk melakukan sinkronisasi data wilayah administrasi Indonesia (Provinsi, Kabupaten/Kota, Kecamatan, Desa/Kelurahan) dengan database kode pos terbaru melalui jalur integrasi yang dioptimalkan.
|
|
13
|
+
|
|
14
|
+
### Fitur Utama
|
|
15
|
+
|
|
16
|
+
- **Akurasi Data Terjamin**: Mengambil informasi langsung dari sumber data primer integrasi wilayah.
|
|
17
|
+
- **Smart Validation Engine**: Algoritma pencocokan cerdas yang mengoreksi variasi penulisan nama daerah untuk memastikan kode pos yang tepat.
|
|
18
|
+
- **Efisiensi Batch**: Kemampuan pemrosesan data masal (*bulk processing*) dengan manajemen beban kerja (*rate limiting*) otomatis.
|
|
19
|
+
- **Arsitektur Modular**: Mendukung penggunaan sebagai Command Line Interface (CLI) maupun sebagai Library (SDK) dalam aplikasi Node.js/TypeScript.
|
|
20
|
+
|
|
21
|
+
## Instalasi
|
|
22
|
+
|
|
23
|
+
Gunakan pengelola paket npm untuk menginstal SDK ini ke dalam proyek Anda:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @damarkuncoro/posindonesia
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Panduan Penggunaan
|
|
30
|
+
|
|
31
|
+
### Integrasi SDK (Library Mode)
|
|
32
|
+
|
|
33
|
+
SDK ini dirancang dengan dukungan TypeScript penuh untuk pengalaman pengembangan yang maksimal.
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { runScraper } from '@damarkuncoro/posindonesia';
|
|
37
|
+
|
|
38
|
+
async function syncPostalData() {
|
|
39
|
+
const results = await runScraper({
|
|
40
|
+
input: './data/villages.csv',
|
|
41
|
+
limit: 100,
|
|
42
|
+
delay: 1000
|
|
43
|
+
}, (current, total, village) => {
|
|
44
|
+
console.log(`Sinkronisasi: ${village} (${current}/${total})`);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Antarmuka Baris Perintah (CLI Mode)
|
|
50
|
+
|
|
51
|
+
Gunakan perintah `scrape-pos` untuk menjalankan sinkronisasi data secara langsung melalui terminal:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Menjalankan sinkronisasi default
|
|
55
|
+
npm start
|
|
56
|
+
|
|
57
|
+
# Menjalankan dengan konfigurasi kustom
|
|
58
|
+
npm start -- --input ./data_input.csv --output ./hasil_sinkronisasi.json --limit 500 --delay 2000
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Spesifikasi Data Output
|
|
62
|
+
|
|
63
|
+
Hasil integrasi akan disajikan dalam format JSON standar industri:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"code": "11.01.01.2001",
|
|
68
|
+
"name": "Keude Bakongan",
|
|
69
|
+
"districtCode": "11.01.01",
|
|
70
|
+
"type": "DESA",
|
|
71
|
+
"provinceName": "Aceh",
|
|
72
|
+
"regencyName": "Kab. Aceh Selatan",
|
|
73
|
+
"districtName": "Bakongan",
|
|
74
|
+
"postalCode": "23773"
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Keamanan & Kepatuhan
|
|
79
|
+
|
|
80
|
+
Kami sangat menghargai integritas data dan keberlangsungan layanan. Pengguna diharapkan untuk:
|
|
81
|
+
1. Mematuhi kebijakan penggunaan data wilayah yang berlaku.
|
|
82
|
+
2. Menggunakan jeda waktu (`delay`) yang wajar dalam pemrosesan masal untuk menjaga stabilitas layanan.
|
|
83
|
+
|
|
84
|
+
## Lisensi
|
|
85
|
+
|
|
86
|
+
Didistribusikan di bawah lisensi [MIT](LICENSE).
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
*Dikembangkan secara profesional untuk mendukung digitalisasi data wilayah Indonesia.*
|
package/lib/cjs/api.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sends a POST request to Pos Indonesia CariKodepos endpoint.
|
|
3
|
+
* @param keyword - The search keyword (village name or postal code)
|
|
4
|
+
* @param cookie - Optional session cookie
|
|
5
|
+
* @returns The raw HTML response
|
|
6
|
+
*/
|
|
7
|
+
export declare function fetchPostalCodeHtml(keyword: string, cookie?: string): Promise<string>;
|
|
8
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAqB3F"}
|
package/lib/cjs/api.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.fetchPostalCodeHtml = fetchPostalCodeHtml;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const qs_1 = __importDefault(require("qs"));
|
|
9
|
+
/**
|
|
10
|
+
* Sends a POST request to Pos Indonesia CariKodepos endpoint.
|
|
11
|
+
* @param keyword - The search keyword (village name or postal code)
|
|
12
|
+
* @param cookie - Optional session cookie
|
|
13
|
+
* @returns The raw HTML response
|
|
14
|
+
*/
|
|
15
|
+
async function fetchPostalCodeHtml(keyword, cookie) {
|
|
16
|
+
const URL = 'https://kodepos.posindonesia.co.id/CariKodepos';
|
|
17
|
+
const payload = qs_1.default.stringify({ kodepos: keyword });
|
|
18
|
+
try {
|
|
19
|
+
const response = await axios_1.default.post(URL, payload, {
|
|
20
|
+
headers: {
|
|
21
|
+
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
|
|
22
|
+
'accept-language': 'en-US,en;q=0.9,id;q=0.8,ms;q=0.7',
|
|
23
|
+
'content-type': 'application/x-www-form-urlencoded',
|
|
24
|
+
'origin': 'https://kodepos.posindonesia.co.id',
|
|
25
|
+
'referer': 'https://kodepos.posindonesia.co.id/CariKodepos',
|
|
26
|
+
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36',
|
|
27
|
+
'cookie': cookie || 'ci_session=siodf5sn3081n9fb1h3pfh7k8r92sjvt; TS011d97f9=01dc40192af9d2c68e0588cf6826f2541733c6f742d0e6382757bb95d8a2f8d27f6da94b22391892939703ae744a8f47fe7d578583'
|
|
28
|
+
},
|
|
29
|
+
timeout: 15000
|
|
30
|
+
});
|
|
31
|
+
return response.data;
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
throw new Error(`Failed to fetch data for ${keyword}: ${error.message}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":";;;;;AASA,kDAqBC;AA9BD,kDAA0B;AAC1B,4CAAoB;AAEpB;;;;;GAKG;AACI,KAAK,UAAU,mBAAmB,CAAC,OAAe,EAAE,MAAe;IACtE,MAAM,GAAG,GAAG,gDAAgD,CAAC;IAC7D,MAAM,OAAO,GAAG,YAAE,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IAEnD,IAAI,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,eAAK,CAAC,IAAI,CAAS,GAAG,EAAE,OAAO,EAAE;YACpD,OAAO,EAAE;gBACL,QAAQ,EAAE,yIAAyI;gBACnJ,iBAAiB,EAAE,kCAAkC;gBACrD,cAAc,EAAE,mCAAmC;gBACnD,QAAQ,EAAE,oCAAoC;gBAC9C,SAAS,EAAE,gDAAgD;gBAC3D,YAAY,EAAE,uHAAuH;gBACrI,QAAQ,EAAE,MAAM,IAAI,oKAAoK;aAC3L;YACD,OAAO,EAAE,KAAK;SACjB,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC;IACzB,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,4BAA4B,OAAO,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7E,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface ScraperOptions {
|
|
2
|
+
input: string;
|
|
3
|
+
limit: number;
|
|
4
|
+
delay: number;
|
|
5
|
+
cookie?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ScrapedVillage {
|
|
8
|
+
code: string;
|
|
9
|
+
name: string;
|
|
10
|
+
districtCode: string;
|
|
11
|
+
type: string;
|
|
12
|
+
provinceName: string;
|
|
13
|
+
regencyName: string;
|
|
14
|
+
districtName: string;
|
|
15
|
+
postalCode: string;
|
|
16
|
+
}
|
|
17
|
+
export type ProgressCallback = (current: number, total: number, village: string) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Core scraping logic separated from CLI.
|
|
20
|
+
* @param options - Configuration options
|
|
21
|
+
* @param onProgress - Callback for progress updates
|
|
22
|
+
* @returns Scraped results
|
|
23
|
+
*/
|
|
24
|
+
export declare function runScraper(options: ScraperOptions, onProgress?: ProgressCallback): Promise<ScrapedVillage[]>;
|
|
25
|
+
//# sourceMappingURL=core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/core.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,cAAc;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAEzF;;;;;GAKG;AACH,wBAAsB,UAAU,CAC5B,OAAO,EAAE,cAAc,EACvB,UAAU,GAAE,gBAA2B,GACxC,OAAO,CAAC,cAAc,EAAE,CAAC,CAkD3B"}
|
package/lib/cjs/core.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runScraper = runScraper;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const api_1 = require("./api");
|
|
39
|
+
const parser_1 = require("./parser");
|
|
40
|
+
const utils_1 = require("./utils");
|
|
41
|
+
/**
|
|
42
|
+
* Core scraping logic separated from CLI.
|
|
43
|
+
* @param options - Configuration options
|
|
44
|
+
* @param onProgress - Callback for progress updates
|
|
45
|
+
* @returns Scraped results
|
|
46
|
+
*/
|
|
47
|
+
async function runScraper(options, onProgress = () => { }) {
|
|
48
|
+
const { input, limit, delay, cookie } = options;
|
|
49
|
+
if (!fs.existsSync(input)) {
|
|
50
|
+
throw new Error(`Input file not found at ${input}`);
|
|
51
|
+
}
|
|
52
|
+
const csvData = fs.readFileSync(input, 'utf-8');
|
|
53
|
+
const lines = csvData.split('\n').filter(line => line.trim() !== '');
|
|
54
|
+
const rows = lines.slice(1);
|
|
55
|
+
const totalToProcess = Math.min(rows.length, limit);
|
|
56
|
+
const scrapedResults = [];
|
|
57
|
+
for (let i = 0; i < totalToProcess; i++) {
|
|
58
|
+
const values = rows[i].split(',');
|
|
59
|
+
const code = values[0];
|
|
60
|
+
const name = values[1];
|
|
61
|
+
const prov = values[2];
|
|
62
|
+
const kab = values[3];
|
|
63
|
+
const kec = values[4];
|
|
64
|
+
onProgress(i, totalToProcess, name);
|
|
65
|
+
try {
|
|
66
|
+
const html = await (0, api_1.fetchPostalCodeHtml)(name, cookie);
|
|
67
|
+
const results = (0, parser_1.parsePostalCodeTable)(html);
|
|
68
|
+
const bestMatch = (0, utils_1.findBestMatch)(results, name, kec);
|
|
69
|
+
scrapedResults.push({
|
|
70
|
+
code: (0, utils_1.formatCode)(code),
|
|
71
|
+
name: name,
|
|
72
|
+
districtCode: (0, utils_1.formatCode)(code.substring(0, 6)),
|
|
73
|
+
type: "DESA",
|
|
74
|
+
provinceName: prov,
|
|
75
|
+
regencyName: kab,
|
|
76
|
+
districtName: kec,
|
|
77
|
+
postalCode: bestMatch ? bestMatch.kodepos : ""
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.error(`\n⚠️ Failed to scrape ${name}: ${error.message}`);
|
|
82
|
+
}
|
|
83
|
+
if (i < totalToProcess - 1) {
|
|
84
|
+
await new Promise(r => setTimeout(r, delay));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
onProgress(totalToProcess, totalToProcess, 'Completed!');
|
|
88
|
+
return scrapedResults;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../src/core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":""}
|
package/lib/cjs/index.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
const commander_1 = require("commander");
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
44
|
+
const cliProgress = __importStar(require("cli-progress"));
|
|
45
|
+
const core_1 = require("./core");
|
|
46
|
+
commander_1.program
|
|
47
|
+
.name('@damarkuncoro/posindonesia')
|
|
48
|
+
.description('Professional Indonesian Postal Code Scraper')
|
|
49
|
+
.version('1.0.0')
|
|
50
|
+
.option('-i, --input <path>', 'Input CSV file path', path.join(__dirname, '../../../docs/database_final.csv'))
|
|
51
|
+
.option('-o, --output <path>', 'Output JSON file path', path.join(__dirname, '../results/scraped_villages_detailed.json'))
|
|
52
|
+
.option('-l, --limit <number>', 'Number of rows to process', '10')
|
|
53
|
+
.option('-d, --delay <ms>', 'Delay between requests in milliseconds', '1000')
|
|
54
|
+
.option('-c, --cookie <string>', 'Custom session cookie for Pos Indonesia site')
|
|
55
|
+
.action(async (options) => {
|
|
56
|
+
const { output, limit, delay } = options;
|
|
57
|
+
console.log(chalk_1.default.blue.bold('\n🚀 Starting Indonesian Postal Code Scraper (TypeScript)\n'));
|
|
58
|
+
const progressBar = new cliProgress.SingleBar({
|
|
59
|
+
format: chalk_1.default.green('Progress |') + chalk_1.default.cyan('{bar}') + '| {percentage}% || {value}/{total} Villages || {village}',
|
|
60
|
+
barCompleteChar: '\u2588',
|
|
61
|
+
barIncompleteChar: '\u2591',
|
|
62
|
+
hideCursor: true
|
|
63
|
+
});
|
|
64
|
+
try {
|
|
65
|
+
const results = await (0, core_1.runScraper)({
|
|
66
|
+
...options,
|
|
67
|
+
limit: parseInt(limit),
|
|
68
|
+
delay: parseInt(delay)
|
|
69
|
+
}, (current, total, village) => {
|
|
70
|
+
if (current === 0) {
|
|
71
|
+
progressBar.start(total, 0, { village });
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
progressBar.update(current, { village });
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
progressBar.stop();
|
|
78
|
+
// Ensure output directory exists
|
|
79
|
+
const outputDir = path.dirname(output);
|
|
80
|
+
if (!fs.existsSync(outputDir)) {
|
|
81
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
82
|
+
}
|
|
83
|
+
fs.writeFileSync(output, JSON.stringify(results, null, 2));
|
|
84
|
+
console.log(chalk_1.default.green.bold(`\n✅ Success! Scraped data saved to:`));
|
|
85
|
+
console.log(chalk_1.default.underline(output));
|
|
86
|
+
console.log('\n');
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
if (progressBar.isActive)
|
|
90
|
+
progressBar.stop();
|
|
91
|
+
console.error(chalk_1.default.red(`\n❌ Error: ${error.message}`));
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
commander_1.program.parse(process.argv);
|
|
96
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAG9B,QAAA,MAAM,MAAM,gBAeV,CAAC;AAEH,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const winston_1 = __importDefault(require("winston"));
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const logger = winston_1.default.createLogger({
|
|
9
|
+
level: 'info',
|
|
10
|
+
format: winston_1.default.format.combine(winston_1.default.format.timestamp(), winston_1.default.format.json()),
|
|
11
|
+
transports: [
|
|
12
|
+
new winston_1.default.transports.File({
|
|
13
|
+
filename: path_1.default.join(__dirname, '../results/error.log'),
|
|
14
|
+
level: 'error'
|
|
15
|
+
}),
|
|
16
|
+
new winston_1.default.transports.File({
|
|
17
|
+
filename: path_1.default.join(__dirname, '../results/combined.log')
|
|
18
|
+
}),
|
|
19
|
+
],
|
|
20
|
+
});
|
|
21
|
+
exports.default = logger;
|
|
22
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":";;;;;AAAA,sDAA8B;AAC9B,gDAAwB;AAExB,MAAM,MAAM,GAAG,iBAAO,CAAC,YAAY,CAAC;IAClC,KAAK,EAAE,MAAM;IACb,MAAM,EAAE,iBAAO,CAAC,MAAM,CAAC,OAAO,CAC5B,iBAAO,CAAC,MAAM,CAAC,SAAS,EAAE,EAC1B,iBAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CACtB;IACD,UAAU,EAAE;QACV,IAAI,iBAAO,CAAC,UAAU,CAAC,IAAI,CAAC;YAC1B,QAAQ,EAAE,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,sBAAsB,CAAC;YACtD,KAAK,EAAE,OAAO;SACf,CAAC;QACF,IAAI,iBAAO,CAAC,UAAU,CAAC,IAAI,CAAC;YAC1B,QAAQ,EAAE,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,yBAAyB,CAAC;SAC1D,CAAC;KACH;CACF,CAAC,CAAC;AAEH,kBAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC"}
|
package/lib/cjs/main.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./core"), exports);
|
|
18
|
+
__exportStar(require("./api"), exports);
|
|
19
|
+
__exportStar(require("./parser"), exports);
|
|
20
|
+
__exportStar(require("./utils"), exports);
|
|
21
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,yCAAuB;AACvB,wCAAsB;AACtB,2CAAyB;AACzB,0CAAwB"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface PostalCodeResult {
|
|
2
|
+
no: string;
|
|
3
|
+
kodepos: string;
|
|
4
|
+
desa_kelurahan: string;
|
|
5
|
+
kecamatan: string;
|
|
6
|
+
kabupaten_kota: string;
|
|
7
|
+
provinsi: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Parses the raw HTML response from Pos Indonesia CariKodepos.
|
|
11
|
+
* @param html - The raw HTML content
|
|
12
|
+
* @returns List of parsed postal code objects
|
|
13
|
+
*/
|
|
14
|
+
export declare function parsePostalCodeTable(html: string): PostalCodeResult[];
|
|
15
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/parser.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAmBrE"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.parsePostalCodeTable = parsePostalCodeTable;
|
|
37
|
+
const cheerio = __importStar(require("cheerio"));
|
|
38
|
+
/**
|
|
39
|
+
* Parses the raw HTML response from Pos Indonesia CariKodepos.
|
|
40
|
+
* @param html - The raw HTML content
|
|
41
|
+
* @returns List of parsed postal code objects
|
|
42
|
+
*/
|
|
43
|
+
function parsePostalCodeTable(html) {
|
|
44
|
+
const $ = cheerio.load(html);
|
|
45
|
+
const results = [];
|
|
46
|
+
$('table tbody tr').each((_i, row) => {
|
|
47
|
+
const cols = $(row).find('td');
|
|
48
|
+
if (cols.length >= 6) {
|
|
49
|
+
results.push({
|
|
50
|
+
no: $(cols[0]).text().trim(),
|
|
51
|
+
kodepos: $(cols[1]).text().trim(),
|
|
52
|
+
desa_kelurahan: $(cols[2]).text().trim(),
|
|
53
|
+
kecamatan: $(cols[3]).text().trim(),
|
|
54
|
+
kabupaten_kota: $(cols[4]).text().trim(),
|
|
55
|
+
provinsi: $(cols[5]).text().trim()
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
return results;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/parser.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgBA,oDAmBC;AAnCD,iDAAmC;AAWnC;;;;GAIG;AACH,SAAgB,oBAAoB,CAAC,IAAY;IAC7C,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,OAAO,GAAuB,EAAE,CAAC;IAEvC,CAAC,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE;QACjC,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YACnB,OAAO,CAAC,IAAI,CAAC;gBACT,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;gBAC5B,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;gBACjC,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;gBACxC,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;gBACnC,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;gBACxC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;aACrC,CAAC,CAAC;QACP,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PostalCodeResult } from './parser';
|
|
2
|
+
/**
|
|
3
|
+
* Formats a raw numerical code into standard Indonesian administrative format (PP.KK.CC.DD).
|
|
4
|
+
* @param code - The raw code (e.g., 1101012001)
|
|
5
|
+
* @returns The formatted code (e.g., 11.01.01.2001)
|
|
6
|
+
*/
|
|
7
|
+
export declare function formatCode(code: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Finds the best match in a list of search results using Fuzzy Matching.
|
|
10
|
+
* @param results - The list of parsed results
|
|
11
|
+
* @param villageName - The target village name
|
|
12
|
+
* @param districtName - The target district name
|
|
13
|
+
* @returns The best matching object or null
|
|
14
|
+
*/
|
|
15
|
+
export declare function findBestMatch(results: PostalCodeResult[], villageName: string, districtName: string): PostalCodeResult | null;
|
|
16
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ/C;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CACzB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GACrB,gBAAgB,GAAG,IAAI,CAkCzB"}
|
package/lib/cjs/utils.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.formatCode = formatCode;
|
|
7
|
+
exports.findBestMatch = findBestMatch;
|
|
8
|
+
const fuse_js_1 = __importDefault(require("fuse.js"));
|
|
9
|
+
/**
|
|
10
|
+
* Formats a raw numerical code into standard Indonesian administrative format (PP.KK.CC.DD).
|
|
11
|
+
* @param code - The raw code (e.g., 1101012001)
|
|
12
|
+
* @returns The formatted code (e.g., 11.01.01.2001)
|
|
13
|
+
*/
|
|
14
|
+
function formatCode(code) {
|
|
15
|
+
if (!code)
|
|
16
|
+
return '';
|
|
17
|
+
const cleanCode = code.replace(/\./g, '');
|
|
18
|
+
const p1 = cleanCode.substring(0, 2);
|
|
19
|
+
const p2 = cleanCode.substring(2, 4);
|
|
20
|
+
const p3 = cleanCode.substring(4, 6);
|
|
21
|
+
const p4 = cleanCode.substring(6);
|
|
22
|
+
return [p1, p2, p3, p4].filter(Boolean).join('.');
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Finds the best match in a list of search results using Fuzzy Matching.
|
|
26
|
+
* @param results - The list of parsed results
|
|
27
|
+
* @param villageName - The target village name
|
|
28
|
+
* @param districtName - The target district name
|
|
29
|
+
* @returns The best matching object or null
|
|
30
|
+
*/
|
|
31
|
+
function findBestMatch(results, villageName, districtName) {
|
|
32
|
+
if (!results || results.length === 0)
|
|
33
|
+
return null;
|
|
34
|
+
// 1. Try Exact Match First
|
|
35
|
+
const exactMatch = results.find(r => r.desa_kelurahan.toLowerCase() === villageName.toLowerCase() &&
|
|
36
|
+
r.kecamatan.toLowerCase() === districtName.toLowerCase());
|
|
37
|
+
if (exactMatch)
|
|
38
|
+
return exactMatch;
|
|
39
|
+
// 2. Fuzzy Match using Fuse.js
|
|
40
|
+
const fuseOptions = {
|
|
41
|
+
keys: [
|
|
42
|
+
{ name: 'desa_kelurahan', weight: 0.7 },
|
|
43
|
+
{ name: 'kecamatan', weight: 0.3 }
|
|
44
|
+
],
|
|
45
|
+
threshold: 0.4, // Adjust for strictness (0.0 perfect, 1.0 match anything)
|
|
46
|
+
includeScore: true
|
|
47
|
+
};
|
|
48
|
+
const fuse = new fuse_js_1.default(results, fuseOptions);
|
|
49
|
+
const fuzzyResults = fuse.search(`${villageName} ${districtName}`);
|
|
50
|
+
if (fuzzyResults.length > 0) {
|
|
51
|
+
return fuzzyResults[0].item;
|
|
52
|
+
}
|
|
53
|
+
// 3. Fallback to simple contains logic
|
|
54
|
+
return results.find(r => (r.desa_kelurahan.toLowerCase().includes(villageName.toLowerCase()) ||
|
|
55
|
+
villageName.toLowerCase().includes(r.desa_kelurahan.toLowerCase())) &&
|
|
56
|
+
(r.kecamatan.toLowerCase().includes(districtName.toLowerCase()) ||
|
|
57
|
+
districtName.toLowerCase().includes(r.kecamatan.toLowerCase()))) || results[0];
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":";;;;;AAQA,gCAQC;AASD,sCAsCC;AA/DD,sDAA2B;AAG3B;;;;GAIG;AACH,SAAgB,UAAU,CAAC,IAAY;IACnC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC1C,MAAM,EAAE,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrC,MAAM,EAAE,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrC,MAAM,EAAE,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrC,MAAM,EAAE,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,aAAa,CACzB,OAA2B,EAC3B,WAAmB,EACnB,YAAoB;IAEpB,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAElD,2BAA2B;IAC3B,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAChC,CAAC,CAAC,cAAc,CAAC,WAAW,EAAE,KAAK,WAAW,CAAC,WAAW,EAAE;QAC5D,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,YAAY,CAAC,WAAW,EAAE,CAC3D,CAAC;IACF,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IAElC,+BAA+B;IAC/B,MAAM,WAAW,GAAG;QAChB,IAAI,EAAE;YACF,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,GAAG,EAAE;YACvC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE;SACrC;QACD,SAAS,EAAE,GAAG,EAAE,0DAA0D;QAC1E,YAAY,EAAE,IAAI;KACrB,CAAC;IAEF,MAAM,IAAI,GAAG,IAAI,iBAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC5C,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,WAAW,IAAI,YAAY,EAAE,CAAC,CAAC;IAEnE,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChC,CAAC;IAED,uCAAuC;IACvC,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CACpB,CAAC,CAAC,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC;QAClE,WAAW,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC;YAC9D,YAAY,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC,CACnE,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC"}
|
package/lib/esm/api.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sends a POST request to Pos Indonesia CariKodepos endpoint.
|
|
3
|
+
* @param keyword - The search keyword (village name or postal code)
|
|
4
|
+
* @param cookie - Optional session cookie
|
|
5
|
+
* @returns The raw HTML response
|
|
6
|
+
*/
|
|
7
|
+
export declare function fetchPostalCodeHtml(keyword: string, cookie?: string): Promise<string>;
|
|
8
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAqB3F"}
|
package/lib/esm/api.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import qs from 'qs';
|
|
3
|
+
/**
|
|
4
|
+
* Sends a POST request to Pos Indonesia CariKodepos endpoint.
|
|
5
|
+
* @param keyword - The search keyword (village name or postal code)
|
|
6
|
+
* @param cookie - Optional session cookie
|
|
7
|
+
* @returns The raw HTML response
|
|
8
|
+
*/
|
|
9
|
+
export async function fetchPostalCodeHtml(keyword, cookie) {
|
|
10
|
+
const URL = 'https://kodepos.posindonesia.co.id/CariKodepos';
|
|
11
|
+
const payload = qs.stringify({ kodepos: keyword });
|
|
12
|
+
try {
|
|
13
|
+
const response = await axios.post(URL, payload, {
|
|
14
|
+
headers: {
|
|
15
|
+
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
|
|
16
|
+
'accept-language': 'en-US,en;q=0.9,id;q=0.8,ms;q=0.7',
|
|
17
|
+
'content-type': 'application/x-www-form-urlencoded',
|
|
18
|
+
'origin': 'https://kodepos.posindonesia.co.id',
|
|
19
|
+
'referer': 'https://kodepos.posindonesia.co.id/CariKodepos',
|
|
20
|
+
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36',
|
|
21
|
+
'cookie': cookie || 'ci_session=siodf5sn3081n9fb1h3pfh7k8r92sjvt; TS011d97f9=01dc40192af9d2c68e0588cf6826f2541733c6f742d0e6382757bb95d8a2f8d27f6da94b22391892939703ae744a8f47fe7d578583'
|
|
22
|
+
},
|
|
23
|
+
timeout: 15000
|
|
24
|
+
});
|
|
25
|
+
return response.data;
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
throw new Error(`Failed to fetch data for ${keyword}: ${error.message}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,OAAe,EAAE,MAAe;IACtE,MAAM,GAAG,GAAG,gDAAgD,CAAC;IAC7D,MAAM,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IAEnD,IAAI,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAS,GAAG,EAAE,OAAO,EAAE;YACpD,OAAO,EAAE;gBACL,QAAQ,EAAE,yIAAyI;gBACnJ,iBAAiB,EAAE,kCAAkC;gBACrD,cAAc,EAAE,mCAAmC;gBACnD,QAAQ,EAAE,oCAAoC;gBAC9C,SAAS,EAAE,gDAAgD;gBAC3D,YAAY,EAAE,uHAAuH;gBACrI,QAAQ,EAAE,MAAM,IAAI,oKAAoK;aAC3L;YACD,OAAO,EAAE,KAAK;SACjB,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC;IACzB,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,4BAA4B,OAAO,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7E,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface ScraperOptions {
|
|
2
|
+
input: string;
|
|
3
|
+
limit: number;
|
|
4
|
+
delay: number;
|
|
5
|
+
cookie?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ScrapedVillage {
|
|
8
|
+
code: string;
|
|
9
|
+
name: string;
|
|
10
|
+
districtCode: string;
|
|
11
|
+
type: string;
|
|
12
|
+
provinceName: string;
|
|
13
|
+
regencyName: string;
|
|
14
|
+
districtName: string;
|
|
15
|
+
postalCode: string;
|
|
16
|
+
}
|
|
17
|
+
export type ProgressCallback = (current: number, total: number, village: string) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Core scraping logic separated from CLI.
|
|
20
|
+
* @param options - Configuration options
|
|
21
|
+
* @param onProgress - Callback for progress updates
|
|
22
|
+
* @returns Scraped results
|
|
23
|
+
*/
|
|
24
|
+
export declare function runScraper(options: ScraperOptions, onProgress?: ProgressCallback): Promise<ScrapedVillage[]>;
|
|
25
|
+
//# sourceMappingURL=core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/core.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,cAAc;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAEzF;;;;;GAKG;AACH,wBAAsB,UAAU,CAC5B,OAAO,EAAE,cAAc,EACvB,UAAU,GAAE,gBAA2B,GACxC,OAAO,CAAC,cAAc,EAAE,CAAC,CAkD3B"}
|
package/lib/esm/core.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import { fetchPostalCodeHtml } from './api';
|
|
3
|
+
import { parsePostalCodeTable } from './parser';
|
|
4
|
+
import { formatCode, findBestMatch } from './utils';
|
|
5
|
+
/**
|
|
6
|
+
* Core scraping logic separated from CLI.
|
|
7
|
+
* @param options - Configuration options
|
|
8
|
+
* @param onProgress - Callback for progress updates
|
|
9
|
+
* @returns Scraped results
|
|
10
|
+
*/
|
|
11
|
+
export async function runScraper(options, onProgress = () => { }) {
|
|
12
|
+
const { input, limit, delay, cookie } = options;
|
|
13
|
+
if (!fs.existsSync(input)) {
|
|
14
|
+
throw new Error(`Input file not found at ${input}`);
|
|
15
|
+
}
|
|
16
|
+
const csvData = fs.readFileSync(input, 'utf-8');
|
|
17
|
+
const lines = csvData.split('\n').filter(line => line.trim() !== '');
|
|
18
|
+
const rows = lines.slice(1);
|
|
19
|
+
const totalToProcess = Math.min(rows.length, limit);
|
|
20
|
+
const scrapedResults = [];
|
|
21
|
+
for (let i = 0; i < totalToProcess; i++) {
|
|
22
|
+
const values = rows[i].split(',');
|
|
23
|
+
const code = values[0];
|
|
24
|
+
const name = values[1];
|
|
25
|
+
const prov = values[2];
|
|
26
|
+
const kab = values[3];
|
|
27
|
+
const kec = values[4];
|
|
28
|
+
onProgress(i, totalToProcess, name);
|
|
29
|
+
try {
|
|
30
|
+
const html = await fetchPostalCodeHtml(name, cookie);
|
|
31
|
+
const results = parsePostalCodeTable(html);
|
|
32
|
+
const bestMatch = findBestMatch(results, name, kec);
|
|
33
|
+
scrapedResults.push({
|
|
34
|
+
code: formatCode(code),
|
|
35
|
+
name: name,
|
|
36
|
+
districtCode: formatCode(code.substring(0, 6)),
|
|
37
|
+
type: "DESA",
|
|
38
|
+
provinceName: prov,
|
|
39
|
+
regencyName: kab,
|
|
40
|
+
districtName: kec,
|
|
41
|
+
postalCode: bestMatch ? bestMatch.kodepos : ""
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error(`\n⚠️ Failed to scrape ${name}: ${error.message}`);
|
|
46
|
+
}
|
|
47
|
+
if (i < totalToProcess - 1) {
|
|
48
|
+
await new Promise(r => setTimeout(r, delay));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
onProgress(totalToProcess, totalToProcess, 'Completed!');
|
|
52
|
+
return scrapedResults;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../src/core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":""}
|
package/lib/esm/index.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { program } from 'commander';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import * as cliProgress from 'cli-progress';
|
|
7
|
+
import { runScraper } from './core';
|
|
8
|
+
program
|
|
9
|
+
.name('@damarkuncoro/posindonesia')
|
|
10
|
+
.description('Professional Indonesian Postal Code Scraper')
|
|
11
|
+
.version('1.0.0')
|
|
12
|
+
.option('-i, --input <path>', 'Input CSV file path', path.join(__dirname, '../../../docs/database_final.csv'))
|
|
13
|
+
.option('-o, --output <path>', 'Output JSON file path', path.join(__dirname, '../results/scraped_villages_detailed.json'))
|
|
14
|
+
.option('-l, --limit <number>', 'Number of rows to process', '10')
|
|
15
|
+
.option('-d, --delay <ms>', 'Delay between requests in milliseconds', '1000')
|
|
16
|
+
.option('-c, --cookie <string>', 'Custom session cookie for Pos Indonesia site')
|
|
17
|
+
.action(async (options) => {
|
|
18
|
+
const { output, limit, delay } = options;
|
|
19
|
+
console.log(chalk.blue.bold('\n🚀 Starting Indonesian Postal Code Scraper (TypeScript)\n'));
|
|
20
|
+
const progressBar = new cliProgress.SingleBar({
|
|
21
|
+
format: chalk.green('Progress |') + chalk.cyan('{bar}') + '| {percentage}% || {value}/{total} Villages || {village}',
|
|
22
|
+
barCompleteChar: '\u2588',
|
|
23
|
+
barIncompleteChar: '\u2591',
|
|
24
|
+
hideCursor: true
|
|
25
|
+
});
|
|
26
|
+
try {
|
|
27
|
+
const results = await runScraper({
|
|
28
|
+
...options,
|
|
29
|
+
limit: parseInt(limit),
|
|
30
|
+
delay: parseInt(delay)
|
|
31
|
+
}, (current, total, village) => {
|
|
32
|
+
if (current === 0) {
|
|
33
|
+
progressBar.start(total, 0, { village });
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
progressBar.update(current, { village });
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
progressBar.stop();
|
|
40
|
+
// Ensure output directory exists
|
|
41
|
+
const outputDir = path.dirname(output);
|
|
42
|
+
if (!fs.existsSync(outputDir)) {
|
|
43
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
fs.writeFileSync(output, JSON.stringify(results, null, 2));
|
|
46
|
+
console.log(chalk.green.bold(`\n✅ Success! Scraped data saved to:`));
|
|
47
|
+
console.log(chalk.underline(output));
|
|
48
|
+
console.log('\n');
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (progressBar.isActive)
|
|
52
|
+
progressBar.stop();
|
|
53
|
+
console.error(chalk.red(`\n❌ Error: ${error.message}`));
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
program.parse(process.argv);
|
|
58
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAG9B,QAAA,MAAM,MAAM,gBAeV,CAAC;AAEH,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import winston from 'winston';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
const logger = winston.createLogger({
|
|
4
|
+
level: 'info',
|
|
5
|
+
format: winston.format.combine(winston.format.timestamp(), winston.format.json()),
|
|
6
|
+
transports: [
|
|
7
|
+
new winston.transports.File({
|
|
8
|
+
filename: path.join(__dirname, '../results/error.log'),
|
|
9
|
+
level: 'error'
|
|
10
|
+
}),
|
|
11
|
+
new winston.transports.File({
|
|
12
|
+
filename: path.join(__dirname, '../results/combined.log')
|
|
13
|
+
}),
|
|
14
|
+
],
|
|
15
|
+
});
|
|
16
|
+
export default logger;
|
|
17
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAClC,KAAK,EAAE,MAAM;IACb,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAC5B,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,EAC1B,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CACtB;IACD,UAAU,EAAE;QACV,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC;YAC1B,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,sBAAsB,CAAC;YACtD,KAAK,EAAE,OAAO;SACf,CAAC;QACF,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC;YAC1B,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,yBAAyB,CAAC;SAC1D,CAAC;KACH;CACF,CAAC,CAAC;AAEH,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC"}
|
package/lib/esm/main.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface PostalCodeResult {
|
|
2
|
+
no: string;
|
|
3
|
+
kodepos: string;
|
|
4
|
+
desa_kelurahan: string;
|
|
5
|
+
kecamatan: string;
|
|
6
|
+
kabupaten_kota: string;
|
|
7
|
+
provinsi: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Parses the raw HTML response from Pos Indonesia CariKodepos.
|
|
11
|
+
* @param html - The raw HTML content
|
|
12
|
+
* @returns List of parsed postal code objects
|
|
13
|
+
*/
|
|
14
|
+
export declare function parsePostalCodeTable(html: string): PostalCodeResult[];
|
|
15
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/parser.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAmBrE"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as cheerio from 'cheerio';
|
|
2
|
+
/**
|
|
3
|
+
* Parses the raw HTML response from Pos Indonesia CariKodepos.
|
|
4
|
+
* @param html - The raw HTML content
|
|
5
|
+
* @returns List of parsed postal code objects
|
|
6
|
+
*/
|
|
7
|
+
export function parsePostalCodeTable(html) {
|
|
8
|
+
const $ = cheerio.load(html);
|
|
9
|
+
const results = [];
|
|
10
|
+
$('table tbody tr').each((_i, row) => {
|
|
11
|
+
const cols = $(row).find('td');
|
|
12
|
+
if (cols.length >= 6) {
|
|
13
|
+
results.push({
|
|
14
|
+
no: $(cols[0]).text().trim(),
|
|
15
|
+
kodepos: $(cols[1]).text().trim(),
|
|
16
|
+
desa_kelurahan: $(cols[2]).text().trim(),
|
|
17
|
+
kecamatan: $(cols[3]).text().trim(),
|
|
18
|
+
kabupaten_kota: $(cols[4]).text().trim(),
|
|
19
|
+
provinsi: $(cols[5]).text().trim()
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
return results;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAWnC;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC7C,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,OAAO,GAAuB,EAAE,CAAC;IAEvC,CAAC,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE;QACjC,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YACnB,OAAO,CAAC,IAAI,CAAC;gBACT,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;gBAC5B,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;gBACjC,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;gBACxC,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;gBACnC,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;gBACxC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;aACrC,CAAC,CAAC;QACP,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PostalCodeResult } from './parser';
|
|
2
|
+
/**
|
|
3
|
+
* Formats a raw numerical code into standard Indonesian administrative format (PP.KK.CC.DD).
|
|
4
|
+
* @param code - The raw code (e.g., 1101012001)
|
|
5
|
+
* @returns The formatted code (e.g., 11.01.01.2001)
|
|
6
|
+
*/
|
|
7
|
+
export declare function formatCode(code: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Finds the best match in a list of search results using Fuzzy Matching.
|
|
10
|
+
* @param results - The list of parsed results
|
|
11
|
+
* @param villageName - The target village name
|
|
12
|
+
* @param districtName - The target district name
|
|
13
|
+
* @returns The best matching object or null
|
|
14
|
+
*/
|
|
15
|
+
export declare function findBestMatch(results: PostalCodeResult[], villageName: string, districtName: string): PostalCodeResult | null;
|
|
16
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ/C;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CACzB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GACrB,gBAAgB,GAAG,IAAI,CAkCzB"}
|
package/lib/esm/utils.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import Fuse from 'fuse.js';
|
|
2
|
+
/**
|
|
3
|
+
* Formats a raw numerical code into standard Indonesian administrative format (PP.KK.CC.DD).
|
|
4
|
+
* @param code - The raw code (e.g., 1101012001)
|
|
5
|
+
* @returns The formatted code (e.g., 11.01.01.2001)
|
|
6
|
+
*/
|
|
7
|
+
export function formatCode(code) {
|
|
8
|
+
if (!code)
|
|
9
|
+
return '';
|
|
10
|
+
const cleanCode = code.replace(/\./g, '');
|
|
11
|
+
const p1 = cleanCode.substring(0, 2);
|
|
12
|
+
const p2 = cleanCode.substring(2, 4);
|
|
13
|
+
const p3 = cleanCode.substring(4, 6);
|
|
14
|
+
const p4 = cleanCode.substring(6);
|
|
15
|
+
return [p1, p2, p3, p4].filter(Boolean).join('.');
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Finds the best match in a list of search results using Fuzzy Matching.
|
|
19
|
+
* @param results - The list of parsed results
|
|
20
|
+
* @param villageName - The target village name
|
|
21
|
+
* @param districtName - The target district name
|
|
22
|
+
* @returns The best matching object or null
|
|
23
|
+
*/
|
|
24
|
+
export function findBestMatch(results, villageName, districtName) {
|
|
25
|
+
if (!results || results.length === 0)
|
|
26
|
+
return null;
|
|
27
|
+
// 1. Try Exact Match First
|
|
28
|
+
const exactMatch = results.find(r => r.desa_kelurahan.toLowerCase() === villageName.toLowerCase() &&
|
|
29
|
+
r.kecamatan.toLowerCase() === districtName.toLowerCase());
|
|
30
|
+
if (exactMatch)
|
|
31
|
+
return exactMatch;
|
|
32
|
+
// 2. Fuzzy Match using Fuse.js
|
|
33
|
+
const fuseOptions = {
|
|
34
|
+
keys: [
|
|
35
|
+
{ name: 'desa_kelurahan', weight: 0.7 },
|
|
36
|
+
{ name: 'kecamatan', weight: 0.3 }
|
|
37
|
+
],
|
|
38
|
+
threshold: 0.4, // Adjust for strictness (0.0 perfect, 1.0 match anything)
|
|
39
|
+
includeScore: true
|
|
40
|
+
};
|
|
41
|
+
const fuse = new Fuse(results, fuseOptions);
|
|
42
|
+
const fuzzyResults = fuse.search(`${villageName} ${districtName}`);
|
|
43
|
+
if (fuzzyResults.length > 0) {
|
|
44
|
+
return fuzzyResults[0].item;
|
|
45
|
+
}
|
|
46
|
+
// 3. Fallback to simple contains logic
|
|
47
|
+
return results.find(r => (r.desa_kelurahan.toLowerCase().includes(villageName.toLowerCase()) ||
|
|
48
|
+
villageName.toLowerCase().includes(r.desa_kelurahan.toLowerCase())) &&
|
|
49
|
+
(r.kecamatan.toLowerCase().includes(districtName.toLowerCase()) ||
|
|
50
|
+
districtName.toLowerCase().includes(r.kecamatan.toLowerCase()))) || results[0];
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,SAAS,CAAC;AAG3B;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACnC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC1C,MAAM,EAAE,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrC,MAAM,EAAE,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrC,MAAM,EAAE,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrC,MAAM,EAAE,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CACzB,OAA2B,EAC3B,WAAmB,EACnB,YAAoB;IAEpB,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAElD,2BAA2B;IAC3B,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAChC,CAAC,CAAC,cAAc,CAAC,WAAW,EAAE,KAAK,WAAW,CAAC,WAAW,EAAE;QAC5D,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,YAAY,CAAC,WAAW,EAAE,CAC3D,CAAC;IACF,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IAElC,+BAA+B;IAC/B,MAAM,WAAW,GAAG;QAChB,IAAI,EAAE;YACF,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,GAAG,EAAE;YACvC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE;SACrC;QACD,SAAS,EAAE,GAAG,EAAE,0DAA0D;QAC1E,YAAY,EAAE,IAAI;KACrB,CAAC;IAEF,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC5C,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,WAAW,IAAI,YAAY,EAAE,CAAC,CAAC;IAEnE,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChC,CAAC;IAED,uCAAuC;IACvC,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CACpB,CAAC,CAAC,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC;QAClE,WAAW,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC;YAC9D,YAAY,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC,CACnE,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@damarkuncoro/posindonesia",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Indonesian Postal Code Scraper from Pos Indonesia Official Website",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/damarkuncoro/posindonesia.git"
|
|
8
|
+
},
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/damarkuncoro/posindonesia/issues"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/damarkuncoro/posindonesia#readme",
|
|
13
|
+
"main": "lib/cjs/main.js",
|
|
14
|
+
"module": "lib/esm/main.js",
|
|
15
|
+
"types": "lib/cjs/main.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"import": "./lib/esm/main.js",
|
|
19
|
+
"require": "./lib/cjs/main.js",
|
|
20
|
+
"types": "./lib/cjs/main.d.ts"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"bin": {
|
|
24
|
+
"scrape-pos": "lib/cjs/index.js"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"start": "ts-node src/index.ts",
|
|
28
|
+
"build:cjs": "tsc -p tsconfig.cjs.json",
|
|
29
|
+
"build:esm": "tsc -p tsconfig.esm.json",
|
|
30
|
+
"build": "rm -rf lib && npm run build:cjs && npm run build:esm",
|
|
31
|
+
"prepublishOnly": "npm run build && npm test",
|
|
32
|
+
"scrape": "ts-node src/index.ts",
|
|
33
|
+
"test": "jest"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"indonesia",
|
|
37
|
+
"postal-code",
|
|
38
|
+
"kodepos",
|
|
39
|
+
"scraper",
|
|
40
|
+
"pos-indonesia",
|
|
41
|
+
"typescript"
|
|
42
|
+
],
|
|
43
|
+
"author": "damarkuncoro",
|
|
44
|
+
"files": [
|
|
45
|
+
"lib",
|
|
46
|
+
"README.md",
|
|
47
|
+
"LICENSE"
|
|
48
|
+
],
|
|
49
|
+
"license": "MIT",
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"axios": "^1.13.6",
|
|
55
|
+
"chalk": "^4.1.2",
|
|
56
|
+
"cheerio": "^1.2.0",
|
|
57
|
+
"cli-progress": "^3.12.0",
|
|
58
|
+
"commander": "^14.0.3",
|
|
59
|
+
"puppeteer": "^24.39.0",
|
|
60
|
+
"qs": "^6.15.0",
|
|
61
|
+
"fuse.js": "^7.1.0",
|
|
62
|
+
"winston": "^3.19.0"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@types/axios": "^0.9.36",
|
|
66
|
+
"@types/chalk": "^0.4.31",
|
|
67
|
+
"@types/cheerio": "^0.22.35",
|
|
68
|
+
"@types/cli-progress": "^3.11.6",
|
|
69
|
+
"@types/jest": "^30.0.0",
|
|
70
|
+
"@types/node": "^25.5.0",
|
|
71
|
+
"@types/qs": "^6.15.0",
|
|
72
|
+
"jest": "^30.3.0",
|
|
73
|
+
"ts-jest": "^29.4.6",
|
|
74
|
+
"ts-node": "^10.9.2",
|
|
75
|
+
"typescript": "^5.9.3"
|
|
76
|
+
}
|
|
77
|
+
}
|