npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

keloola-errly-js

v0.1.2

Published

Frontend error tracking SDK for Keloola Errly

Readme

keloola-errly-js

Universal JavaScript SDK untuk frontend error tracking. Menangkap JS errors, unhandled promise rejections, dan HTTP errors di browser lalu mengirimnya ke Errly Dashboard.

  • 🪶 Ringan (~3 kB gzip), zero dependency
  • 📦 ESM + CJS + UMD — bundler-friendly atau lewat <script>
  • 🧩 Wrapper resmi: React, Vue, Next.js, Nuxt
  • 🛡️ Tidak pernah meng-crash app host — semua operasi internal dibungkus try/catch
  • 🔒 Strip data sensitif (password, token, kartu kredit) sebelum kirim

Instalasi

npm install keloola-errly-js

Atau via CDN (vanilla JS):

<script src="https://cdn.errly.app/keloola-errly-js.min.js"></script>

Quick start

import Errly from 'keloola-errly-js'

Errly.init({
  apiKey: 'proj_xxxxxxxxxxxx',          // dari dashboard Errly
  dsn: 'https://errly.yourdomain.com',  // URL dashboard
})

Setelah init(), global JS error & promise rejection otomatis tertangkap.

Env variable per framework

SDK berjalan di browser, jadi API key harus di-expose ke client — perhatikan prefix-nya:

| Framework / bundler | Akses env | Prefix wajib | | --- | --- | --- | | Vite (React/Vue + Vite) | import.meta.env.VITE_ERRLY_API_KEY | VITE_ | | Next.js | process.env.NEXT_PUBLIC_ERRLY_API_KEY | NEXT_PUBLIC_ | | Create React App | process.env.REACT_APP_ERRLY_API_KEY | REACT_APP_ | | Nuxt | useRuntimeConfig().public.errlyApiKey | via runtimeConfig | | Astro | import.meta.env.PUBLIC_ERRLY_API_KEY | PUBLIC_ |


Konfigurasi (Errly.init)

Errly.init({
  apiKey: 'proj_xxxxxxxxxxxx',  // WAJIB
  dsn: 'https://...',           // WAJIB
  environment: 'production',    // default: window.location.hostname
  release: '1.0.0',             // versi app, opsional
  sampleRate: 1.0,              // 0.0–1.0, default 1.0 (100%)
  ignoreErrors: [],             // string | RegExp yang diabaikan
  beforeSend: null,             // hook modifikasi/cancel payload (return null = cancel)
  debug: false,                 // log ke console kalau true
  maxBreadcrumbs: 20,           // default 20
  attachStacktrace: true,       // default true
})

Detail opsi

Ringkasan cepat:

| Opsi | Default | Singkatnya | | --- | --- | --- | | sampleRate | 1.0 | Berapa persen error dikirim (kontrol volume) | | ignoreErrors | [] | Buang error yang message-nya cocok | | beforeSend | null | Hook terakhir: modifikasi / batalkan payload | | debug | false | Log aktivitas SDK ke console | | maxBreadcrumbs | 20 | Jumlah jejak aksi user yang disimpan | | attachStacktrace | true | Sertakan stack trace di payload |

Penjelasan lengkap di bawah.


sampleRate — default 1.0

Membatasi berapa banyak error yang benar-benar dikirim. Berguna saat traffic tinggi supaya dashboard tidak banjir dan hemat kuota. Nilainya 0.01.0:

| Nilai | Efek | | --- | --- | | 1.0 | Kirim semua (100%) | | 0.25 | Kirim ±25%, sisanya dibuang acak | | 0 | Tidak mengirim apa pun |

Di balik layar: if (Math.random() > sampleRate) return.

Errly.init({ apiKey, dsn, sampleRate: 0.2 })  // kirim ~20% error

⚠️ Dua hal penting:

  1. Sampling per-event acak, bukan per-sesi/per-user — satu user bisa saja error pertamanya terkirim tapi error kedua tidak.
  2. Hanya berlaku di capture() (exception: JS error global, promise rejection, Errly.capture()). captureMessage() tidak terpengaruh — jadi pesan manual dan auto-capture HTTP 5xx selalu terkirim 100%.

ignoreErrors — default []

Daftar pola untuk membuang error yang message-nya cocok. Biasanya untuk noise yang tidak bisa kamu kontrol: ResizeObserver loop limit exceeded, error dari browser extension, ChunkLoadError, dll.

Errly.init({
  apiKey, dsn,
  ignoreErrors: [
    'ResizeObserver loop',   // STRING = cocok kalau message MENGANDUNG teks ini
    /^Script error\.?$/,     // REGEXP = kontrol presisi
    /ChunkLoadError/,
  ],
})
  • String → substring (message.includes(pattern)). 'Network' akan mencocokkan 'NetworkError when fetching'.
  • RegExp → pattern.test(message).

⚠️ Yang dicek hanya error.message — bukan stack trace, filename, atau URL. Berlaku di capture() dan captureMessage().


beforeSend — default null

Hook terakhir sebelum payload dikirim. Titik kontrol paling fleksibel untuk memodifikasi, memperkaya, atau membatalkan event.

Errly.init({
  apiKey, dsn,
  beforeSend: (payload) => {
    // a) Batalkan kondisional → return null
    if (payload.message.includes('ResizeObserver')) return null

    // b) Tambah/ubah data
    payload.extra = { ...payload.extra, appVersion: '1.2.3' }

    // c) Scrub manual data sensitif yang lolos
    if (payload.user?.email) payload.user.email = '[hidden]'

    return payload   // WAJIB return payload (atau objek) kalau tidak mau batal
  },
})
  • return null → event dibuang.
  • return payload / objek → di-merge ke payload via Object.assign.

⚠️ Hook ini jalan paling akhir — setelah sampleRate & ignoreErrors (yang nge-gate lebih awal) dan setelah stripSensitiveData() otomatis. Jadi yang kamu terima sudah payload final; cocok untuk scrub/enrich tambahan. Lupa return (jadi undefined) → payload tetap terkirim apa adanya.


debug — default false

Kalau true, SDK menulis log ke console: saat init, tiap kirim ([Errly] Sending payload: …), dan saat pengiriman gagal.

Errly.init({ apiKey, dsn, debug: true })
// → [Errly] Initialized. Environment: localhost
// → [Errly] Sending payload: { type: 'exception', message: '…', … }

⚠️ Matikan di production supaya payload error tidak bocor ke console user. Semua file test/*.html memang pakai debug: true.


maxBreadcrumbs — default 20

Batas jumlah "jejak" aksi user yang disimpan sebelum error (klik, navigasi, request HTTP), lalu ikut dikirim di payload untuk membantu reproduksi.

Ini ring buffer: saat penuh, breadcrumb paling lama dibuang. Jadi maxBreadcrumbs: 20 = selalu simpan 20 jejak terbaru.

Errly.init({ apiKey, dsn, maxBreadcrumbs: 50 })  // konteks lebih panjang

⚠️ Trade-off: angka lebih besar = konteks lebih kaya, tapi payload lebih berat (tiap breadcrumb punya type, message, data, timestamp). 20 umumnya cukup.


attachStacktrace — default true

Sertakan stack trace di payload — info paling berharga untuk debugging (file:line:col tempat error terjadi). Stack dipotong maksimal 10 baris pertama.

Errly.init({ apiKey, dsn, attachStacktrace: false })  // tanpa stack (lebih ringan/privat)

⚠️ Hanya berpengaruh pada exception via capture(). captureMessage() memang tidak punya stack. false membuat debugging jauh lebih susah — umumnya biarkan true.


Urutan pemrosesan satu error:

Errly.capture(err)
  → sampleRate?         drop acak (capture() saja)
  → ignoreErrors?       drop kalau message cocok
  → buildPayload()      + browser, breadcrumbs (maxBreadcrumbs), stack (attachStacktrace)
  → stripSensitiveData  redact otomatis (password/token/kartu kredit)
  → beforeSend()        modifikasi / cancel
  → transport.send()    kirim (+ log kalau debug)

Public API

| Method | Return | Singkatnya | | --- | --- | --- | | init(options) | void | Inisialisasi SDK + pasang semua handler | | capture(error, context?) | void | Kirim exception manual | | captureMessage(msg, level?) | void | Kirim pesan (bukan Error object) | | setUser(user) | void | Set identitas user di tiap payload | | clearUser() | void | Hapus identitas user | | addBreadcrumb(crumb) | void | Tambah jejak aksi manual | | flush() | Promise<void> | Tunggu semua event pending terkirim |

🛡️ Semua method aman — dibungkus try/catch, tidak pernah throw / meng-crash app. Kalau dipanggil sebelum init(), method diabaikan diam-diam (kecuali flush() yang mengembalikan Promise.resolve()).


Errly.init(options)

Inisialisasi SDK: validasi apiKey/dsn, lalu pasang semua integrasiwindow.onerror, unhandledrejection, fetch interceptor, dan breadcrumb tracker (klik & navigasi). Wajib dipanggil sekali sebelum method lain.

Errly.init({ apiKey: 'proj_xxx', dsn: 'https://errly.yourdomain.com' })

⚠️ Kalau apiKey/dsn kosong atau init gagal → muncul console.warn dan SDK jadi no-op (tidak crash). Lihat Konfigurasi untuk daftar opsi lengkap.


Errly.capture(error, context?)

Kirim exception secara manual (mis. di dalam try/catch). Argumen error boleh Error atau nilai apa pun (otomatis di-coerce jadi Error). context opsional:

Errly.capture(error, {
  level: 'error',                      // 'info' | 'warning' | 'error' | 'fatal' (default 'error')
  tags:  { section: 'checkout' },      // label pendek untuk filter di dashboard
  extra: { cartId, retryCount: 2 },    // data bebas untuk konteks
})

⚠️ Kena sampleRate & ignoreErrors (bisa di-drop). Stack ikut kalau attachStacktrace: true. Field context.user tidak dipakai — identitas user diambil dari setUser(), bukan dari sini.


Errly.captureMessage(message, level?)

Kirim pesan (bukan objek Error) — untuk log peristiwa penting yang bukan exception. level default 'error'.

Errly.captureMessage('Pembayaran ditolak gateway', 'warning')

⚠️ level di sini hanya 'info' | 'warning' | 'error'. Pesan tidak punya stack trace. Kena ignoreErrors, tapi tidak kena sampleRate (selalu terkirim).


Errly.setUser(user) / Errly.clearUser()

Set/hapus identitas user yang otomatis disertakan di setiap payload berikutnya — mempermudah melacak error per-user di dashboard.

// saat login
Errly.setUser({ id: 42, email: '[email protected]', name: 'Andi', plan: 'pro' })  // boleh atribut custom

// saat logout
Errly.clearUser()

💡 id, email, name opsional, plus atribut custom bebas. Panggil clearUser() saat logout supaya error user berikutnya tidak salah atribusi.


Errly.addBreadcrumb(crumb)

Tambah jejak manual ke timeline (selain klik/navigasi/HTTP yang sudah otomatis). Berguna menandai langkah bisnis penting sebelum error.

Errly.addBreadcrumb({
  type: 'custom',                      // 'navigation' | 'click' | 'http' | 'console' | 'custom'
  category: 'checkout',
  message: 'User klik "Bayar sekarang"',
  data: { amount: 150000 },
})

💡 timestamp terisi otomatis kalau dikosongkan. Jejak disimpan sebanyak maxBreadcrumbs terbaru (ring buffer).


Errly.flush()

Mengembalikan Promise yang resolve setelah semua event pending terkirim. Pengiriman SDK bersifat async/antrian, jadi panggil ini sebelum aksi yang bisa menghentikan halaman (redirect, logout, beforeunload).

await Errly.flush()
window.location.href = '/login'

Framework wrappers

React — keloola-errly-js/react

import Errly from 'keloola-errly-js'
import { ErrlyErrorBoundary } from 'keloola-errly-js/react'

Errly.init({
  apiKey: import.meta.env.VITE_ERRLY_API_KEY,
  dsn: import.meta.env.VITE_ERRLY_DSN,
})

function App() {
  return (
    <ErrlyErrorBoundary fallback={<p>Oops, something broke.</p>}>
      <MyApp />
    </ErrlyErrorBoundary>
  )
}

Tersedia juga HOC withErrly(Component, fallback?).

Vue — keloola-errly-js/vue

import { createApp } from 'vue'
import { ErrlyVuePlugin } from 'keloola-errly-js/vue'
import App from './App.vue'

createApp(App)
  .use(ErrlyVuePlugin, {
    apiKey: import.meta.env.VITE_ERRLY_API_KEY,
    dsn: import.meta.env.VITE_ERRLY_DSN,
  })
  .mount('#app')

Next.js (App Router, 14+) — keloola-errly-js/next

⚠️ Jangan init lewat instrumentation.ts. register() di sana hanya jalan di server (NEXT_RUNTIME = 'nodejs'/'edge', tak pernah 'browser'), sedangkan SDK ini butuh window. Init dari client component.

1. Buat client component app/errly-init.tsx:

'use client'
import { register } from 'keloola-errly-js/next'

register({
  apiKey: process.env.NEXT_PUBLIC_ERRLY_API_KEY!,
  dsn: process.env.NEXT_PUBLIC_ERRLY_DSN!,
  environment: process.env.NODE_ENV,
})

export function ErrlyInit() {
  return null
}

2. Pasang di app/layout.tsx:

import { ErrlyInit } from './errly-init'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html><body><ErrlyInit />{children}</body></html>
  )
}

3. Tangkap render error di app/global-error.tsx:

'use client'
import { useEffect } from 'react'
import Errly from 'keloola-errly-js'

export default function GlobalError({ error, reset }: { error: Error; reset: () => void }) {
  useEffect(() => { Errly.capture(error) }, [error])
  return (
    <html><body><h2>Something went wrong.</h2><button onClick={reset}>Try again</button></body></html>
  )
}

⚠️ Tanpa prefix NEXT_PUBLIC_, variable tidak tersedia di browser. 📌 Di Next 15.3+, isi register(...) boleh dipindah ke instrumentation-client.ts.

Nuxt

Salin plugin berikut ke plugins/errly.client.ts (lihat docs/integrations.md):

export default defineNuxtPlugin(() => {
  const config = useRuntimeConfig()
  Errly.init({
    apiKey: config.public.errlyApiKey as string,
    dsn: config.public.errlyDsn as string,
  })
})

nuxt.config.ts:

export default defineNuxtConfig({
  plugins: ['~/plugins/errly.client.ts'],
  runtimeConfig: {
    public: {
      errlyApiKey: process.env.NUXT_PUBLIC_ERRLY_API_KEY,
      errlyDsn: process.env.NUXT_PUBLIC_ERRLY_DSN,
    },
  },
})

Astro — keloola-errly-js/astro

Tambahkan integration di astro.config.mjs — script init otomatis ter-inject ke setiap halaman, global error & promise rejection ditangkap handler bawaan SDK:

import { defineConfig } from 'astro/config'
import { errly } from 'keloola-errly-js/astro'

export default defineConfig({
  integrations: [
    errly({
      apiKey: import.meta.env.PUBLIC_ERRLY_API_KEY,
      dsn: import.meta.env.PUBLIC_ERRLY_DSN,
    }),
  ],
})

⚠️ Astro pakai prefix PUBLIC_ untuk env yang boleh diexpose ke client.

Untuk komponen island React/Vue di dalam Astro, pakai wrapper masing-masing (keloola-errly-js/react, /vue) seperti biasa.


Development

npm install          # install deps
npm test             # jalankan unit tests (vitest)
npm run test:coverage
npm run typecheck    # tsc --noEmit
npm run build        # build dist/ + wrapper (react/vue/next)

Build menghasilkan:

  • dist/keloola-errly.js (ESM), dist/keloola-errly.cjs (CJS), dist/keloola-errly.umd.js (UMD)
  • dist/index.d.ts (type definitions)
  • dist/{react,vue,next}/ (wrapper terkompilasi — ikut di-publish lewat files: ["dist"])

Smoke-test manual di browser: npx serve test/ lalu buka test/manual.html.


Lisensi

MIT