Perpustakaan klien Node.js untuk menggunakan Google API. Dukungan untuk otorisasi dan otentikasi dengan OAuth 2.0, Keys API dan token JWT disertakan.
Google API
Memulai
Instalasi
Menggunakan Perpustakaan Klien
Sampel
Referensi API
Otentikasi dan Otorisasi
Klien OAuth2
Menggunakan kunci API
Kredensial Default Aplikasi
Kredensial Akun Layanan
Mengatur Auth Global atau Layanan Layanan
Penggunaan
Menentukan Badan Permintaan
Unggahan media
Opsi permintaan
Menggunakan proxy
API yang didukung
Naskah
Http/2
Lisensi
Berkontribusi
Pertanyaan/Masalah?
Daftar lengkap API yang didukung dapat ditemukan di Google APIS Explorer. Titik akhir API dihasilkan secara otomatis, jadi jika API tidak ada dalam daftar, saat ini tidak didukung oleh Perpustakaan Klien API ini.
Saat memanfaatkan API Google Cloud Platform seperti Datastore, Cloud Storage, atau Pub/Sub, disarankan untuk memanfaatkan pustaka klien @Google-Cloud. Perpustakaan ini adalah klien yang dibangun khusus, klien Node.js idiomatik yang dirancang untuk layanan platform Google Cloud tertentu. Kami sarankan menginstal paket API individual, seperti @google-cloud/storage
. Untuk menjelajahi daftar komprehensif paket spesifik API Google Cloud, silakan merujuk ke https://cloud.google.com/nodejs/docs/reference.
Perpustakaan klien ini secara resmi didukung oleh Google. Namun, perpustakaan ini dianggap lengkap dan dalam mode pemeliharaan. Ini berarti bahwa kami akan mengatasi bug kritis dan masalah keamanan tetapi tidak akan menambahkan fitur baru. Untuk API Google Cloud Platform, kami sarankan menggunakan Google-Cloud-Node yang sedang dalam pengembangan aktif.
Perpustakaan ini mendukung LTS pemeliharaan, LTS aktif, dan rilis Node.js. saat ini saat ini Lihat jadwal rilis Node.js untuk informasi lebih lanjut.
Perpustakaan ini didistribusikan di npm
. Untuk menambahkannya sebagai ketergantungan, jalankan perintah berikut:
$ npm instal googleapis
Jika Anda perlu mengurangi waktu startup, Anda dapat menginstal submodule sebagai ketergantungannya sendiri. Kami berupaya menerbitkan submodul yang tidak ada dalam daftar ini. Untuk menambahkannya sebagai ketergantungan, jalankan perintah sampel berikut, ganti dengan API pilihan Anda:
$ npm instal @googleapis/docs
Anda dapat menjalankan pencarian ini di npm
, untuk menemukan daftar submodul yang tersedia.
Ini adalah contoh yang sangat sederhana. Ini menciptakan klien blogger dan mengambil detail blog yang diberikan ID blog:
const {google} = membutuhkan ('googleapis'); // Setiap API dapat mendukung beberapa versi. Dengan sampel ini, kami mendapatkan // V3 dari Blogger API, dan menggunakan kunci API untuk authenticate.const blogger = google.blogger ({ Versi: 'V3', AUTH: 'Kunci API Anda'}); const params = { BlogID: '3213900'}; // Dapatkan blog detailblogger.blogs.get (params, (err, res) => { if (err) {console.error (err); lempar err; } console.log (`URL blog adalah $ {res.data.url}`);});
Alih -alih menggunakan callback, Anda juga dapat menggunakan janji!
blogger.blogs.get (params) .then (res => {console.log (`URL blog adalah $ {res.data.url}`); }) .catch (error => {console.error (error); });
Atau async/menunggu:
fungsi async runsample () { const res = menunggu blogger.blogs.get (params); console.log (`URL blog adalah $ {res.data.url}`);} runsample (). Catch (Console.Error);
Atau, Anda dapat melakukan panggilan langsung ke API dengan memasang submodule:
const docs = membutuhkan ('@googleapis/docs') const auth = new docs.auth.googleAuth ({ KeyFileName: 'path_to_service_account_key.json', // scopes dapat ditentukan sebagai array atau sebagai string tunggal yang dibatasi ruang. SCOPES: ['https://www.googleapis.com/Auth/document' ();const authclient = menunggu auth.getClient (); const client = menunggu docs.docs ({versi:' v1 ', auth: authclient }); const createresponse = menunggu client.documents.create ({requestbody: {title: 'dokumen baru Anda!',},}); console.log (createresponse.data);
Ada banyak sampel? Jika Anda mencoba mencari cara menggunakan API ... lihat di sana terlebih dahulu! Jika ada sampel yang Anda butuhkan, jangan ragu untuk mengajukan masalah.
Perpustakaan ini memiliki satu set lengkap dokumentasi referensi API. Dokumentasi ini dihasilkan secara otomatis, dan lokasi dapat berubah.
Ada beberapa cara untuk mengotentikasi ke Google API. Beberapa layanan mendukung semua metode otentikasi, sementara yang lain hanya dapat mendukung satu atau dua.
OAuth2 - Ini memungkinkan Anda untuk melakukan panggilan API atas nama pengguna yang diberikan. Dalam model ini, pengguna mengunjungi aplikasi Anda, masuk dengan akun Google mereka, dan memberikan aplikasi Anda dengan otorisasi terhadap satu set lingkup. Pelajari lebih lanjut.
Kunci API - Dengan kunci API, Anda dapat mengakses layanan Anda dari klien atau server. Biasanya kurang aman, ini hanya tersedia pada subset kecil layanan dengan lingkup terbatas. Pelajari lebih lanjut.
Aplikasi Kredensial Default - Menyediakan akses otomatis ke Google API menggunakan Google Cloud SDK untuk pengembangan lokal, atau GCE Metadata Server untuk aplikasi yang digunakan ke Google Cloud Platform. Pelajari lebih lanjut.
Kredensial Akun Layanan - Dalam model ini, aplikasi Anda berbicara langsung ke Google API menggunakan akun layanan. Ini berguna ketika Anda memiliki aplikasi backend yang akan berbicara langsung ke Google API dari backend. Pelajari lebih lanjut.
Untuk mempelajari lebih lanjut tentang klien otentikasi, lihat Perpustakaan Google Auth.
Modul ini dilengkapi dengan klien OAuth2 yang memungkinkan Anda untuk mengambil token akses, menyegarkannya, dan mencoba lagi permintaan dengan mulus. Dasar -dasar implementasi OAuth2 Google dijelaskan pada otorisasi Google dan dokumentasi otentikasi.
Dalam contoh -contoh berikut, Anda mungkin memerlukan CLIENT_ID
, CLIENT_SECRET
dan REDIRECT_URL
. Anda dapat menemukan informasi ini dengan pergi ke konsol pengembang, mengklik proyek Anda -> API & AUTH -> Kredensial.
Arahkan ke konsol cloud dan buat ID klien OAuth2 baru
Pilih Web Application
untuk jenis aplikasi
Tambahkan URI Redirect Resmi dengan nilai http://localhost:3000/oauth2callback
(atau nilai yang berlaku untuk skenario Anda)
Klik Create
, dan Ok
di layar berikut
Klik ikon Download
di sebelah ID klien OAuth2 yang baru Anda buat
Pastikan untuk menyimpan file ini di tempat yang aman, dan jangan periksa file ini ke kontrol sumber!
Untuk informasi lebih lanjut tentang OAuth2 dan cara kerjanya, lihat di sini.
Aplikasi sampel lengkap yang mengizinkan dan mengotentikasi dengan klien OAuth2 tersedia di samples/oauth2.js
.
Untuk meminta izin dari pengguna untuk mengambil token akses, Anda mengarahkan mereka ke halaman persetujuan. Untuk membuat URL halaman persetujuan:
const {google} = membutuhkan ('googleapis'); const oauth2client = google.auth.oauth2 baru ( Your_client_id, Your_client_secret, Your_redirect_url); // menghasilkan URL yang meminta izin untuk Blogger dan Google Calendar Scopescons Scopes = [ 'https://www.googleapis.com/auth/blogger', 'https://www.googleapis.com/Auth/calendar'ace;const url = oauth2client.generateAuthurl ({ // 'online' (default) atau 'offline' (mendapat refresh_token) Access_type: 'offline', // Jika Anda hanya membutuhkan satu ruang lingkup, Anda dapat meneruskannya sebagai string SCOPE: SCOPES});
Catatan penting - refresh_token
hanya dikembalikan pada otorisasi pertama. Lebih detail di sini.
Setelah pengguna telah memberikan izin pada halaman persetujuan, Google akan mengarahkan halaman ke URL pengalihan yang telah Anda berikan dengan parameter kueri kode.
GET /oauthcallback?code={authorizationCode}
Dengan kode yang dikembalikan, Anda dapat meminta token akses seperti yang ditunjukkan di bawah ini:
// Ini akan memberikan objek dengan access_token dan refresh_token.// simpan ini di tempat yang aman sehingga dapat digunakan di waktu lain.
Dengan kredensial yang ditetapkan pada klien OAuth2 Anda - Anda siap untuk pergi!
Akses token kedaluwarsa. Perpustakaan ini akan secara otomatis menggunakan token refresh untuk mendapatkan token akses baru jika akan berakhir. Cara mudah untuk memastikan Anda selalu menyimpan token terbaru adalah dengan menggunakan acara tokens
:
oauth2client.on ('token', (token) => { if (tokens.refresh_token) {// simpan refresh_token di database saya! console.log (tokens.refresh_token); } console.log (tokens.access_token);});
Acara token ini hanya terjadi pada otorisasi pertama, dan Anda harus mengatur access_type
Anda untuk offline
saat memanggil metode generateAuthUrl
untuk menerima token refresh. Jika Anda telah memberikan aplikasi Anda izin yang diperlukan tanpa menetapkan kendala yang sesuai untuk menerima token refresh, Anda harus menghormati aplikasi aplikasi untuk menerima token refresh segar. Anda dapat mencabut akses aplikasi Anda ke akun Anda di sini.
Untuk mengatur refresh_token
di lain waktu, Anda dapat menggunakan metode setCredentials
:
OAuth2Client.SetCredentials ({ refresh_token: `stored_refresh_token`});
Setelah klien memiliki token refresh, akses token akan diperoleh dan disegarkan secara otomatis dalam panggilan berikutnya ke API.
Token penyegaran dapat berhenti bekerja setelah dikabulkan, baik karena:
Pengguna telah mencabut akses aplikasi Anda
Token Refresh belum digunakan selama 6 bulan
Pengguna mengubah kata sandi dan token refresh berisi lingkup gmail
Akun pengguna telah melampaui sejumlah maksimum token refresh hidup
Aplikasi ini memiliki status 'pengujian' dan layar persetujuan dikonfigurasi untuk jenis pengguna eksternal, menyebabkan token berakhir dalam 7 hari
Sebagai pengembang, Anda harus menulis kode Anda untuk menangani kasus di mana token refresh tidak lagi berfungsi.
Anda mungkin perlu mengirim kunci API dengan permintaan yang akan Anda buat. Berikut ini menggunakan kunci API untuk membuat permintaan ke layanan API blogger untuk mengambil nama blog, URL, dan jumlah totalnya:
const {google} = membutuhkan ('googleapis'); const blogger = google.blogger_v3 ({ Versi: 'V3', AUTH: 'your_api_key' // Tentukan tombol API Anda di sini}); const params = { BlogID: '3213900'}; Fungsi Async Main (params) { const res = menunggu blogger.blogs.get ({blogid: params.blogid}); console.log (`$ {res.data.name} memiliki $ {res.data.posts.totalitems} posting! URL blog adalah $ {res.data.url}`)}; main (). Catch (konsol. kesalahan);
Untuk mempelajari lebih lanjut tentang kunci API, silakan lihat dokumentasinya.
Daripada secara manual membuat klien OAuth2, klien JWT, atau klien menghitung, perpustakaan auth dapat membuat jenis kredensial yang benar untuk Anda, tergantung pada lingkungan yang dijalankan kode Anda.
Misalnya, klien JWT Auth akan dibuat ketika kode Anda berjalan pada mesin pengembang lokal Anda, dan klien komputasi akan dibuat ketika kode yang sama berjalan pada instance yang dikonfigurasi dari Google Compute Engine. Kode di bawah ini menunjukkan cara mengambil jenis kredensial default, tergantung pada lingkungan runtime.
Untuk menggunakan kredensial default aplikasi secara lokal dengan Google Cloud SDK, jalankan:
$ gCloud auth aplikasi-default login
Saat berjalan di GCP, Otorisasi Layanan secara otomatis disediakan melalui Server Metadata GCE.
const {google} = membutuhkan ('googleapis'); const compute = google.compute ('v1'); fungsi async main () { const auth = google.auth.googleAuth baru ({// scopes dapat ditentukan baik sebagai array atau sebagai satu, space-delimited string.scopes: ['https://www.googleapis.com/auth/compute'] }); const authclient = menunggu auth.getClient (); // dapatkan ID proyek saat ini const proyek = menunggu auth.getProjectId (); // Ambil daftar zona GCE dalam suatu proyek. const res = menunggu compute.zones.list ({Project, auth: authclient}); console.log (res.data);} main (). Catch (Console.Error);
Akun layanan memungkinkan Anda untuk melakukan otentikasi tingkat-ke-server, tingkat aplikasi menggunakan akun robot. Anda akan membuat akun layanan, mengunduh keyfile, dan menggunakannya untuk mengotentikasi ke Google API. Untuk membuat akun layanan:
Buka halaman Kunci Akun Layanan Buat
Pilih New Service Account
di drop -down
Klik tombol Create
Simpan file kredensial akun layanan di tempat yang aman, dan jangan periksa file ini ke kontrol sumber ! Untuk merujuk file kredensial akun layanan, Anda memiliki beberapa opsi.
GOOGLE_APPLICATION_CREDENTIALS
env var Anda dapat mulai memproses dengan variabel lingkungan bernama GOOGLE_APPLICATION_CREDENTIALS
. Nilai Env ini harus menjadi jalur lengkap ke file kredensial akun layanan:
$ Google_application_credentials =./Your-secret-key.json node server.js
keyFile
Atau, Anda dapat menentukan jalur ke file kredensial akun layanan melalui properti keyFile
di konstruktor GoogleAuth
:
const {google} = membutuhkan ('googleapis'); const auth = google.auth.googleAuth baru ({ Keyfile: '/path/to/your-secret-key.json', SCOPES: ['https://www.googleapis.com/auth/cloud-platform'],});
Anda dapat mengatur auth
sebagai opsi global atau tingkat layanan sehingga Anda tidak perlu menentukannya setiap permintaan. Misalnya, Anda dapat mengatur auth
sebagai opsi global:
const {google} = membutuhkan ('googleapis'); const oauth2client = google.auth.oauth2 baru ( Your_client_id, Your_client_secret, Your_redirect_url); // atur auth sebagai global defaultgoogle.options ({ auth: oauth2client});
Alih-alih mengatur opsi secara global, Anda juga dapat mengatur klien otentikasi di tingkat layanan:
const {google} = membutuhkan ('googleapis'); const oauth2client = google.auth.oauth2 baru ( Your_client_id, Your_client_secret, Your_redirect_url); const drive = google.drive ({ Versi: 'V2', auth: oauth2client});
Lihat bagian Opsi untuk informasi lebih lanjut.
Badan permintaan ditentukan dalam objek parameter requestBody
dari permintaan. Tubuh ditentukan sebagai objek JavaScript dengan pasangan kunci/nilai. Misalnya, sampel ini menciptakan pengamat yang memposting pemberitahuan ke pub/sub Google Cloud ketika email dikirim ke akun Gmail:
const res = menunggu gmail.users.watch ({ userid: 'saya', requestbody: {// ganti dengan `proyek/$ {proyek_id}/topics/$ {topic_name}` topicname: `Proyek/el-gato/topik/gmail` }}); console.log (res.data);
Klien ini mendukung unggahan media multipart. Parameter sumber daya ditentukan dalam objek parameter requestBody
, dan media itu sendiri ditentukan dalam media.mimeType
media.body
.
Contoh ini mengunggah file teks biasa ke Google Drive dengan judul "Tes" dan Isi "Hello World".
const drive = google.drive ({ Versi: 'V3', auth: oauth2client}); const res = menunggu drive.files.create ({ requestbody: {name: 'test', mimetype: 'Text/Plain' }, media: {mimetype: 'text/polos', body: 'hello world' }});
Anda juga dapat mengunggah media dengan menentukan media.body
sebagai aliran yang dapat dibaca. Ini dapat memungkinkan Anda untuk mengunggah file yang sangat besar yang tidak dapat masuk ke dalam memori.
const fs = membutuhkan ('fs'); const drive = google.drive ({ Versi: 'V3', AUTH: OAuth2Client}); Function Async Main () { const res = menunggu drive.files.create ({requestbody: {name: 'testimage.png', mimetype: 'image/png'}, media: {mimetype: 'Image/png', body: fs.createreadstream ('mengagumkan .png ')} }); console.log (res.data);} main (). Catch (Console.Error);
Untuk lebih banyak contoh permintaan pembuatan dan modifikasi dengan lampiran media, lihat sampel samples/drive/upload.js
.
Untuk kontrol yang lebih baik atas bagaimana panggilan API Anda dilakukan, kami memberi Anda kemampuan untuk menentukan opsi tambahan yang dapat diterapkan langsung ke objek 'gaxios' yang digunakan di perpustakaan ini untuk membuat panggilan jaringan ke API.
Anda dapat menentukan opsi tambahan baik di objek google
global atau berdasarkan klien layanan. Opsi yang Anda tentukan terlampir pada objek gaxios
sehingga apa pun yang didukung gaxios
, pustaka ini mendukung. Anda juga dapat menentukan parameter permintaan global atau per-layanan yang akan dilampirkan pada semua panggilan API yang Anda buat.
Daftar lengkap opsi yang didukung dapat ditemukan di sini.
Anda dapat memilih opsi default yang akan dikirim dengan setiap permintaan. Opsi ini akan digunakan untuk setiap layanan yang dipakai oleh Google Client. Dalam contoh ini, properti timeout
GaxiosOptions
akan ditetapkan untuk setiap permintaan:
const {google} = membutuhkan ('googleapis'); google.options ({{{{{{{{{{ // Semua permintaan yang dibuat dengan objek ini akan menggunakan pengaturan ini kecuali ditimpa. Timeout: 1000, auth: auth});
Anda juga dapat memodifikasi parameter yang dikirim dengan setiap permintaan:
const {google} = membutuhkan ('googleapis'); google.options ({{{{{{{{{{ // Semua permintaan dari semua layanan akan berisi parameter kueri di atas // Kecuali jika diganti baik di klien layanan atau dalam panggilan API individual. params: {quauser: '[email protected]' }});
Anda juga dapat menentukan opsi saat membuat klien layanan.
const blogger = google.blogger ({ Versi: 'V3', // Semua permintaan yang dibuat dengan objek ini akan menggunakan auth yang ditentukan. auth: 'API Key';});
Dengan melakukan ini, setiap panggilan API yang dilakukan dengan klien layanan ini akan menggunakan 'API KEY'
untuk mengotentikasi.
Catatan: Klien yang dibuat tidak dapat diubah sehingga Anda harus membuat yang baru jika Anda ingin menentukan opsi yang berbeda.
Mirip dengan contoh di atas, Anda juga dapat memodifikasi parameter yang digunakan untuk setiap panggilan layanan yang diberikan:
const blogger = google.blogger ({ Versi: 'V3', // Semua permintaan yang dibuat dengan klien layanan ini akan berisi // Parameter kueri BlogID kecuali ditimpa dalam panggilan API individual. params: {blogId: '3213900' }}); // Panggilan dengan klien drive ini tidak akan berisi parameter kueri blogID.const drive = google.drive ('v3'); ...
Anda dapat menentukan objek auth
yang akan digunakan per permintaan. Setiap permintaan juga mewarisi opsi yang ditentukan di tingkat layanan dan tingkat global.
Misalnya:
const {google} = membutuhkan ('googleapis'); const bigQuery = google.bigQuery ('v2'); fungsi async main () { // Metode ini mencari GCLOUD_PROJECT dan google_application_credentials // Variabel Lingkungan. const auth = google.auth.googleAuth baru ({scopes: ['https://www.googleapis.com/auth/cloud-platform'] }); const authclient = menunggu auth.getClient (); const projectId = menunggu auth.getProjectId (); const request = {ProjectId, DatasetId: '<your_dataset_id>', // Ini adalah "permintaan level" opsiuth: authclient }; const res = menunggu bigquery.datasets.delete (permintaan); console.log (res.data);} main (). Catch (Console.Error);
Anda juga dapat mengganti opsi Gaxios per permintaan, seperti url
, method
, dan responseType
.
Misalnya:
const res = menunggu drive.files.export ({ FileID: 'Asxkjod9s79', // A Google Doc mimetype: 'Application/PDF'}, { // Pastikan kita mendapatkan data biner responsetype: 'stream'});
Anda dapat menggunakan variabel lingkungan berikut untuk memperkirakan permintaan HTTP dan HTTPS:
HTTP_PROXY
/ http_proxy
HTTPS_PROXY
/ https_proxy
Ketika http_proxy / http_proxy ditetapkan, mereka akan digunakan untuk proxy non-SSL permintaan yang tidak memiliki opsi konfigurasi proxy eksplisit hadir. Demikian pula, https_proxy / https_proxy akan dihormati untuk permintaan SSL yang tidak memiliki opsi konfigurasi proxy eksplisit. Ini valid untuk mendefinisikan proxy di salah satu variabel lingkungan, tetapi kemudian mengesampingkannya untuk permintaan tertentu, menggunakan opsi konfigurasi proxy.
Anda dapat secara terprogram mendapatkan daftar API yang didukung, dan semua versi yang tersedia:
const {google} = membutuhkan ('googleapis'); const apis = google.getsupportedapis ();
Ini akan mengembalikan objek dengan nama API sebagai nama properti objek, dan array string versi sebagai nilai objek;
Perpustakaan ini ditulis dalam TypeScript, dan menyediakan jenis di luar kotak. Semua kelas dan antarmuka yang dihasilkan untuk setiap API diekspor di bawah ${apiName}_${version}
namespace. Misalnya, jenis API V3 drive semua tersedia dari namespace drive_v3
:
impor { Google, // Objek tingkat atas yang digunakan untuk mengakses layanan drive_v3, // Untuk setiap klien layanan, ada namespace yang diekspor Auth, // namespace untuk tipe terkait auth Umum, // Tipe Umum yang Digunakan Di seluruh Perpustakaan} Dari 'Googleapis'; // Catatan: Menggunakan Jenis Eksplisit seperti `auth.googleauth` hanya di sini untuk // tujuan demonstrasi. Secara umum dengan naskah, tipe -tipe ini akan // disimpulkan.const auth: auth.googleAuth = google.auth.googleAuth () baru baru: drive: drive_v3.drive = google.drive ({ Versi: 'V3', auth,}); // Ada tipe yang dihasilkan untuk setiap set Parameter ParametersConsTParams: drive_v3.params $ resource $ file $ list = {}; const res = menunggu drive.files.list (listParams); // ada yang dihasilkan Jenis untuk bidang respons dan listresult Wellconst: drive_v3.schema $ fileList = res.data;
Perpustakaan ini memiliki dukungan untuk HTTP/2. Untuk mengaktifkannya, gunakan opsi http2
di mana saja parameter permintaan diterima:
const {google} = membutuhkan ('googleapis'); google.options ({{{{{{{{{{ http2: true,});
HTTP/2 seringkali lebih banyak kinerja, karena memungkinkan multiplexing beberapa permintaan bersamaan di atas satu soket. Dalam HTTP/2 API tradisional, klien bertanggung jawab secara langsung untuk membuka dan menutup sesi yang dibuat untuk membuat permintaan. Untuk mempertahankan kompatibilitas dengan API yang ada, modul ini akan secara otomatis menggunakan kembali sesi yang ada, yang dikumpulkan setelah pemalasan selama 500 ms. Sebagian besar keuntungan kinerja akan terlihat dalam beban kerja gaya batch, dan loop ketat.
Anda dapat menemukan daftar rinci perubahan besar dan fitur baru di catatan rilis kami. Jika Anda telah menggunakan pustaka ini sebelum 25.x
, lihat catatan rilis kami untuk mempelajari tentang memigrasi kode Anda dari 24.xx
ke 25.xx
Itu cukup mudah :)
Perpustakaan ini dilisensikan di bawah Apache 2.0. Teks lisensi lengkap tersedia dalam lisensi.
Kami menyukai kontribusi! Sebelum mengirimkan permintaan tarik, selalu baik untuk memulai dengan masalah baru terlebih dahulu. Untuk mempelajari lebih lanjut, lihat Kontribusi.
Ajukan pertanyaan terkait pengembangan Anda di StackOverflow.
Jika Anda telah menemukan bug/masalah, silakan ajukan di GitHub.