Lompat ke konten
dokumentasi.id
Commit 3ce63fd
v3
Star5,3 rb

Kamu membaca versi lama. Halaman ini hanya bisa dibaca.

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-42. Kontributor diminta menjalankannya sebelum mengajukan pull request CONTRIBUTING.md:25-27. 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-7.

Cakupan berkas yang diperiksa

getFiles() menelusuri direktori data/ secara rekursif dan hanya mengumpulkan berkas berekstensi .json src/utils/index.ts:13-33. 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-10. 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-8, 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.
Fig. 1: Satu putaran validasi di check.tsTiap 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-25. 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 documentationUrl tidak pernah dianggap duplikat. Penolakannya baru terjadi di pass kedua.
  • Karena fragment dibuang lebih dahulu, https://contoh.dev/api#satu dan https://contoh.dev/api#dua dihitung 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-33. Setelah lolos, nilai field langsung dibersihkan di memori dengan api.documentationUrl = api.documentationUrl.split('#')[0] src/check.ts:27-33. Pembersihan ini tidak pernah ditulis balik ke data/: skrip hanya membaca berkas lewat Bun.file(file).json() src/check.ts:9-10, 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-7. Bila URL sudah ada di dalamnya, skrip mencetak [ cache ] => ${api.documentationUrl} dan continue tanpa fetch src/check.ts:35-38. Tabel berikut membandingkan dua penampung tersebut.

PenampungCakupanTujuanEfek saat kena
seen (Set<string>)satu berkas kategoritolak URL duplikat di dalam kategoriprocess.exit(1)
crawled (string[])seluruh proses, semua berkashindari fetch ulang URL yang samacontinue, hanya log cache

Untuk URL yang belum pernah di-crawl, skrip memanggil fetch dengan opsi berikut src/check.ts:40-49:

OpsiNilai
headers{ 'Accept-Encoding': 'zlib, gzip' }
agentnew https.Agent({ rejectUnauthorized: false })
redirect'follow'
follow10
signalAbortSignal.timeout(10_000)

⚠️ rejectUnauthorized: false mematikan verifikasi sertifikat TLS untuk semua fetch, sehingga sertifikat yang tidak valid tetap dilewati. Nilainya di-hardcode bersama pembuatan agent di level modul src/check.ts:6-7.

Deteksi NSFW dan jalur yang gagal diam-diam

Body response diambil dengan response.text() lalu diserahkan ke isBlacklisted src/check.ts:50-59. Fungsi itu mencocokkan substring dengan html.includes(item) pada setiap entri daftar src/utils/index.ts:35-37, dan daftar tersebut berisi tiga kata: nfsw, porn, hentai src/data/blacklist.json:1. 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, bukan nsfw. Halaman yang hanya memuat kata nsfw tidak akan tertangkap oleh isBlacklisted src/data/blacklist.json:1.

⚠️ Blok catch (error: any) {} kosong src/check.ts:50-59. Kegagalan jaringan, sertifikat, dan AbortSignal.timeout(10_000) ditelan tanpa log maupun process.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.
Fig. 2: Jalur keluar validator untuk satu api itemTiga 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.