diff --git a/.claude/eslint-rules/prefer-dynamic-import-with-feature-guard.js b/.claude/eslint-rules/prefer-dynamic-import-with-feature-guard.js
new file mode 100644
index 0000000..43a1471
--- /dev/null
+++ b/.claude/eslint-rules/prefer-dynamic-import-with-feature-guard.js
@@ -0,0 +1,68 @@
+/**
+ * ESLint rule: prefer-dynamic-import-with-feature-guard
+ *
+ * Warns when a file has both static non-framework imports and a browser feature
+ * detection guard (e.g. `if (!navigator.xxx) return`), which suggests the
+ * import should be dynamic to avoid loading the module when the feature is unavailable.
+ */
+
+const FRAMEWORK_PACKAGES = /^(react|react-dom|next|next-cloudinary|nextra)($|\/)/
+
+module.exports = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description:
+ 'Prefer dynamic imports when the module is only used inside a browser feature guard.',
+ },
+ messages: {
+ preferDynamic:
+ 'Static import "{{ source }}" may be loaded unnecessarily. ' +
+ 'Consider a dynamic import inside the feature guard (e.g. `if (!navigator.xxx) return`).',
+ },
+ },
+
+ create(context) {
+ const projectImports = []
+ let hasNavigatorGuard = false
+
+ function isNavigatorGuard(node) {
+ // Matches: !navigator.foo or !navigator?.foo
+ if (node.type !== 'UnaryExpression' || node.operator !== '!') return false
+ const arg = node.argument
+ if (arg.type === 'MemberExpression') {
+ return arg.object?.name === 'navigator'
+ }
+ if (arg.type === 'ChainExpression') {
+ return arg.expression?.object?.name === 'navigator'
+ }
+ return false
+ }
+
+ return {
+ ImportDeclaration(node) {
+ if (!FRAMEWORK_PACKAGES.test(node.source.value)) {
+ projectImports.push(node)
+ }
+ },
+
+ IfStatement(node) {
+ if (isNavigatorGuard(node.test)) {
+ hasNavigatorGuard = true
+ }
+ },
+
+ 'Program:exit'() {
+ if (hasNavigatorGuard && projectImports.length > 0) {
+ projectImports.forEach((imp) => {
+ context.report({
+ node: imp,
+ messageId: 'preferDynamic',
+ data: { source: imp.source.value },
+ })
+ })
+ }
+ },
+ }
+ },
+}
diff --git a/.claude/eslint.config.cjs b/.claude/eslint.config.cjs
new file mode 100644
index 0000000..548dbef
--- /dev/null
+++ b/.claude/eslint.config.cjs
@@ -0,0 +1,23 @@
+const preferDynamicImport = require('./eslint-rules/prefer-dynamic-import-with-feature-guard.js')
+
+module.exports = [
+ {
+ plugins: {
+ local: {
+ rules: {
+ 'prefer-dynamic-import-with-feature-guard': preferDynamicImport,
+ },
+ },
+ },
+ rules: {
+ 'local/prefer-dynamic-import-with-feature-guard': 'warn',
+ },
+ languageOptions: {
+ ecmaVersion: 2020,
+ sourceType: 'module',
+ parserOptions: {
+ ecmaFeatures: { jsx: true },
+ },
+ },
+ },
+]
diff --git a/.claude/hooks/eslint-check.sh b/.claude/hooks/eslint-check.sh
new file mode 100755
index 0000000..a5fd085
--- /dev/null
+++ b/.claude/hooks/eslint-check.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+# PostToolUse hook: runs ESLint with the prefer-dynamic-import-with-feature-guard
+# rule on any JS/JSX file that Claude just wrote or edited.
+
+INPUT=$(cat)
+
+FILE_PATH=$(echo "$INPUT" | node -e "
+ process.stdin.setEncoding('utf8')
+ let data = ''
+ process.stdin.on('data', chunk => data += chunk)
+ process.stdin.on('end', () => {
+ try {
+ const parsed = JSON.parse(data)
+ process.stdout.write(parsed.tool_input?.file_path || '')
+ } catch {}
+ })
+")
+
+# Only lint JS/JSX files
+if [[ -z "$FILE_PATH" ]] || [[ ! "$FILE_PATH" =~ \.(js|jsx)$ ]]; then
+ exit 0
+fi
+
+if [[ ! -f "$FILE_PATH" ]]; then
+ exit 0
+fi
+
+CONFIG="$(cd "$(dirname "$0")/.." && pwd)/eslint.config.cjs"
+
+result=$(npx eslint --config "$CONFIG" "$FILE_PATH" 2>&1)
+
+if [[ -n "$result" ]]; then
+ echo "$result"
+ exit 1
+fi
+
+exit 0
diff --git a/components/LazyVideoPlayer.js b/components/LazyVideoPlayer.js
new file mode 100644
index 0000000..2f4127f
--- /dev/null
+++ b/components/LazyVideoPlayer.js
@@ -0,0 +1,60 @@
+import { useState } from 'react'
+import { CldVideoPlayer } from 'next-cloudinary'
+
+export function LazyVideoPlayer({ src, width, height, poster }) {
+ const [playing, setPlaying] = useState(false)
+
+ if (playing) {
+ return
+ }
+
+ return (
+
setPlaying(true)}
+ style={{
+ position: 'relative',
+ cursor: 'pointer',
+ background: '#000',
+ aspectRatio: `${width} / ${height}`,
+ overflow: 'hidden',
+ }}
+ >
+ {poster && (
+

+ )}
+
+
+ )
+}
diff --git a/components/WebMCP.js b/components/WebMCP.js
new file mode 100644
index 0000000..fa351f4
--- /dev/null
+++ b/components/WebMCP.js
@@ -0,0 +1,95 @@
+import { useEffect } from 'react'
+
+export function WebMCP() {
+ useEffect(() => {
+ if (!navigator.modelContext) return
+
+ import(/* webpackChunkName: "snippets-registry" */ '../lib/snippets-registry').then(({ snippets }) => {
+ navigator.modelContext.registerTool({
+ name: 'list_snippets',
+ description:
+ 'List all web performance measurement snippets available on this site. Returns metadata without code.',
+ inputSchema: { type: 'object', properties: {} },
+ execute: () =>
+ snippets.map(({ id, category, title, description, url }) => ({
+ id,
+ category,
+ title,
+ description,
+ url,
+ })),
+ })
+
+ navigator.modelContext.registerTool({
+ name: 'get_snippet',
+ description:
+ 'Get the full JavaScript code for a specific web performance snippet by its ID.',
+ inputSchema: {
+ type: 'object',
+ properties: {
+ id: {
+ type: 'string',
+ description:
+ 'Snippet ID (e.g. "LCP", "CLS", "TTFB", "INP"). Use list_snippets to discover available IDs.',
+ },
+ },
+ required: ['id'],
+ },
+ execute: ({ id }) => {
+ const snippet = snippets.find((s) => s.id === id)
+ if (!snippet) {
+ return { error: `Snippet "${id}" not found. Use list_snippets to see available IDs.` }
+ }
+ return snippet
+ },
+ })
+
+ navigator.modelContext.registerTool({
+ name: 'search_snippets',
+ description:
+ 'Search web performance snippets by category and/or keyword. Returns metadata without code.',
+ inputSchema: {
+ type: 'object',
+ properties: {
+ category: {
+ type: 'string',
+ description:
+ 'Filter by category. One of: CoreWebVitals, Loading, Interaction, Media, Resources.',
+ },
+ query: {
+ type: 'string',
+ description: 'Keyword to filter by title or description.',
+ },
+ },
+ },
+ execute: ({ category, query } = {}) => {
+ let results = snippets
+
+ if (category) {
+ results = results.filter((s) => s.category === category)
+ }
+
+ if (query) {
+ const q = query.toLowerCase()
+ results = results.filter(
+ (s) =>
+ s.title.toLowerCase().includes(q) ||
+ s.description.toLowerCase().includes(q) ||
+ s.id.toLowerCase().includes(q)
+ )
+ }
+
+ return results.map(({ id, category, title, description, url }) => ({
+ id,
+ category,
+ title,
+ description,
+ url,
+ }))
+ },
+ })
+ })
+ }, [])
+
+ return null
+}
diff --git a/lib/snippets-registry.js b/lib/snippets-registry.js
new file mode 100644
index 0000000..697ebeb
--- /dev/null
+++ b/lib/snippets-registry.js
@@ -0,0 +1,286 @@
+import cls from '../snippets/CoreWebVitals/CLS.js?raw'
+import inp from '../snippets/CoreWebVitals/INP.js?raw'
+import lcp from '../snippets/CoreWebVitals/LCP.js?raw'
+import lcpImageEntropy from '../snippets/CoreWebVitals/LCP-Image-Entropy.js?raw'
+import lcpSubParts from '../snippets/CoreWebVitals/LCP-Sub-Parts.js?raw'
+import lcpTrail from '../snippets/CoreWebVitals/LCP-Trail.js?raw'
+import lcpVideoCandidate from '../snippets/CoreWebVitals/LCP-Video-Candidate.js?raw'
+
+import ttfb from '../snippets/Loading/TTFB.js?raw'
+import jsExecutionTimeBreakdown from '../snippets/Loading/JS-Execution-Time-Breakdown.js?raw'
+import fcp from '../snippets/Loading/FCP.js?raw'
+import backForwardCache from '../snippets/Loading/Back-Forward-Cache.js?raw'
+import cssMediaQueriesAnalysis from '../snippets/Loading/CSS-Media-Queries-Analysis.js?raw'
+import criticalCSSDetection from '../snippets/Loading/Critical-CSS-Detection.js?raw'
+import ssrHydrationDataAnalysis from '../snippets/Loading/SSR-Hydration-Data-Analysis.js?raw'
+import validatePreloadAsyncDeferScripts from '../snippets/Loading/Validate-Preload-Async-Defer-Scripts.js?raw'
+import resourceHintsValidation from '../snippets/Loading/Resource-Hints-Validation.js?raw'
+import prefetchResourceValidation from '../snippets/Loading/Prefetch-Resource-Validation.js?raw'
+import priorityHintsAudit from '../snippets/Loading/Priority-Hints-Audit.js?raw'
+import serviceWorkerAnalysis from '../snippets/Loading/Service-Worker-Analysis.js?raw'
+
+import interactions from '../snippets/Interaction/Interactions.js?raw'
+import inputLatencyBreakdown from '../snippets/Interaction/Input-Latency-Breakdown.js?raw'
+import layoutShiftLoadingAndInteraction from '../snippets/Interaction/Layout-Shift-Loading-and-Interaction.js?raw'
+import longAnimationFrames from '../snippets/Interaction/Long-Animation-Frames.js?raw'
+import longAnimationFramesScriptAttribution from '../snippets/Interaction/Long-Animation-Frames-Script-Attribution.js?raw'
+import longAnimationFramesHelpers from '../snippets/Interaction/Long-Animation-Frames-Helpers.js?raw'
+import longTask from '../snippets/Interaction/LongTask.js?raw'
+import scrollPerformance from '../snippets/Interaction/Scroll-Performance.js?raw'
+
+import imageElementAudit from '../snippets/Media/Image-Element-Audit.js?raw'
+import videoElementAudit from '../snippets/Media/Video-Element-Audit.js?raw'
+import svgEmbeddedBitmapAnalysis from '../snippets/Media/SVG-Embedded-Bitmap-Analysis.js?raw'
+
+import networkBandwidthConnectionQuality from '../snippets/Resources/Network-Bandwidth-Connection-Quality.js?raw'
+
+export const snippets = [
+ {
+ id: 'CLS',
+ category: 'CoreWebVitals',
+ title: 'Cumulative Layout Shift (CLS)',
+ description: 'Measures visual stability by tracking unexpected layout shifts during page load',
+ url: '/CoreWebVitals/CLS',
+ code: cls,
+ },
+ {
+ id: 'INP',
+ category: 'CoreWebVitals',
+ title: 'Interaction to Next Paint (INP)',
+ description: 'Measures responsiveness by tracking the latency of all user interactions',
+ url: '/CoreWebVitals/INP',
+ code: inp,
+ },
+ {
+ id: 'LCP',
+ category: 'CoreWebVitals',
+ title: 'Largest Contentful Paint (LCP)',
+ description: 'Measures loading performance by timing when the largest visible content element renders',
+ url: '/CoreWebVitals/LCP',
+ code: lcp,
+ },
+ {
+ id: 'LCP-Image-Entropy',
+ category: 'CoreWebVitals',
+ title: 'LCP Image Entropy',
+ description: 'Analyzes the visual complexity of LCP images to identify optimization opportunities',
+ url: '/CoreWebVitals/LCP-Image-Entropy',
+ code: lcpImageEntropy,
+ },
+ {
+ id: 'LCP-Sub-Parts',
+ category: 'CoreWebVitals',
+ title: 'LCP Sub-Parts',
+ description: 'Breaks down LCP into TTFB, load delay, load time, and render delay phases',
+ url: '/CoreWebVitals/LCP-Sub-Parts',
+ code: lcpSubParts,
+ },
+ {
+ id: 'LCP-Trail',
+ category: 'CoreWebVitals',
+ title: 'LCP Trail',
+ description: 'Traces the chain of LCP candidates over time to identify the final LCP element',
+ url: '/CoreWebVitals/LCP-Trail',
+ code: lcpTrail,
+ },
+ {
+ id: 'LCP-Video-Candidate',
+ category: 'CoreWebVitals',
+ title: 'LCP Video Candidate',
+ description: 'Detects when a video element is the LCP candidate and analyzes its loading',
+ url: '/CoreWebVitals/LCP-Video-Candidate',
+ code: lcpVideoCandidate,
+ },
+ {
+ id: 'TTFB',
+ category: 'Loading',
+ title: 'Time to First Byte (TTFB)',
+ description: 'Measures server response time and network latency for the initial HTML document',
+ url: '/Loading/TTFB',
+ code: ttfb,
+ },
+ {
+ id: 'JS-Execution-Time-Breakdown',
+ category: 'Loading',
+ title: 'JS Execution Time Breakdown',
+ description: 'Profiles JavaScript execution time per script to identify slow-parsing or long-running scripts',
+ url: '/Loading/JS-Execution-Time-Breakdown',
+ code: jsExecutionTimeBreakdown,
+ },
+ {
+ id: 'FCP',
+ category: 'Loading',
+ title: 'First Contentful Paint (FCP)',
+ description: 'Measures when the browser renders the first piece of content from the DOM',
+ url: '/Loading/FCP',
+ code: fcp,
+ },
+ {
+ id: 'Back-Forward-Cache',
+ category: 'Loading',
+ title: 'Back/Forward Cache (bfcache)',
+ description: 'Checks bfcache eligibility and identifies reasons that prevent pages from being cached',
+ url: '/Loading/Back-Forward-Cache',
+ code: backForwardCache,
+ },
+ {
+ id: 'CSS-Media-Queries-Analysis',
+ category: 'Loading',
+ title: 'CSS Media Queries Analysis',
+ description: 'Audits CSS stylesheets for media query usage and render-blocking impact',
+ url: '/Loading/CSS-Media-Queries-Analysis',
+ code: cssMediaQueriesAnalysis,
+ },
+ {
+ id: 'Critical-CSS-Detection',
+ category: 'Loading',
+ title: 'Critical CSS Detection',
+ description: 'Identifies above-the-fold CSS rules to help inline critical styles and defer the rest',
+ url: '/Loading/Critical-CSS-Detection',
+ code: criticalCSSDetection,
+ },
+ {
+ id: 'SSR-Hydration-Data-Analysis',
+ category: 'Loading',
+ title: 'SSR Framework Hydration Data Analysis',
+ description: 'Analyzes server-side rendering hydration payloads (Next.js __NEXT_DATA__, etc.) for size and content',
+ url: '/Loading/SSR-Hydration-Data-Analysis',
+ code: ssrHydrationDataAnalysis,
+ },
+ {
+ id: 'Validate-Preload-Async-Defer-Scripts',
+ category: 'Loading',
+ title: 'Validate Preload on Async/Defer Scripts',
+ description: 'Detects anti-patterns where scripts use both preload and async/defer, causing redundant requests',
+ url: '/Loading/Validate-Preload-Async-Defer-Scripts',
+ code: validatePreloadAsyncDeferScripts,
+ },
+ {
+ id: 'Resource-Hints-Validation',
+ category: 'Loading',
+ title: 'Resource Hints Validation',
+ description: 'Audits dns-prefetch, preconnect, prefetch, and preload hints for correctness and effectiveness',
+ url: '/Loading/Resource-Hints-Validation',
+ code: resourceHintsValidation,
+ },
+ {
+ id: 'Prefetch-Resource-Validation',
+ category: 'Loading',
+ title: 'Prefetch Resource Validation',
+ description: 'Verifies that prefetched resources are actually used and identifies unused prefetches',
+ url: '/Loading/Prefetch-Resource-Validation',
+ code: prefetchResourceValidation,
+ },
+ {
+ id: 'Priority-Hints-Audit',
+ category: 'Loading',
+ title: 'Priority Hints Audit',
+ description: 'Checks fetchpriority attributes on resources to ensure critical assets load first',
+ url: '/Loading/Priority-Hints-Audit',
+ code: priorityHintsAudit,
+ },
+ {
+ id: 'Service-Worker-Analysis',
+ category: 'Loading',
+ title: 'Service Worker Analysis',
+ description: 'Inspects registered service workers, their scope, state, and caching strategies',
+ url: '/Loading/Service-Worker-Analysis',
+ code: serviceWorkerAnalysis,
+ },
+ {
+ id: 'Interactions',
+ category: 'Interaction',
+ title: 'Interactions',
+ description: 'Tracks all user interactions (clicks, key presses, pointer events) with their durations',
+ url: '/Interaction/Interactions',
+ code: interactions,
+ },
+ {
+ id: 'Input-Latency-Breakdown',
+ category: 'Interaction',
+ title: 'Input Latency Breakdown',
+ description: 'Decomposes input delay into processing time and presentation delay for INP debugging',
+ url: '/Interaction/Input-Latency-Breakdown',
+ code: inputLatencyBreakdown,
+ },
+ {
+ id: 'Layout-Shift-Loading-and-Interaction',
+ category: 'Interaction',
+ title: 'Layout Shift Loading and Interaction',
+ description: 'Separates CLS into shifts caused by page loading versus user interactions',
+ url: '/Interaction/Layout-Shift-Loading-and-Interaction',
+ code: layoutShiftLoadingAndInteraction,
+ },
+ {
+ id: 'Long-Animation-Frames',
+ category: 'Interaction',
+ title: 'Long Animation Frames',
+ description: 'Identifies frames that take longer than 50ms to render, causing jank and poor responsiveness',
+ url: '/Interaction/Long-Animation-Frames',
+ code: longAnimationFrames,
+ },
+ {
+ id: 'Long-Animation-Frames-Script-Attribution',
+ category: 'Interaction',
+ title: 'Long Animation Frames Script Attribution',
+ description: 'Attributes long animation frames to specific scripts to identify the root cause of jank',
+ url: '/Interaction/Long-Animation-Frames-Script-Attribution',
+ code: longAnimationFramesScriptAttribution,
+ },
+ {
+ id: 'Long-Animation-Frames-Helpers',
+ category: 'Interaction',
+ title: 'Long Animation Frames Helpers',
+ description: 'Utility functions for filtering and analyzing long animation frame data',
+ url: '/Interaction/Long-Animation-Frames-Helpers',
+ code: longAnimationFramesHelpers,
+ },
+ {
+ id: 'LongTask',
+ category: 'Interaction',
+ title: 'Long Task',
+ description: 'Detects JavaScript tasks that block the main thread for more than 50ms',
+ url: '/Interaction/LongTask',
+ code: longTask,
+ },
+ {
+ id: 'Scroll-Performance',
+ category: 'Interaction',
+ title: 'Scroll Performance',
+ description: 'Measures scroll jank and frame rate during scrolling to diagnose smooth scrolling issues',
+ url: '/Interaction/Scroll-Performance',
+ code: scrollPerformance,
+ },
+ {
+ id: 'Image-Element-Audit',
+ category: 'Media',
+ title: 'Image Element Audit',
+ description: 'Audits all images for format, dimensions, lazy loading, fetchpriority, and sizing issues',
+ url: '/Media/Image-Element-Audit',
+ code: imageElementAudit,
+ },
+ {
+ id: 'Video-Element-Audit',
+ category: 'Media',
+ title: 'Video Element Audit',
+ description: 'Inspects video elements for autoplay, preload strategy, poster images, and performance impact',
+ url: '/Media/Video-Element-Audit',
+ code: videoElementAudit,
+ },
+ {
+ id: 'SVG-Embedded-Bitmap-Analysis',
+ category: 'Media',
+ title: 'SVG Embedded Bitmap Analysis',
+ description: 'Detects SVGs that embed raster images (base64 or URL), defeating SVG size benefits',
+ url: '/Media/SVG-Embedded-Bitmap-Analysis',
+ code: svgEmbeddedBitmapAnalysis,
+ },
+ {
+ id: 'Network-Bandwidth-Connection-Quality',
+ category: 'Resources',
+ title: 'Network Bandwidth & Connection Quality',
+ description: 'Reads Network Information API to detect connection type, bandwidth, RTT, and save-data mode',
+ url: '/Resources/Network-Bandwidth-Connection-Quality',
+ code: networkBandwidthConnectionQuality,
+ },
+]
diff --git a/package-lock.json b/package-lock.json
index 3453197..54aec94 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,6 +18,7 @@
"react-dom": "^18.2.0"
},
"devDependencies": {
+ "eslint": "^10.0.2",
"vercel": "^32.4.1"
}
},
@@ -244,6 +245,152 @@
"node": ">=16"
}
},
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.2.tgz",
+ "integrity": "sha512-YF+fE6LV4v5MGWRGj7G404/OZzGNepVF8fxk7jqmqo3lrza7a0uUcDnROGRBG1WFC1omYUS/Wp1f42i0M+3Q3A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^3.0.2",
+ "debug": "^4.3.1",
+ "minimatch": "^10.2.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@eslint/config-array/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/@eslint/config-array/node_modules/brace-expansion": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
+ "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/@eslint/config-array/node_modules/minimatch": {
+ "version": "10.2.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
+ "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.2.tgz",
+ "integrity": "sha512-a5MxrdDXEvqnIq+LisyCX6tQMPF/dSJpCfBgBauY+pNZ28yCtSsTvyTYrMhaI+LK26bVyCJfJkT0u8KIj2i1dQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^1.1.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.0.tgz",
+ "integrity": "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.2.tgz",
+ "integrity": "sha512-HOy56KJt48Bx8KmJ+XGQNSUMT/6dZee/M54XyUyuvTvPXJmsERRvBchsUVx1UMe1WwIH49XLAczNC7V2INsuUw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.0.tgz",
+ "integrity": "sha512-bIZEUzOI1jkhviX2cp5vNyXQc6olzb2ohewQubuYlMXZ2Q/XjBO0x0XhGPvc9fjSIiUN0vw+0hq53BJ4eQSJKQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^1.1.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
"node_modules/@fastify/busboy": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
@@ -270,6 +417,58 @@
"react-dom": "^16 || ^17 || ^18"
}
},
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.7",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
+ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
@@ -1695,6 +1894,13 @@
"@types/ms": "*"
}
},
+ "node_modules/@types/esrecurse": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
+ "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
@@ -2346,9 +2552,9 @@
"license": "ISC"
},
"node_modules/acorn": {
- "version": "8.15.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
- "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
+ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
@@ -2403,12 +2609,11 @@
}
},
"node_modules/ajv": {
- "version": "6.12.6",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
- "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
+ "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
"dev": true,
"license": "MIT",
- "optional": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -4012,6 +4217,13 @@
"node": ">=4.0.0"
}
},
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/del": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz",
@@ -4954,6 +5166,236 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/eslint": {
+ "version": "10.0.2",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.0.2.tgz",
+ "integrity": "sha512-uYixubwmqJZH+KLVYIVKY1JQt7tysXhtj21WSvjcSmU5SVNzMus1bgLe+pAt816yQ8opKfheVVoPLqvVMGejYw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.2",
+ "@eslint/config-array": "^0.23.2",
+ "@eslint/config-helpers": "^0.5.2",
+ "@eslint/core": "^1.1.0",
+ "@eslint/plugin-kit": "^0.6.0",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "ajv": "^6.14.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^9.1.1",
+ "eslint-visitor-keys": "^5.0.1",
+ "espree": "^11.1.1",
+ "esquery": "^1.7.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "minimatch": "^10.2.1",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "9.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.1.tgz",
+ "integrity": "sha512-GaUN0sWim5qc8KVErfPBWmc31LEsOkrUJbvJZV+xuL3u2phMUK4HIvXlWAakfC8W4nzlK+chPEAkYOYb5ZScIw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@types/esrecurse": "^4.3.1",
+ "@types/estree": "^1.0.8",
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/eslint/node_modules/brace-expansion": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
+ "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/eslint/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/eslint/node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/minimatch": {
+ "version": "10.2.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
+ "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/eslint/node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/espree": {
+ "version": "11.1.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-11.1.1.tgz",
+ "integrity": "sha512-AVHPqQoZYc+RUM4/3Ly5udlZY/U4LS8pIG05jEjWM2lQMU/oaZ7qshzAl2YP1tfNmXfftH3ohurfwNAug+MnsQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.16.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^5.0.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
"node_modules/esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
@@ -4967,6 +5409,42 @@
"node": ">=4"
}
},
+ "node_modules/esquery": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
"node_modules/estree-util-attach-comments": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-2.1.1.tgz",
@@ -5069,6 +5547,16 @@
"@types/estree": "^1.0.0"
}
},
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
@@ -5185,8 +5673,14 @@
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true,
- "license": "MIT",
- "optional": true
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
},
"node_modules/fastq": {
"version": "1.20.1",
@@ -5223,6 +5717,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
@@ -5285,6 +5792,27 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.4.tgz",
+ "integrity": "sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/flexsearch": {
"version": "0.7.43",
"resolved": "https://registry.npmjs.org/flexsearch/-/flexsearch-0.7.43.tgz",
@@ -6927,6 +7455,16 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
"node_modules/indent-string": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
@@ -7304,6 +7842,13 @@
"js-yaml": "bin/js-yaml.js"
}
},
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
@@ -7333,8 +7878,14 @@
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true,
- "license": "MIT",
- "optional": true
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
},
"node_modules/jsonc-parser": {
"version": "3.3.1",
@@ -7370,6 +7921,16 @@
"katex": "cli.js"
}
},
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
"node_modules/khroma": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz",
@@ -7399,6 +7960,20 @@
"integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==",
"license": "MIT"
},
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@@ -9254,6 +9829,13 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/neo-async": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
@@ -12991,6 +13573,24 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/os-paths": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/os-paths/-/os-paths-4.4.0.tgz",
@@ -13443,6 +14043,16 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/pretty-ms": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz",
@@ -18919,6 +19529,19 @@
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
}
},
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/type-fest": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
@@ -19593,6 +20216,16 @@
"string-width": "^1.0.2 || 2 || 3 || 4"
}
},
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/wordwrap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
diff --git a/package.json b/package.json
index 3b34d93..53a8d49 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
"react-dom": "^18.2.0"
},
"devDependencies": {
+ "eslint": "^10.0.2",
"vercel": "^32.4.1"
},
"overrides": {
diff --git a/pages/_app.js b/pages/_app.js
index 2d9d8ae..5644bd2 100644
--- a/pages/_app.js
+++ b/pages/_app.js
@@ -1,9 +1,11 @@
import Script from "next/script";
import "../styles/globals.css";
+import { WebMCP } from "../components/WebMCP";
function WebPerfSnippets({ Component, pageProps }) {
return (
<>
+