Lompat ke konten
dokumentasi.id
Commit bd74801
v1
Star10 rb

Kamu membaca versi lama. Halaman ini hanya bisa dibaca.

2.1

Alur Parsing User-Agent dan Client Hints

Satu pemanggilan new UAParser(...).getResult() melewati empat lapis kerja. UAParser menyiapkan masukan, UAItem.parseUA mengisi field dari string User-Agent, UAItem.parseCH menimpanya dengan client hints, lalu UAItem.detectFeature menambal sisa field dari kemampuan browser. Semua lapis menulis ke IData yang sama, sehingga urutan pemanggilan menentukan nilai akhir. Bentuk field hasilnya dibahas di Model Data Hasil dan Enums.

Konstruktor memilih masukan

UAParser(ua, extensions, headers) menerima tiga bentuk argumen. Bila argumen pertama sebuah object, konstruktor memutuskan apakah itu extensions atau headers. Bila argumen pertama string dan argumen kedua bukan extensions, argumen kedua diperlakukan sebagai headers src/main/ua-parser.js:1422-1438.

Headers dinormalkan sebelum dipakai. Bila object headers punya metode append, isinya disalin ke object biasa dengan key lowercase. Bila tidak, key di-lowercase dan nilai berbentuk array digabung dengan ', ' src/main/ua-parser.js:1440-1457.

String User-Agent diambil berurutan: argumen ua bila string, lalu headers['user-agent'], lalu navigator.userAgent, dan terakhir string kosong src/main/ua-parser.js:1463-1466.

NilaiAsalRujukan
userAgentargumen ua, headers['user-agent'], navigator.userAgent, atau ''src/main/ua-parser.js:1463-1466
httpUACHnew UACHData(headers, true)src/main/ua-parser.js:1468-1469
regexMapdefaultRegexes, lalu useExtension(extensions)src/main/ua-parser.js:1468-1469 dan src/main/ua-parser.js:1505-1508
userAgent akhirsetUA(userAgent) memotong ke UA_MAX_LENGTH = 500src/main/ua-parser.js:1501-1504 dan src/main/ua-parser.js:22-23

UACHData(headers, true) membaca header sec-ch-ua, sec-ch-ua-full-version-list, sec-ch-ua-mobile (diuji dengan /\?1/), sec-ch-ua-model, sec-ch-ua-platform, sec-ch-ua-platform-version, sec-ch-ua-arch, sec-ch-ua-form-factors, dan sec-ch-ua-bitness. Nama field yang dipakai adalah isi CH_ALL_VALUES src/main/ua-parser.js:63-79 dan src/main/ua-parser.js:1204-1223.

getResult merangkai lima item

createItemFunc(RESULT) membuat UAItem(RESULT, userAgent, regexMap, httpUACH), menulis 'ua', lalu memanggil this.getBrowser(), getCPU(), getDevice(), getEngine(), dan getOS() untuk mengisi IResult src/main/ua-parser.js:1471-1490. Tiap item getter membuat UAItem(itemType, userAgent, regexMap[itemType], httpUACH) dan langsung menjalankan parseUA() src/main/ua-parser.js:1483-1488.

sequenceDiagram
    participant C as Pemanggil
    participant P as UAParser
    participant R as UAItem RESULT
    participant I as UAItem per itemType
    participant M as rgxMapper
    C->>P: new UAParser ua extensions headers
    P->>P: normalisasi headers dan pemilihan userAgent
    P->>P: setUA userAgent dan useExtension extensions
    C->>P: getResult
    P->>R: new UAItem RESULT userAgent regexMap httpUACH
    loop browser lalu cpu device engine os
        R->>R: this.getBrowser dan seterusnya
        R->>I: new UAItem itemType userAgent regexMap itemType httpUACH
        I->>M: parseUA memanggil rgxMapper pada data
        M-->>I: data terisi name version major type
        I-->>R: get mengembalikan IData
        R->>R: set itemType ke data IResult
    end
    R-->>C: IResult siap
%% fig: Siklus getResult sampai IResult
%% desc: Konstruktor menyiapkan masukan, lalu getResult membuat satu UAItem RESULT yang memanggil lima item getter dan masing-masing menjalankan parseUA lewat rgxMapper.
Fig. 1: Siklus getResult sampai IResultKonstruktor menyiapkan masukan, lalu getResult membuat satu UAItem RESULT yang memanggil lima item getter dan masing-masing menjalankan parseUA lewat rgxMapper.

Field awal tiap item ditentukan defaultProps.init. Browser memakai [name, version, major, type], CPU memakai [architecture], device memakai [type, model, vendor], engine dan OS memakai [name, version] src/main/ua-parser.js:1087-1093. createIData membungkusnya menjadi IData dan menyediakan getItem(), withClientHints(), serta withFeatureCheck() src/main/ua-parser.js:1113-1150. Konstruktor UAItem sendiri hanya menyimpan itemType, ua, uaCH, rgxMap, dan data src/main/ua-parser.js:1226-1235.

UAItem.parseUA mengisi field dari string UA

Untuk semua itemType selain result, parseUA memanggil rgxMapper.call(this.data, this.ua, this.rgxMap) src/main/ua-parser.js:1296-1318. rgxMapper menelusuri pasangan array regex dan daftar prop, berhenti pada match pertama, lalu menulis nilai ke this.data sesuai bentuk prop (string, pasangan [nama, nilai], atau mapper fungsi/regex) src/main/ua-parser.js:218-235.

itemTypeTambahan setelah rgxMapperRujukan
lainnyahanya hasil rgxMappersrc/main/ua-parser.js:1297-1299
browserset(MAJOR, majorize(version))src/main/ua-parser.js:1301-1303
osbila versi iOS dimulai 18 atau 19, versi diambil ulang dari ) Version/ ketika angka mayor versi Safari minimal 26src/main/ua-parser.js:1305-1315

UAItem.parseCH menimpa field dengan client hints

parseCH tidak dipanggil oleh getResult(); ia baru berjalan lewat withClientHints() atau lewat cabang result di dalam parseCH itu sendiri src/main/ua-parser.js:1128-1146. Karena itu pemanggilan tanpa withClientHints() hanya membaca header User-Agent test/unit/ua-ch.js:54-60.

sequenceDiagram
    participant C as Pemanggil
    participant D as IData
    participant I as UAItem
    participant N as navigator.userAgentData
    participant U as UACHData
    C->>D: withClientHints
    alt NAVIGATOR_UADATA tidak tersedia
        D->>I: parseCH dengan uaCH dari httpUACH
        I-->>C: IData hasil parseCH
    else Chromium 85 ke atas
        D->>N: getHighEntropyValues CH_ALL_VALUES
        N-->>D: res
        D->>U: new UACHData res false
        D->>I: setCH lalu parseCH
        I-->>C: Promise berisi IData
    end
%% fig: Dua jalur withClientHints
%% desc: Tanpa navigator.userAgentData parseCH langsung dijalankan memakai httpUACH, sedangkan Chromium 85 ke atas menunggu getHighEntropyValues lebih dulu.
Fig. 2: Dua jalur withClientHintsTanpa navigator.userAgentData parseCH langsung dijalankan memakai httpUACH, sedangkan Chromium 85 ke atas menunggu getHighEntropyValues lebih dulu.

Pada cabang result, tiap item diambil lewat getItem(), lalu setCH(uaCH), parseCH(), dan get(). Instance UAItem-nya sama dengan hasil parseUA, sehingga field yang sudah terisi hanya berubah bila client hints menulis nilai baru src/main/ua-parser.js:1404-1417.

itemTypeSumber di uaCHTindakanRujukan
browser, enginefullVersionList lalu brandsmenelusuri brand, melewati /not.a.brand/i, memetakan nama lewat browserHintsMap, mengisi name/version/major; pada engine hanya brand Chromium yang mengisi versionsrc/main/ua-parser.js:1320-1351
cpuarchitecture, bitnessmenambah akhiran 64 bila bitness bernilai 64, lalu rgxMapper dengan string archName + ';'src/main/ua-parser.js:1353-1359
devicemobile, model, formFactorsmengisi type mobile, mengisi model, parse ulang 'droid 9; ' + model + ')' untuk type dan vendor, lalu memetakan formFactors lewat formFactorsMapsrc/main/ua-parser.js:1360-1389
osplatform, platformVersionmengisi name/version; untuk Windows angka mayor minimal 13 menjadi '11', selebihnya '10'; bila model bernilai Xbox, name menjadi Xbox dan version menjadi undefinedsrc/main/ua-parser.js:1390-1403

Pemetaan nilai memakai dua tabel statis: browserHintsMap untuk nama browser dan formFactorsMap untuk tipe perangkat, yang memetakan embedded, mobile, tablet, smarttv, wearable, xr, dan ? src/main/ua-parser.js:320-341.

UAItem.detectFeature menambal dari kemampuan browser

detectFeature hanya bekerja bila NAVIGATOR.userAgent sama dengan this.ua yang sedang di-parse src/main/ua-parser.js:1252-1294. Di luar kondisi itu IData tidak berubah, sehingga di Node.js fungsi ini praktis tidak berefek.

graph TD
    START["detectFeature"] --> GUARD{"NAVIGATOR.userAgent == this.ua"}
    GUARD -- "tidak" --> SKIP["IData tidak berubah"]
    GUARD -- "ya" --> SW{"itemType"}
    SW -- "browser" --> BR["navigator.brave.isBrave -> name Brave"]
    SW -- "device" --> DEV["userAgentData.mobile -> type mobile | maxTouchPoints > 2 -> model iPad dan type tablet"]
    SW -- "os" --> OS["userAgentData.platform -> name"]
    SW -- "result" --> RES["detect tiap item lalu set ke IResult"]
    style START fill:#f59e0b,color:#000
    style GUARD fill:#f59e0b,color:#000
    style SKIP fill:#dc2626,color:#fff
    style BR fill:#2d7d46,color:#fff
    style DEV fill:#2d7d46,color:#fff
    style OS fill:#2d7d46,color:#fff
    style RES fill:#8b5cf6,color:#fff
%% fig: Guard dan cabang detectFeature
%% desc: detectFeature berjalan hanya bila string UA yang di-parse sama dengan navigator.userAgent, lalu mengoreksi per itemType memakai API browser.
Fig. 3: Guard dan cabang detectFeaturedetectFeature berjalan hanya bila string UA yang di-parse sama dengan navigator.userAgent, lalu mengoreksi per itemType memakai API browser.

Cabang device mengisi type mobile dari navigator.userAgentData bila type masih kosong. Bila model bernilai Macintosh dan navigator.maxTouchPoints lebih dari 2 sementara navigator.standalone terdefinisi, model diubah menjadi iPad dan type menjadi tablet src/main/ua-parser.js:1261-1271. Cabang os mengisi name dari userAgentData.platform bila hasil parseUA belum punya nama src/main/ua-parser.js:1272-1277.

⚠️ withClientHints() mengembalikan Promise di Chromium 85+ tetapi objek IData biasa di Node.js dan browser tanpa navigator.userAgentData. IData.prototype.then membuat kedua bentuk itu bisa dirangkai dengan .then(), namun kode yang memakai await perlu memperhitungkan bahwa nilainya baru terisi setelah promise selesai src/main/ua-parser.js:1177-1195.

Berkas .mjs mengikuti alur yang sama

src/main/ua-parser.mjs adalah versi ESM hasil bangkitan dari src/main/ua-parser.js; berkas itu menyatakan dirinya tidak boleh diedit langsung dan hanya menambahkan export {UAParser} di akhir berkas src/main/ua-parser.mjs:1-3 dan src/main/ua-parser.mjs:1519. Alur UAParser, parseUA, parseCH, dan detectFeature identik, hanya nomor barisnya bergeser beberapa baris. Rincian API pemanggilnya ada di API UAParser.