@yukino.js/sentry
v0.0.1
Published
Framework-agnostic sentry sdk with first-class support for react and vue3+
Downloads
165
Maintainers
Readme
@yukino.js/sentry
@yukino.js/sentry is a browser monitoring and analytics SDK for page views, declarative clicks, runtime errors, resource errors, HTTP requests, performance metrics, exposure tracking, white-screen detection, offline reporting, and screen record context.
The core entry is framework agnostic. React and Vue integrations are published as dedicated subpath exports so non-framework users do not load framework dependencies.
Installation
npm install @yukino.js/sentryReact and Vue are optional peer dependencies. Install them only when the matching integration is used.
npm install reactnpm install vuePackage Exports
import { init, destroy, isInitialized, enablePlugin } from "@yukino.js/sentry";
import {
PerformancePlugin,
ScreenRecordPlugin,
ExposurePlugin,
} from "@yukino.js/sentry/plugins";
import { ReactErrorBoundary } from "@yukino.js/sentry/react";
import { vuePlugin } from "@yukino.js/sentry/vue";Each public export provides ESM, CJS, and TypeScript declaration files.
Quick Start
import { init, enablePlugin } from "@yukino.js/sentry";
import {
PerformancePlugin,
ScreenRecordPlugin,
ExposurePlugin,
} from "@yukino.js/sentry/plugins";
init({
dsn: "/api/log",
projectId: "frontend-app",
userId: "anonymous",
});
enablePlugin(
new PerformancePlugin(),
new ScreenRecordPlugin(),
new ExposurePlugin(),
);dsn must be a non-empty string. If dsn is empty, initialization is rejected.
Lifecycle APIs
init
init(options) validates the input with zod, merges it with default options, writes runtime configuration, installs capture listeners, starts page-view lifecycle tracking, and initializes visitor identity when enabled.
import { init } from "@yukino.js/sentry";
init({
dsn: "/api/log",
projectId: "checkout-web",
userId: "user-001",
enableFetch: true,
enableXhr: true,
enableClick: true,
});destroy
destroy() cleans plugin instances, event subscriptions, browser listeners, and decorated global methods, and resets per-session state (breadcrumbs, error deduplication, queued reports).
import { destroy } from "@yukino.js/sentry";
destroy();Use it when resetting tests, unloading a micro-frontend, or dynamically disabling monitoring.
isInitialized
import { init, isInitialized } from "@yukino.js/sentry";
if (!isInitialized()) {
init({ dsn: "/api/log" });
}Configuration
init accepts partial options. Values not provided by the caller use SDK defaults.
| Option | Type | Default | Description |
| --------------------------- | ---------------------- | --------------------------------------------------- | ------------------------------------------------------ |
| dsn | string | "" | Report endpoint. Required for initialization. |
| projectId | string | "unknown" | Frontend project identifier. |
| userId | string | "unknown" | Current user identifier. |
| disabled | boolean | false | Disable the SDK. |
| enableXhr | boolean | true | Capture XMLHttpRequest requests. |
| enableFetch | boolean | true | Capture fetch requests. |
| enableClick | boolean | true | Capture declarative click events. |
| enableError | boolean | true | Capture runtime and resource errors. |
| enableUnhandledRejection | boolean | true | Capture unhandled promise rejections. |
| enableHashChange | boolean | true | Capture hash navigation. |
| enableHistory | boolean | true | Capture history navigation. |
| enableWhiteScreen | boolean | true | Enable white-screen detection. |
| enableFingerprint | boolean | false | Enable FingerprintJS anonymous visitor identity. |
| anonymousId | string | "unknown" | SDK-generated anonymous visitor id. |
| visitorId | string | "unknown" | Backend-bound visitor id. |
| screenRecordDurationMs | number | 3000 | Rolling screen record window length. |
| screenRecordEventTypes | EventType[] | [Error, Xhr, Fetch, Resource, UnhandledRejection] | Event types that trigger screen record reporting. |
| hasSkeleton | boolean | false | Whether the page has a skeleton screen. |
| rootCssSelectors | string[] | ["html", "body", "#app", "#root"] | Root selectors used by white-screen detection. |
| clickThrottleDelay | number | 0 | Click capture throttle delay in milliseconds. |
| maxBreadcrumbs | number | 30 | Breadcrumb capacity. |
| repeatCodeError | boolean | false | Report duplicate code errors. |
| enableHttpPerformance | boolean | false | Report successful HTTP requests as performance events. |
| ignoreErrors | (string \| RegExp)[] | [] | Runtime error ignore rules. |
| excludeAPIs | (string \| RegExp)[] | [] | HTTP request ignore rules. |
| cacheMaxLength | number | 10 | Maximum batch size. |
| cacheWaitingTime | number | 2000 | Batch wait time in milliseconds. |
| maxQueueLength | number | 200 | Maximum queued events while offline or retrying. |
| retryIntervalMilliseconds | number | 60000 | Server recovery probe interval. |
| offlineCacheKey | string | "yukino_sentry_offline_cache" | localStorage key for offline cache. |
| tracesSampleRate | number | 1 | Sampling rate from 0 to 1. |
| beforeBreadcrumb | function | undefined | Hook before storing a breadcrumb. |
| beforeSend | function | undefined | Hook before one event enters Reporter queue. |
| beforeSendBatch | function | undefined | Hook before a batch enters transport. |
| afterSend | function | undefined | Hook after a batch enters transport successfully. |
Example production configuration:
import { init } from "@yukino.js/sentry";
init({
dsn: "https://example.com/api/log",
projectId: "production-web",
userId: "unknown",
enableFingerprint: true,
enableHttpPerformance: true,
tracesSampleRate: 1,
excludeAPIs: ["https://example.com/api/log"],
ignoreErrors: [/ResizeObserver loop limit exceeded/],
});Report Data
Reporter sends IReportData objects to the configured dsn.
| Field | Description |
| ------------- | ------------------------------------- |
| id | Reporter instance id. |
| type | Event type. |
| name | Event name. |
| message | Event message. |
| status | OK or Error. |
| time | Formatted time. |
| timestamp | Numeric timestamp. |
| url | Current page URL. |
| userId | User identifier. |
| anonymousId | FingerprintJS anonymous visitor id. |
| visitorId | Backend-bound visitor id. |
| projectId | Project identifier. |
| sdkVersion | SDK version. |
| deviceInfo | Device, browser, OS, and screen data. |
| payload | Original event payload. |
Event Types
The SDK can report the following event categories:
| Type | Description |
| -------------------------- | ------------------------------- |
| XMLHttpRequest | XHR request. |
| fetch | fetch request. |
| Click | Declarative click. |
| Event hashchange | Hash navigation. |
| History | History navigation. |
| Resource | Static resource load failure. |
| Event unhandledrejection | Unhandled promise rejection. |
| Error | JavaScript runtime error. |
| Vue | Vue error. |
| React | React error. |
| Performance | Performance metric. |
| ScreenRecord | Screen record payload. |
| Exposure | Exposure duration event. |
| WhiteScreen | White-screen event. |
| Custom | Custom business event. |
| PV | Page view and dwell-time event. |
Error Capture
The SDK captures:
windowerrorevents.- Static resource load errors.
console.errorerror objects or error text.- Unhandled promise rejections.
- React ErrorBoundary errors.
- Vue
app.config.errorHandlererrors.
Duplicate code errors are deduplicated by default. Enable duplicate reporting with:
init({
dsn: "/api/log",
repeatCodeError: true,
});Ignore known noise:
init({
dsn: "/api/log",
ignoreErrors: ["Script error.", /ResizeObserver loop limit exceeded/],
});HTTP Capture
The SDK decorates XMLHttpRequest.prototype.open, XMLHttpRequest.prototype.send, and globalThis.fetch. It captures method, URL, status code, elapsed time, request data, response data, and Server-Timing.
Status classification:
| Status code | SDK status |
| ------------ | ---------- |
| 100 to 399 | OK |
| 400 to 599 | Error |
| Other values | Error |
Exclude report endpoints or health checks:
init({
dsn: "/api/log",
excludeAPIs: ["/api/log", /\/health$/],
});Report successful HTTP requests as performance events:
init({
dsn: "/api/log",
enableHttpPerformance: true,
});Page Views and Dwell Time
The SDK reports an initial PageLoad PV event during initialization.
On hash or history route changes, it:
- Deduplicates unchanged URLs.
- Reports
PageDwellfor the previous page. - Reports a new PV for the next page.
- Flushes current dwell time on
pagehidewhen possible.
Dwell time less than or equal to 100 ms is ignored to reduce noise.
Manual page-view reporting:
import { tracePageView } from "@yukino.js/sentry";
tracePageView({
name: "ProductDetail",
message: location.href,
extra: {
productId: "sku-001",
},
});Declarative Clicks
Declarative click tracking uses yukino-sentry-* attributes. Plain clicks are not reported unless the clicked element or one of its composed path ancestors has a tracking attribute.
<section yukino-sentry-view="profile-card" yukino-sentry-src="home">
<button yukino-sentry-ev="save-profile" yukino-sentry-msg="Save">Save</button>
</section>Reserved attributes:
| Attribute | Description |
| -------------------- | ------------------------------ |
| yukino-sentry-ev | Explicit event ID. |
| yukino-sentry-msg | Human-readable message. |
| yukino-sentry-view | View ID and event ID fallback. |
Custom yukino-sentry-* attributes become params.
<a
yukino-sentry-ev="open-banner"
yukino-sentry-msg="Open campaign banner"
yukino-sentry-campaign="spring"
yukino-sentry-rank="1"
>
Campaign
</a>The reported click payload (DeclarativeClickData) includes:
| Field | Type | Description |
| ---------------- | ------------------------------------------ | -------------------------------------------------------------------------------- |
| ev | string | Event ID (from yukino-sentry-ev, title, yukino-sentry-view, or tag). |
| msg | string | Human-readable message (from yukino-sentry-msg, text, aria-label, or tag). |
| triggerPageUrl | string | Current page URL (location.href). |
| x | number | Click X coordinate (element offset + scroll offset). |
| y | number | Click Y coordinate (element offset + scroll offset). |
| params | Readonly<Record<string, string \| null>> | Custom yukino-sentry-* attributes (excluding reserved keys). |
| elementPath | string | CSS-selector-like ancestor path (nearest 5 levels with id/class, max 128 chars). |
| triggerTime | number | Date.now() at click time. |
White-Screen Detection
White-screen detection samples viewport points every second after the page is ready and checks whether those points still resolve to configured root elements. Sampling stops as soon as real content is observed; a white screen is reported only when the page stays blank for 10 consecutive samples. In skeleton mode the first sample records a baseline of on-screen selectors, and a report is sent only when the skeleton never transitions to different content within the sampling window.
Default root selectors:
["html", "body", "#app", "#root"];init({
dsn: "/api/log",
enableWhiteScreen: true,
rootCssSelectors: ["html", "body", "#app"],
});Skeleton-screen pages can enable skeleton mode:
init({
dsn: "/api/log",
enableWhiteScreen: true,
hasSkeleton: true,
});Visitor Identity
The SDK tracks three identity values:
| Field | Description |
| ------------- | ------------------------------------------------------------------- |
| anonymousId | Anonymous visitor id generated by FingerprintJS and stored locally. |
| visitorId | Backend-bound visitor id. |
| userId | Current logged-in user id. |
Enable FingerprintJS:
import { getIdentity, init } from "@yukino.js/sentry";
init({
dsn: "/api/log",
enableFingerprint: true,
});
console.log(getIdentity());Update user and visitor ids:
import { setUserId, setVisitorId } from "@yukino.js/sentry";
setUserId("user-001");
setVisitorId("visitor-001");Read identity:
import { getIdentity } from "@yukino.js/sentry";
const identity = getIdentity();anonymousId is stored in localStorage with the key yukino_sentry_anonymous_id.
Reporter
Reporter is the unified data outlet. It transforms captured payloads into report data and sends batches to dsn.
Reporter behavior:
- Batches events.
- Applies sampling through
tracesSampleRate. - Persists offline events to localStorage.
- Flushes cached events after network recovery.
- Probes server recovery with HEAD requests after failed fetch reports.
- Avoids concurrent flush races with an
isFlushingguard. - Supports
sendBeaconand fetch transports.
Transport priority:
- Use
navigator.sendBeaconfor batches up to 60 KB. - Use fetch POST as the fallback, with
keepaliveenabled for bodies up to 60 KB.
Flush offline cache manually:
import { flushOfflineCache } from "@yukino.js/sentry";
await flushOfflineCache();Reporter Hooks
Register hooks after initialization or provide equivalent hooks in init options.
import { afterSend, beforeSend, beforeSendBatch } from "@yukino.js/sentry";
beforeSend((data) => {
if (data.type === "Click") {
return false;
}
return data;
});
beforeSendBatch((eventList) => {
return eventList.filter((item) => item.status !== "OK");
});
afterSend((eventList) => {
console.log("reported", eventList.length);
});Equivalent initialization form:
init({
dsn: "/api/log",
beforeSend(data) {
return data;
},
beforeSendBatch(eventList) {
return eventList;
},
afterSend(eventList) {
console.log(eventList.length);
},
});Manual APIs
traceError
import { traceError } from "@yukino.js/sentry";
try {
throw new Error("Unexpected state");
} catch (error) {
traceError(error);
}tracePerformance
import { tracePerformance } from "@yukino.js/sentry";
tracePerformance({
name: "SearchLatency",
message: "/api/search",
value: 128,
});traceCustomEvent
import { traceCustomEvent } from "@yukino.js/sentry";
traceCustomEvent({
name: "CheckoutSuccess",
message: "Submit order",
extra: {
orderId: "order-001",
},
});tracePageView
import { tracePageView } from "@yukino.js/sentry";
tracePageView({
name: "ManualPageView",
message: location.href,
});Plugin System
Plugins extend the SDK without coupling optional capabilities to the core entry. A plugin class extends SentryPlugin, implements init, and can implement destroy for cleanup.
import { enablePlugin } from "@yukino.js/sentry";
import { PerformancePlugin } from "@yukino.js/sentry/plugins";
enablePlugin(new PerformancePlugin());Enabled plugins are stored in the plugin registry. destroy() calls each plugin's destroy() method when available.
PerformancePlugin
import { enablePlugin } from "@yukino.js/sentry";
import { PerformancePlugin } from "@yukino.js/sentry/plugins";
enablePlugin(new PerformancePlugin());The plugin collects:
- Web Vitals.
- Navigation Timing page-load metrics.
- Resource Timing metrics.
- Long Task entries.
- Fallback resource-element timing for dynamically inserted resources.
performance.measureUserAgentSpecificMemorywhen supported.
Unsupported browser capabilities are skipped safely.
ScreenRecordPlugin
import { enablePlugin } from "@yukino.js/sentry";
import {
ScreenRecordPlugin,
unzipScreenRecord,
} from "@yukino.js/sentry/plugins";
enablePlugin(new ScreenRecordPlugin());
// With custom options
enablePlugin(new ScreenRecordPlugin({ durationMs: 5000 }));Screen recording is based on rrweb. The plugin keeps a rolling record window. When selected error or network events occur, the recent record window is reported as a ScreenRecord event.
Decode a record payload (async — pako is loaded on demand):
const events = await unzipScreenRecord(recordPayload);ExposurePlugin
import { enablePlugin } from "@yukino.js/sentry";
import { ExposurePlugin } from "@yukino.js/sentry/plugins";
const exposure = new ExposurePlugin();
enablePlugin(exposure);Observe one element:
const element = document.querySelector("#banner");
if (element) {
exposure.observe({
target: element,
threshold: 0.5,
params: {
bannerId: "spring-001",
},
});
}Observe multiple elements:
const first = document.querySelector("#first");
const second = document.querySelector("#second");
if (first && second) {
exposure.observe([
{
target: first,
threshold: 0.5,
params: { position: "first" },
},
{
target: second,
threshold: 0.75,
params: { position: "second" },
},
]);
}Cancel observation:
exposure.unobserve(element);
exposure.unobserve([first, second]);Exposure events are reported when an observed element leaves the viewport after becoming visible. The payload contains threshold, observe time, show time, show end time, duration, and user params.
React Integration
import { init } from "@yukino.js/sentry";
import { ReactErrorBoundary } from "@yukino.js/sentry/react";
init({ dsn: "/api/log" });
export function App() {
return (
<ReactErrorBoundary fallback={<div>Something went wrong</div>}>
<Page />
</ReactErrorBoundary>
);
}fallback can also be a function:
<ReactErrorBoundary
fallback={(error, errorInfo) => (
<div>
{error.message}
{errorInfo.componentStack}
</div>
)}
>
<Page />
</ReactErrorBoundary>The boundary reports React events with the error, stack, and React ErrorInfo.
Vue 3 Integration
import { createApp } from "vue";
import { vuePlugin } from "@yukino.js/sentry/vue";
import App from "./app.vue";
const app = createApp(App);
app.use(vuePlugin, {
dsn: "/api/log",
projectId: "vue-app",
});
app.mount("#app");The plugin installs app.config.errorHandler, reports Vue events, and then calls the previous error handler if one existed.
Vite Dev-Server Plugin
The SDK provides a Vite plugin that creates a mock report endpoint during development, writing reported data to log files instead of sending it to a real server.
// vite.config.ts
import { defineConfig } from "vite";
import { sentryPlugin } from "@yukino.js/sentry/vite";
export default defineConfig({
// `dsn` should match the @yukino.js/sentry `init({ dsn: "/api/log" })` dsn value
plugins: [sentryPlugin({ dsn: "/api/log" })],
});Available Exports
| Export | Vite Version | Description |
| --------------- | ------------ | --------------------------------------- |
| sentryPlugin | Vite 8 | Default export. For current Vite. |
| sentryPlugin7 | Vite 7 | For projects using Vite 7 specifically. |
Both factories take an optional options object.
Options
| Option | Type | Default | Description |
| ------ | -------- | ----------- | ---------------------------------------- |
| dsn | string | "/sentry" | The URL path to intercept POST requests. |
The plugin only activates for the dev server (apply: "serve"), creates a logs/ directory in process.cwd(), writes a timestamped JSONL log file (sentry_YYYYMMDDHHMMSS.jsonl), enriches error records with original source positions resolved from the dev server's module graph source maps, and returns { code: 0, message: "success" }.
Webpack Dev-Server Plugin
The @yukino.js/sentry/webpack subpath provides the same mock report endpoint for webpack-dev-server, plus source map resolution based on emitted .map assets.
// webpack.config.mjs
import { sentryPlugin } from "@yukino.js/sentry/webpack";
export default {
plugins: [sentryPlugin({ dsn: "/api/log" })],
devServer: {
// ...
},
};| Export | Description |
| --------------------- | ----------------------------------------------------------------------------- |
| sentryPlugin | Factory returning a SentryWebpackPlugin instance. Default export. |
| SentryWebpackPlugin | Webpack plugin class. No-op unless compiler.options.devServer exists. |
| sentryMiddleware | Connect/express-style middleware for manual mounting (no source map support). |
Browser Compatibility
sendBeaconis preferred for batches up to 60 KB;fetchPOST is the fallback, usingkeepaliveonly for bodies up to 60 KB.PerformanceObserverpowers Web Vitals, long task, and resource timing when available.MutationObserveris used as a fallback for dynamically inserted resources.IntersectionObserveris required byExposurePlugin.performance.measureUserAgentSpecificMemoryis optional.@rrweb/recordis used only by the screen record plugin.
Quality Gates
pnpm exec eslint ./sentry --quiet --ext .js,.jsx,.ts,.tsx
pnpm --filter @yukino.js/sentry typecheck
pnpm --filter @yukino.js/sentry test
pnpm test:coverage
pnpm buildCoverage thresholds are 70 for lines, functions, branches, and statements.
