8
Validator Data lewat check.ts
src/check.ts adalah validator yang berdiri di depan data kategori. Skrip ini dijalankan lewat perintah bun check, yang memanggil bun run src/check.ts package.json:36-421. Kontributor diminta menjalankannya sebelum mengajukan pull request CONTRIBUTING.md:25-272. Karena tidak ada fungsi main, seluruh body skrip berjalan langsung sebagai top-level for await di atas berkas hasil getFiles(), dengan isBlacklisted dan tipe CategoryType sebagai dependensi src/check.ts:1-73.
Cakupan berkas yang diperiksa
getFiles() menelusuri direktori data/ secara rekursif dan hanya mengumpulkan berkas berekstensi .json src/utils/index.ts:13-334. Cara kerja penelusuran itu, termasuk resolve() dan aturan rekursinya, dijelaskan di Helper Berkas dan Filter Blacklist. Setiap berkas yang kembali lalu dibaca dengan Bun.file(file).json() dan diperlakukan sebagai CategoryType src/check.ts:9-105. Artinya satu putaran validasi berjalan per berkas kategori, bukan per kategori: data/music/id.json dan data/music/en.json diperiksa terpisah. Pasangan berkas bahasa itu sendiri dibahas di Dukungan Multi-Bahasa.
Tipe ItemType mendeklarasikan documentationUrl: string sebagai field wajib src/types.ts:1-86, tetapi tipe TypeScript tidak menjaga data JSON saat runtime. Karena itulah check.ts tetap memeriksa keberadaan field tersebut secara eksplisit.
Satu putaran validasi
Diagram berikut mengikuti satu putaran penuh untuk satu berkas kategori: dua pass pemeriksaan, pembersihan fragment, lalu fetch ke documentationUrl.
sequenceDiagram
autonumber
participant CLI as bun check
participant CHK as src check.ts
participant GF as getFiles
participant DAT as data json
participant BL as isBlacklisted
participant WEB as documentationUrl
CLI->>CHK: bun run src/check.ts
CHK->>GF: getFiles
GF-->>CHK: daftar path berkas json
loop setiap berkas kategori
CHK->>DAT: Bun.file.json
DAT-->>CHK: CategoryType
Note over CHK: pass 1 - Set seen per berkas
alt url sudah ada di seen
CHK-->>CLI: console.error Duplikat lalu process.exit 1
else url baru
CHK->>CHK: seen.add
end
Note over CHK: pass 2 - documentationUrl wajib
alt documentationUrl kosong
CHK-->>CLI: documentationUrl is required.
else documentationUrl ada
CHK->>CHK: buang fragment anchor
alt url sudah ada di crawled
CHK->>CHK: log cache lalu continue
else url belum di-crawl
CHK->>WEB: fetch dengan timeout 10_000 ms
WEB-->>CHK: body teks
CHK->>BL: isBlacklisted body
BL-->>CHK: boolean
alt body memuat kata blacklist
CHK-->>CLI: process.exit 1
else body bersih
CHK->>CHK: crawled.push dan log status
end
opt error jaringan atau timeout
CHK->>CHK: catch kosong tanpa log
end
end
end
end
CHK-->>CLI: selesai tanpa process.exit
%% fig: Satu putaran validasi di check.ts
%% desc: Tiap berkas kategori melewati pass duplikat, pass field wajib, pembersihan fragment, lalu fetch dan pemeriksaan blacklist.Putaran pertama: penolakan URL duplikat per kategori
Pass pertama membuat Set<string> bernama seen di dalam loop berkas, sehingga cakupannya satu berkas kategori src/check.ts:12-257. Untuk setiap item, URL diambil lewat api.documentationUrl?.split('#')[0], jadi fragment #anchor dibuang sebelum dibandingkan. Bila URL sudah tercatat, skrip mencetak Duplikat documentationUrl di ${file}: ${url} lalu memanggil process.exit(1).
Dua konsekuensi urutan ini perlu diingat:
- Perbandingan memakai optional chaining, sehingga item tanpa
documentationUrltidak pernah dianggap duplikat. Penolakannya baru terjadi di pass kedua. - Karena fragment dibuang lebih dahulu,
https://contoh.dev/api#satudanhttps://contoh.dev/api#duadihitung sebagai URL yang sama.
Karena seen dibuat ulang per berkas, URL yang sama di id.json dan en.json tidak saling dianggap duplikat. Yang menahan pengulangan di tingkat lintas berkas adalah crawled.
Putaran kedua: field wajib dan pembersihan fragment
Pass kedua memeriksa !('documentationUrl' in api) || !api.documentationUrl, lalu menolak dengan pesan documentationUrl is required. dan process.exit(1) src/check.ts:27-338. Setelah lolos, nilai field langsung dibersihkan di memori dengan api.documentationUrl = api.documentationUrl.split('#')[0] src/check.ts:27-338. Pembersihan ini tidak pernah ditulis balik ke data/: skrip hanya membaca berkas lewat Bun.file(file).json() src/check.ts:9-105, jadi URL berfragment tetap ada di JSON sampai kontributor mengubahnya sendiri.
Cache crawled dan fetch ke halaman dokumentasi
crawled adalah array di level modul, jadi isinya bertahan melintasi seluruh berkas dalam satu kali jalan src/check.ts:6-79. Bila URL sudah ada di dalamnya, skrip mencetak [ cache ] => ${api.documentationUrl} dan continue tanpa fetch src/check.ts:35-3810. Tabel berikut membandingkan dua penampung tersebut.
| Penampung | Cakupan | Tujuan | Efek saat kena |
|---|---|---|---|
seen (Set<string>) | satu berkas kategori | tolak URL duplikat di dalam kategori | process.exit(1) |
crawled (string[]) | seluruh proses, semua berkas | hindari fetch ulang URL yang sama | continue, hanya log cache |
Untuk URL yang belum pernah di-crawl, skrip memanggil fetch dengan opsi berikut src/check.ts:40-4911:
| Opsi | Nilai |
|---|---|
headers | { 'Accept-Encoding': 'zlib, gzip' } |
agent | new https.Agent({ rejectUnauthorized: false }) |
redirect | 'follow' |
follow | 10 |
signal | AbortSignal.timeout(10_000) |
⚠️
rejectUnauthorized: falsemematikan verifikasi sertifikat TLS untuk semua fetch, sehingga sertifikat yang tidak valid tetap dilewati. Nilainya di-hardcode bersama pembuatanagentdi level modul src/check.ts:6-79.
Deteksi NSFW dan jalur yang gagal diam-diam
Body response diambil dengan response.text() lalu diserahkan ke isBlacklisted src/check.ts:50-5912. Fungsi itu mencocokkan substring dengan html.includes(item) pada setiap entri daftar src/utils/index.ts:35-3713, dan daftar tersebut berisi tiga kata: nfsw, porn, hentai src/data/blacklist.json:114. Bila cocok, skrip mencetak Sorry, the NSFW API is not allowed here :) lalu process.exit(1). Bila tidak, URL dimasukkan ke crawled dan status response dicetak.
⚠️ Entri pertama daftar adalah
nfsw, bukannsfw. Halaman yang hanya memuat katansfwtidak akan tertangkap olehisBlacklistedsrc/data/blacklist.json:114.
⚠️ Blok
catch (error: any) {}kosong src/check.ts:50-5912. Kegagalan jaringan, sertifikat, danAbortSignal.timeout(10_000)ditelan tanpa log maupunprocess.exit, sehingga URL yang tidak dapat dijangkau tetap dianggap lolos.
Diagram berikut memisahkan jalur yang menghentikan proses dari jalur yang dibiarkan lewat.
flowchart LR
A["api item dari data json"] --> B{"url sudah ada di seen?"}
B -->|ya| C["Duplikat documentationUrl"]
B -->|tidak| D{"documentationUrl ada?"}
D -->|tidak| E["documentationUrl is required."]
D -->|ada| F{"url sudah ada di crawled?"}
F -->|ya| G["log cache lalu continue"]
F -->|tidak| H["fetch dengan timeout 10_000 ms"]
H --> I{"isBlacklisted body?"}
I -->|ya| J["Sorry, the NSFW API is not allowed here :)"]
I -->|tidak| K["crawled.push dan log status"]
H --> L["catch kosong tanpa log"]
style A fill:#336791,color:#fff
style B fill:#f59e0b,color:#000
style D fill:#f59e0b,color:#000
style F fill:#f59e0b,color:#000
style H fill:#f59e0b,color:#000
style I fill:#f59e0b,color:#000
style C fill:#dc2626,color:#fff
style E fill:#dc2626,color:#fff
style J fill:#dc2626,color:#fff
style L fill:#dc2626,color:#fff
style G fill:#8b5cf6,color:#fff
style K fill:#2d7d46,color:#fff
%% fig: Jalur keluar validator untuk satu api item
%% desc: Tiga aturan menghentikan proses dengan exit code 1, cache hanya dilewati, dan kegagalan fetch dibiarkan tanpa jejak.Ringkasnya, hanya ada tiga pesan yang menghentikan proses, dan ketiganya memakai process.exit(1) sehingga bun check gagal. Pesan [ ... ], [ ${response.status} ], dan [ cache ] hanyalah jejak progres di konsol. Pemanggilan bun check dari CI dijelaskan di Otomasi GitHub Actions, sedangkan langkah menjalankannya sendiri ada di Menjalankan Proyek di Lokal.