6
Deteksi Client Hints
Selain UA string, ua-parser-js membaca sumber data kedua: client hints. Halaman Pipeline Deteksi Regex dan String Map menjelaskan pembacaan UA string; di sini dijelaskan bagaimana header sec-ch-ua* dipetakan ke field hasil yang sama lewat UACHData dan parseCH(), serta bagaimana hasilnya jatuh kembali ke UA string ketika hints tidak tersedia.
Dua jalur masuk data hints
UAParser menerima headers sebagai argumen pertama. Konstruktor memeriksa argumen bertipe object dan memperlakukannya sebagai headers, lalu menormalkan nama setiap header menjadi huruf kecil; objek Headers yang memiliki method append diubah lebih dulu menjadi objek biasa, dan nilai berupa array digabung dengan ', '. src/main/ua-parser.js:1422-14381 src/main/ua-parser.js:1440-14562
Setelah normalisasi, satu instance UACHData dibuat dengan isHttpUACH = true dan dibagikan ke setiap UAItem sebagai uaCH saat getter membuat item. src/main/ua-parser.js:1463-14693 src/main/ua-parser.js:1471-14904
withClientHints() memilih satu dari dua cabang pemrosesan berdasarkan NAVIGATOR_UADATA, yaitu window.navigator.userAgentData yang dibaca sekali saat modul dimuat. src/main/ua-parser.js:1128-11465
| Lingkungan | Kondisi | Jalur yang dijalankan | Nilai balik |
|---|---|---|---|
server / Node, atau browser tanpa userAgentData | !NAVIGATOR_UADATA | item.parseCH().get() | IData, sinkron |
| Chromium 85+ | NAVIGATOR_UADATA tersedia | getHighEntropyValues(CH_ALL_VALUES) lalu setCH(new UACHData(res, false)), parseCH(), get() | Promise berisi IData |
src/main/ua-parser.js:1128-11465
Karena IData.prototype.then didefinisikan, kedua bentuk itu dapat dipakai dengan pola .then() yang sama, sehingga UAParser(headers).withClientHints().then(ua => ...) bekerja di server maupun di browser. src/main/ua-parser.js:1177-11956 test/unit/ua-ch.js:109-1257
Method ini juga tersedia pada hasil setiap getter, misalnya new UAParser(headers).getBrowser().withClientHints() atau .getDevice().withClientHints(). test/unit/ua-ch.js:20-258
sequenceDiagram
participant C as Caller
participant P as UAParser
participant D as UACHData
participant I as IData
C->>P: new UAParser(headers)
P->>P: normalisasi nama header ke lowercase
P->>D: new UACHData(headers, true)
P->>I: getBrowser, getCPU, getDevice, getEngine, getOS
I->>I: parseUA dari UA string
C->>I: withClientHints()
alt NAVIGATOR_UADATA tidak ada
I->>I: parseCH lalu get
I-->>C: IData sinkron
else Chromium 85+
I->>I: getHighEntropyValues(CH_ALL_VALUES)
I->>D: setCH dengan UACHData(res, false)
I->>I: parseCH lalu get
I-->>C: Promise berisi IData
end
%% fig: Siklus withClientHints pada dua lingkungan
%% desc: Header dipetakan sekali saat konstruksi, lalu withClientHints memilih jalur sinkron di server atau jalur promise lewat getHighEntropyValues di Chromium.⚠️
setCH()menggantiuaCHpadaUAItemyang sama, dan cabang browser mengembalikanPromise. Tanpaawaitatau.then(), field hasil masih berisi nilai dariparseUA()saja. src/main/ua-parser.js:1247-12509 src/main/ua-parser.js:1128-11465
UACHData: dari header mentah ke properti
UACHData menginisialisasi sembilan properti dari CH_ALL_VALUES menjadi undefined, lalu mengisinya dari header bila isHttpUACH bernilai true. Bila false, properti dengan nama yang sama disalin dari objek res milik getHighEntropyValues. src/main/ua-parser.js:1204-122410 src/main/ua-parser.js:63-7911
| Konstanta | Nama header | Properti | Cara baca |
|---|---|---|---|
CH | sec-ch-ua | brands | itemListToArray |
CH_FULL_VER_LIST | sec-ch-ua-full-version-list | fullVersionList | itemListToArray |
CH_ARCH | sec-ch-ua-arch | architecture | normalizeHeaderValue |
CH_BITNESS | sec-ch-ua-bitness | bitness | normalizeHeaderValue |
CH_FORM_FACTORS | sec-ch-ua-form-factors | formFactors | itemListToArray |
CH_MOBILE | sec-ch-ua-mobile | mobile | ?1 diuji dengan regex |
CH_MODEL | sec-ch-ua-model | model | normalizeHeaderValue |
CH_PLATFORM | sec-ch-ua-platform | platform | normalizeHeaderValue |
CH_PLATFORM_VER | sec-ch-ua-platform-version | platformVersion | normalizeHeaderValue |
src/main/ua-parser.js:63-7911 src/main/ua-parser.js:1207-121812
itemListToArray membuang tanda kutip lewat normalizeHeaderValue, memecah nilai pada ,, lalu memecah setiap token pada ;v= sehingga menghasilkan { brand, version }; token tanpa ;v= disimpan sebagai string biasa. normalizeHeaderValue hanya menerima string, membuang ", dan memotong nilai pada UA_MAX_LENGTH (500 karakter). src/main/ua-parser.js:170-18313 src/main/ua-parser.js:190-19214 src/main/ua-parser.js:22-2415
Karena itu nilai kosong seperti 'sec-ch-ua-full-version' : '""' atau 'sec-ch-ua-arch' : '""' menjadi string kosong, bukan versi atau arsitektur, sehingga field terkait tidak terisi. test/unit/ua-ch.js:156-17616
Pemetaan ke field hasil lewat parseCH
parseCH() bercabang menurut itemType dan hanya menulis field yang sumbernya tersedia. src/main/ua-parser.js:1320-135217
itemType | Sumber hints | Aksi |
|---|---|---|
BROWSER, ENGINE | fullVersionList, fallback brands | melewati brand yang cocok /not.a.brand/i, memetakan nama lewat browserHintsMap, versi engine diambil dari entri Chromium |
CPU | architecture, bitness | menambahkan akhiran 64 bila bitness == '64', lalu mem-parse ulang archName + ';' dengan rgxMapper |
DEVICE | mobile, model, formFactors | mobile menetapkan mobile; model memicu parse ulang 'droid 9; ' + model + ')' untuk vendor/type; formFactors dipetakan lewat formFactorsMap |
OS | platform, platformVersion, model | Windows: majorize versi platform 13 atau lebih menjadi '11', selain itu '10'; model == 'Xbox' mengubah nama menjadi Xbox |
RESULT | semua di atas | memanggil setCH(uaCH).parseCH().get() pada tiap sub-item |
src/main/ua-parser.js:1320-135217 src/main/ua-parser.js:1353-135918 src/main/ua-parser.js:1360-1389 src/main/ua-parser.js:1390-1403 src/main/ua-parser.js:1404-1417
Urutan entri brand tidak menentukan hasil. browserHintsMap memetakan nama brand ke nama kanonik seperti Chrome ke Google Chrome dan Edge ke Microsoft Edge, sehingga rangkaian Not_A Brand, Chromium, Google Chrome, Microsoft Edge dalam urutan apa pun tetap menghasilkan Edge bila hadir, lalu Chrome, lalu Chromium. src/main/ua-parser.js:331-34122 test/unit/ua-ch.js:178-22223
formFactorsMap memetakan form factor ke device.type; kunci ? meliputi Desktop dan Unknown dan strMapper mengembalikan undefined untuk kunci itu, sehingga "Unknown" tidak menetapkan tipe. src/main/ua-parser.js:320-32924 src/main/ua-parser.js:285-30025 test/unit/ua-ch.js:128-15426
Header sec-ch-ua-model saja sudah cukup untuk mengisi device.model, device.vendor, dan device.type, misalnya model Xbox menghasilkan vendor Microsoft dengan tipe console. test/unit/ua-ch.js:405-41627
graph TD
H1["brands dan fullVersionList"] --> B["BROWSER, ENGINE: browserHintsMap"]
H2["architecture dan bitness"] --> C["CPU: rgxMapper archName plus titik koma"]
H3["mobile, model, formFactors"] --> D["DEVICE: formFactorsMap dan parse ulang model"]
H4["platform dan platformVersion"] --> O["OS: Windows versi 13 ke atas jadi 11"]
UA["parseUA dari UA string"] --> R["IData hasil"]
B --> R
C --> R
D --> R
O --> R
X["Field hints kosong atau undefined"] -.-> R
style H1 fill:#8b5cf6,color:#fff
style H2 fill:#8b5cf6,color:#fff
style H3 fill:#8b5cf6,color:#fff
style H4 fill:#8b5cf6,color:#fff
style B fill:#f59e0b,color:#000
style C fill:#f59e0b,color:#000
style D fill:#f59e0b,color:#000
style O fill:#f59e0b,color:#000
style UA fill:#2d7d46,color:#fff
style R fill:#336791,color:#fff
style X fill:#dc2626,color:#fff
%% fig: Alur properti UACHData ke field hasil
%% desc: Setiap itemType membaca properti hints tertentu lalu menulis field-nya sendiri, sementara nilai dari parseUA tetap menjadi isi awal dan menjadi cadangan saat field hints kosong.Fallback ke UA string
parseCH() hanya menetapkan field yang sumber hints-nya ada, sehingga nilai hasil parseUA() yang dijalankan sebelumnya tetap dipakai ketika hints tidak lengkap. Pada contoh dengan hanya sec-ch-ua-mobile: ?1, nama dan versi browser tetap berasal dari UA string, sementara device.type menjadi mobile. test/unit/ua-ch.js:76-9628
| Field | Nilai | Sumber |
|---|---|---|
browser.name | Chrome | UA string |
browser.version | 110.0.0.0 | UA string |
cpu.architecture | x86_64 | UA string |
device.type | mobile | sec-ch-ua-mobile |
device.model, device.vendor | undefined | tidak ada hints |
Tanpa pemanggilan withClientHints() sama sekali, seluruh header sec-ch-ua* diabaikan dan semua field berasal dari UA string: versi 110.0.0.0, arsitektur x86_64, serta device.type, device.model, dan device.vendor yang undefined. test/unit/ua-ch.js:54-7429
Sebaliknya, bila hints tersedia nilainya menang atas UA string. Pada contoh, UA string menyebut Chrome/110.0.0.0 tetapi header versi menyebut 93.0.1.2, sehingga hasilnya browser.version 93.0.1.2, cpu.architecture arm64, os.version 11 untuk sec-ch-ua-platform-version "13", serta device.model Pixel 99 dengan vendor Google. test/unit/ua-ch.js:8-5230
Data uji berbentuk tabel UACHTests dengan field desc, headers, dan expect dipakai untuk memverifikasi deteksi brand yang hanya bersumber dari sec-ch-ua, lalu setiap entri diuji dengan UAParser(test.headers).withClientHints(). test/data/ua-ch/headers.js:1-1531 test/unit/ua-ch.js:225-23232
Untuk menggabungkan pembacaan hints dengan pemeriksaan fitur, lihat Feature Check dan Perbandingan Hasil.