web-stability-observer 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # app-runtime-metrics
2
+
3
+ [![npm version](https://img.shields.io/npm/v/app-runtime-metrics.svg?style=flat-square)](https://www.npmjs.com/package/app-runtime-metrics)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](https://opensource.org/licenses/MIT)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg?style=flat-square)](https://www.typescriptlang.org/)
6
+ [![Size](https://img.shields.io/badge/size-2kb-green.svg?style=flat-square)]()
7
+
8
+ 🚀 **Ultra-lightweight** runtime performance and stability monitoring SDK for modern web applications (Vue 3, React, Svelte, etc.).
9
+
10
+ Unlike traditional monitoring tools that only track network requests or FCP/LCP, **App Runtime Metrics** focuses on the **Stability Entropy** of your application's lifecycle, helping you detect memory leaks and event loop jitter in long-running sessions.
11
+
12
+ ## ✨ Features
13
+
14
+ - **📊 Real-time Memory Tracking**: Monitors Heap usage trends to detect potential leaks.
15
+ - **🛡️ Runtime Entropy Analysis**: Uses a lightweight heuristic algorithm to calculate a "Stability Score" (0-1) based on JS event loop jitter.
16
+ - **⚡ Zero Dependencies**: Built with vanilla TypeScript, no bloat.
17
+ - **🌲 Tree-shaking Friendly**: Fully compatible with Vite, Webpack, and Rollup.
18
+ - **🕵️ Production Optimized**: Auto-detects environment to minimize overhead during development.
19
+
20
+ ## 📦 Installation
21
+
22
+ ```bash
23
+ # using npm
24
+ npm install app-runtime-metrics
25
+
26
+ # using yarn
27
+ yarn add app-runtime-metrics
28
+
29
+ # using pnpm
30
+ pnpm add app-runtime-metrics
package/dist/cls.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ export declare class CLSMonitor {
2
+ private value;
3
+ private entries;
4
+ constructor();
5
+ private init;
6
+ getCLS(): {
7
+ value: number;
8
+ entries: any[];
9
+ };
10
+ }
@@ -0,0 +1,11 @@
1
+ import { ErrorLog, ResourceErrorLog } from './types';
2
+ export declare class ErrorMonitor {
3
+ private jsErrors;
4
+ private resourceErrors;
5
+ constructor();
6
+ private init;
7
+ getErrors(): {
8
+ jsErrors: ErrorLog[];
9
+ resourceErrors: ResourceErrorLog[];
10
+ };
11
+ }
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * web-stability-observer v1.0.1
3
+ * (c) 2024-2025 Vue Community Contributor
4
+ * Released under the MIT License.
5
+ */
6
+ "use strict";class e{constructor(){this.value=0,this.entries=[],this.init()}init(){if("undefined"==typeof PerformanceObserver)return;new PerformanceObserver(e=>{for(const r of e.getEntries())r.hadRecentInput||(this.value+=r.value,this.entries.push(r))}).observe({type:"layout-shift",buffered:!0})}getCLS(){return{value:this.value,entries:this.entries}}}class r{constructor(){this.jsErrors=[],this.resourceErrors=[],this.init()}init(){window.addEventListener("error",e=>{var r,s;const t=e.target;!t||"IMG"!==t.tagName&&"SCRIPT"!==t.tagName&&"LINK"!==t.tagName?this.jsErrors.push({type:(null===(r=e.error)||void 0===r?void 0:r.name)||"Unknown",message:e.message,stack:null===(s=e.error)||void 0===s?void 0:s.stack}):this.resourceErrors.push({type:"ResourceError",src:t.src||t.href,tagName:t.tagName})},!0),window.addEventListener("unhandledrejection",e=>{var r,s;this.jsErrors.push({type:"UnhandledRejection",message:(null===(r=e.reason)||void 0===r?void 0:r.message)||e.reason+"",stack:null===(s=e.reason)||void 0===s?void 0:s.stack})})}getErrors(){return{jsErrors:this.jsErrors,resourceErrors:this.resourceErrors}}}console.log("WebStabilityObserver module loaded"),module.exports=class{constructor(s){this.config=Object.assign({threshold:.1,autoReport:!0,enableErrorTracking:!0},s),this.clsMonitor=new e,this.errorMonitor=new r,this.registerLifecycle()}analyze(){const{value:e,entries:r}=this.clsMonitor.getCLS(),{jsErrors:s,resourceErrors:t}=this.errorMonitor.getErrors();return{cls:e,clsEntries:r,jsErrors:s,resourceErrors:t,isStable:e<=(this.config.threshold||.1)&&0===s.length&&0===t.length,timestamp:Date.now(),url:window.location.href}}registerLifecycle(){this.config.autoReport&&this.config.reportUrl&&document.addEventListener("visibilitychange",()=>{if(console.log("visibilitychange"),"hidden"===document.visibilityState){const e=this.analyze();((e,r)=>{if(navigator.sendBeacon){const s=new Blob([JSON.stringify(r)],{type:"application/json; charset=UTF-8"});navigator.sendBeacon(e,s)}else fetch(e,{method:"POST",body:JSON.stringify(r),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(console.error)})(this.config.reportUrl,e)}})}};
@@ -0,0 +1,10 @@
1
+ import { StabilityConfig, StabilityData } from './types';
2
+ declare class WebStabilityObserver {
3
+ private clsMonitor;
4
+ private errorMonitor;
5
+ private config;
6
+ constructor(options?: StabilityConfig);
7
+ analyze(): StabilityData;
8
+ private registerLifecycle;
9
+ }
10
+ export default WebStabilityObserver;
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * web-stability-observer v1.0.1
3
+ * (c) 2024-2025 Vue Community Contributor
4
+ * Released under the MIT License.
5
+ */
6
+ class e{constructor(){this.value=0,this.entries=[],this.init()}init(){if("undefined"==typeof PerformanceObserver)return;new PerformanceObserver(e=>{for(const r of e.getEntries())r.hadRecentInput||(this.value+=r.value,this.entries.push(r))}).observe({type:"layout-shift",buffered:!0})}getCLS(){return{value:this.value,entries:this.entries}}}class r{constructor(){this.jsErrors=[],this.resourceErrors=[],this.init()}init(){window.addEventListener("error",e=>{var r,s;const t=e.target;!t||"IMG"!==t.tagName&&"SCRIPT"!==t.tagName&&"LINK"!==t.tagName?this.jsErrors.push({type:(null===(r=e.error)||void 0===r?void 0:r.name)||"Unknown",message:e.message,stack:null===(s=e.error)||void 0===s?void 0:s.stack}):this.resourceErrors.push({type:"ResourceError",src:t.src||t.href,tagName:t.tagName})},!0),window.addEventListener("unhandledrejection",e=>{var r,s;this.jsErrors.push({type:"UnhandledRejection",message:(null===(r=e.reason)||void 0===r?void 0:r.message)||e.reason+"",stack:null===(s=e.reason)||void 0===s?void 0:s.stack})})}getErrors(){return{jsErrors:this.jsErrors,resourceErrors:this.resourceErrors}}}class s{constructor(s){this.config=Object.assign({threshold:.1,autoReport:!0,enableErrorTracking:!0},s),this.clsMonitor=new e,this.errorMonitor=new r,this.registerLifecycle()}analyze(){const{value:e,entries:r}=this.clsMonitor.getCLS(),{jsErrors:s,resourceErrors:t}=this.errorMonitor.getErrors();return{cls:e,clsEntries:r,jsErrors:s,resourceErrors:t,isStable:e<=(this.config.threshold||.1)&&0===s.length&&0===t.length,timestamp:Date.now(),url:window.location.href}}registerLifecycle(){this.config.autoReport&&this.config.reportUrl&&document.addEventListener("visibilitychange",()=>{if(console.log("visibilitychange"),"hidden"===document.visibilityState){const e=this.analyze();((e,r)=>{if(navigator.sendBeacon){const s=new Blob([JSON.stringify(r)],{type:"application/json; charset=UTF-8"});navigator.sendBeacon(e,s)}else fetch(e,{method:"POST",body:JSON.stringify(r),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(console.error)})(this.config.reportUrl,e)}})}}console.log("WebStabilityObserver module loaded");export{s as default};
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * web-stability-observer v1.0.1
3
+ * (c) 2024-2025 Vue Community Contributor
4
+ * Released under the MIT License.
5
+ */
6
+ !function(e,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):(e="undefined"!=typeof globalThis?globalThis:e||self).SessionTracker=r()}(this,function(){"use strict";class e{constructor(){this.value=0,this.entries=[],this.init()}init(){"undefined"!=typeof PerformanceObserver&&new PerformanceObserver(e=>{for(const r of e.getEntries())r.hadRecentInput||(this.value+=r.value,this.entries.push(r))}).observe({type:"layout-shift",buffered:!0})}getCLS(){return{value:this.value,entries:this.entries}}}class r{constructor(){this.jsErrors=[],this.resourceErrors=[],this.init()}init(){window.addEventListener("error",e=>{var r,s;const t=e.target;!t||"IMG"!==t.tagName&&"SCRIPT"!==t.tagName&&"LINK"!==t.tagName?this.jsErrors.push({type:(null===(r=e.error)||void 0===r?void 0:r.name)||"Unknown",message:e.message,stack:null===(s=e.error)||void 0===s?void 0:s.stack}):this.resourceErrors.push({type:"ResourceError",src:t.src||t.href,tagName:t.tagName})},!0),window.addEventListener("unhandledrejection",e=>{var r,s;this.jsErrors.push({type:"UnhandledRejection",message:(null===(r=e.reason)||void 0===r?void 0:r.message)||e.reason+"",stack:null===(s=e.reason)||void 0===s?void 0:s.stack})})}getErrors(){return{jsErrors:this.jsErrors,resourceErrors:this.resourceErrors}}}return console.log("WebStabilityObserver module loaded"),class{constructor(s){this.config=Object.assign({threshold:.1,autoReport:!0,enableErrorTracking:!0},s),this.clsMonitor=new e,this.errorMonitor=new r,this.registerLifecycle()}analyze(){const{value:e,entries:r}=this.clsMonitor.getCLS(),{jsErrors:s,resourceErrors:t}=this.errorMonitor.getErrors();return{cls:e,clsEntries:r,jsErrors:s,resourceErrors:t,isStable:e<=(this.config.threshold||.1)&&0===s.length&&0===t.length,timestamp:Date.now(),url:window.location.href}}registerLifecycle(){this.config.autoReport&&this.config.reportUrl&&document.addEventListener("visibilitychange",()=>{if(console.log("visibilitychange"),"hidden"===document.visibilityState){const e=this.analyze();((e,r)=>{if(navigator.sendBeacon){const s=new Blob([JSON.stringify(r)],{type:"application/json; charset=UTF-8"});navigator.sendBeacon(e,s)}else fetch(e,{method:"POST",body:JSON.stringify(r),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(console.error)})(this.config.reportUrl,e)}})}}});
@@ -0,0 +1,2 @@
1
+ import { StabilityData } from './types';
2
+ export declare const reportData: (url: string, data: StabilityData) => void;
@@ -0,0 +1,25 @@
1
+ export interface StabilityConfig {
2
+ reportUrl?: string;
3
+ autoReport?: boolean;
4
+ threshold?: number;
5
+ enableErrorTracking?: boolean;
6
+ }
7
+ export interface StabilityData {
8
+ cls: number;
9
+ clsEntries: any[];
10
+ jsErrors: ErrorLog[];
11
+ resourceErrors: ResourceErrorLog[];
12
+ isStable: boolean;
13
+ timestamp: number;
14
+ url: string;
15
+ }
16
+ export interface ErrorLog {
17
+ message: string;
18
+ stack?: string;
19
+ type: 'ReferenceError' | 'TypeError' | 'UnhandledRejection' | 'Unknown';
20
+ }
21
+ export interface ResourceErrorLog {
22
+ src: string;
23
+ tagName: string;
24
+ type: 'ResourceError';
25
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Web Vitals Collector
3
+ * Captures FCP and LCP metrics using PerformanceObserver API.
4
+ */
5
+ export interface VitalsData {
6
+ fcp: number;
7
+ lcp: number;
8
+ }
9
+ export declare function initVitals(): void;
10
+ export declare function getVitals(): VitalsData;
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "web-stability-observer",
3
+ "version": "1.0.2",
4
+ "description": "Lightweight Web Vitals and error tracking plugin for Vue 3 applications.",
5
+ "main": "dist/index.cjs.js",
6
+ "module": "dist/index.esm.js",
7
+ "types": "dist/index.d.ts",
8
+ "type": "module",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "rollup -c"
14
+ },
15
+ "keywords": [
16
+ "vue",
17
+ "vue3",
18
+ "performance",
19
+ "vitals",
20
+ "monitor",
21
+ "analytics"
22
+ ],
23
+ "peerDependencies": {
24
+ "vue": "^3.0.0"
25
+ },
26
+ "author": "Vue Community Contributor",
27
+ "license": "MIT",
28
+ "devDependencies": {
29
+ "@rollup/plugin-commonjs": "^29.0.0",
30
+ "@rollup/plugin-json": "^6.1.0",
31
+ "@rollup/plugin-node-resolve": "^16.0.3",
32
+ "@rollup/plugin-terser": "^0.4.4",
33
+ "@rollup/plugin-typescript": "^12.3.0",
34
+ "rollup": "^4.53.3",
35
+ "tslib": "^2.8.1",
36
+ "typescript": "^5.9.3"
37
+ }
38
+ }