@geenius/tools 0.1.0

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.
Files changed (160) hide show
  1. package/.changeset/config.json +11 -0
  2. package/.env.example +2 -0
  3. package/.github/CODEOWNERS +1 -0
  4. package/.github/ISSUE_TEMPLATE/bug_report.md +16 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +11 -0
  6. package/.github/PULL_REQUEST_TEMPLATE.md +10 -0
  7. package/.github/dependabot.yml +11 -0
  8. package/.github/workflows/ci.yml +23 -0
  9. package/.github/workflows/release.yml +29 -0
  10. package/.node-version +1 -0
  11. package/.nvmrc +1 -0
  12. package/.prettierrc +7 -0
  13. package/.project/ACCOUNT.yaml +4 -0
  14. package/.project/IDEAS.yaml +7 -0
  15. package/.project/PROJECT.yaml +11 -0
  16. package/.project/ROADMAP.yaml +15 -0
  17. package/CHANGELOG.md +16 -0
  18. package/CODE_OF_CONDUCT.md +26 -0
  19. package/CONTRIBUTING.md +69 -0
  20. package/LICENSE +21 -0
  21. package/README.md +1 -0
  22. package/SECURITY.md +18 -0
  23. package/SUPPORT.md +14 -0
  24. package/package.json +75 -0
  25. package/packages/convex/shared/README.md +1 -0
  26. package/packages/convex/shared/package.json +42 -0
  27. package/packages/convex/shared/src/audit/index.ts +5 -0
  28. package/packages/convex/shared/src/audit/presets.ts +165 -0
  29. package/packages/convex/shared/src/audit/schema.ts +85 -0
  30. package/packages/convex/shared/src/audit/write.ts +102 -0
  31. package/packages/convex/shared/src/extract.ts +75 -0
  32. package/packages/convex/shared/src/index.ts +41 -0
  33. package/packages/convex/shared/src/messages.ts +45 -0
  34. package/packages/convex/shared/src/security.ts +112 -0
  35. package/packages/convex/shared/src/throw.ts +184 -0
  36. package/packages/convex/shared/src/types.ts +57 -0
  37. package/packages/convex/shared/src/utils.ts +58 -0
  38. package/packages/convex/shared/tsconfig.json +28 -0
  39. package/packages/convex/shared/tsup.config.ts +12 -0
  40. package/packages/devtools/package.json +27 -0
  41. package/packages/devtools/react/README.md +1 -0
  42. package/packages/devtools/react/package.json +53 -0
  43. package/packages/devtools/react/src/components/DesignPreview.tsx +59 -0
  44. package/packages/devtools/react/src/components/DesignSwitcherDropdown.tsx +99 -0
  45. package/packages/devtools/react/src/components/DevSidebar.tsx +247 -0
  46. package/packages/devtools/react/src/components/DevToolbar.tsx +242 -0
  47. package/packages/devtools/react/src/components/GitHubIssueDialog.tsx +402 -0
  48. package/packages/devtools/react/src/components/InspectorOverlay.tsx +312 -0
  49. package/packages/devtools/react/src/components/PageLoadWaterfall.tsx +144 -0
  50. package/packages/devtools/react/src/components/PerformancePanel.tsx +330 -0
  51. package/packages/devtools/react/src/context/DevModeContext.tsx +226 -0
  52. package/packages/devtools/react/src/context/PerformanceContext.tsx +143 -0
  53. package/packages/devtools/react/src/data/designs.ts +13 -0
  54. package/packages/devtools/react/src/hooks/useGitHubLabels.ts +47 -0
  55. package/packages/devtools/react/src/hooks/useVirtualList.ts +124 -0
  56. package/packages/devtools/react/src/index.ts +77 -0
  57. package/packages/devtools/react/src/panels/ConvexSpy.tsx +130 -0
  58. package/packages/devtools/react/src/panels/DatabaseSeeder.tsx +116 -0
  59. package/packages/devtools/react/src/panels/DevModePhase2.tsx +191 -0
  60. package/packages/devtools/react/src/panels/DevModePhase3.tsx +234 -0
  61. package/packages/devtools/react/src/panels/FeatureFlagsToggle.tsx +104 -0
  62. package/packages/devtools/react/src/panels/QuickRouteJump.tsx +152 -0
  63. package/packages/devtools/react/src/services/github-service.ts +247 -0
  64. package/packages/devtools/react/tsconfig.json +31 -0
  65. package/packages/devtools/react/tsup.config.ts +18 -0
  66. package/packages/devtools/solidjs/README.md +1 -0
  67. package/packages/devtools/solidjs/package.json +49 -0
  68. package/packages/devtools/solidjs/src/components/DesignPreview.tsx +51 -0
  69. package/packages/devtools/solidjs/src/components/DesignSwitcherDropdown.tsx +95 -0
  70. package/packages/devtools/solidjs/src/components/DevSidebar.tsx +247 -0
  71. package/packages/devtools/solidjs/src/components/DevToolbar.tsx +242 -0
  72. package/packages/devtools/solidjs/src/components/GitHubIssueDialog.tsx +400 -0
  73. package/packages/devtools/solidjs/src/components/InspectorOverlay.tsx +311 -0
  74. package/packages/devtools/solidjs/src/components/PageLoadWaterfall.tsx +144 -0
  75. package/packages/devtools/solidjs/src/components/PerformancePanel.tsx +330 -0
  76. package/packages/devtools/solidjs/src/context/DevModeContext.tsx +216 -0
  77. package/packages/devtools/solidjs/src/context/PerformanceContext.tsx +135 -0
  78. package/packages/devtools/solidjs/src/data/designs.ts +13 -0
  79. package/packages/devtools/solidjs/src/hooks/createGitHubLabels.ts +47 -0
  80. package/packages/devtools/solidjs/src/index.ts +64 -0
  81. package/packages/devtools/solidjs/src/services/github-service.ts +247 -0
  82. package/packages/devtools/solidjs/tsconfig.json +21 -0
  83. package/packages/devtools/src/index.ts +377 -0
  84. package/packages/devtools/tsup.config.ts +12 -0
  85. package/packages/env/package.json +30 -0
  86. package/packages/env/src/index.ts +264 -0
  87. package/packages/env/tsup.config.ts +12 -0
  88. package/packages/errors/package.json +27 -0
  89. package/packages/errors/react/README.md +1 -0
  90. package/packages/errors/react/package.json +72 -0
  91. package/packages/errors/react/src/analytics.ts +16 -0
  92. package/packages/errors/react/src/components/ErrorBoundary.tsx +248 -0
  93. package/packages/errors/react/src/components/ErrorDisplay.tsx +328 -0
  94. package/packages/errors/react/src/components/ValidationErrors.tsx +102 -0
  95. package/packages/errors/react/src/config.ts +199 -0
  96. package/packages/errors/react/src/constants.ts +74 -0
  97. package/packages/errors/react/src/hooks/useErrorBoundary.ts +92 -0
  98. package/packages/errors/react/src/hooks/useErrorHandler.ts +87 -0
  99. package/packages/errors/react/src/index.ts +96 -0
  100. package/packages/errors/react/src/types.ts +102 -0
  101. package/packages/errors/react/src/utils/errorMessages.ts +35 -0
  102. package/packages/errors/react/src/utils/errorPolicy.ts +139 -0
  103. package/packages/errors/react/src/utils/extractAppError.ts +174 -0
  104. package/packages/errors/react/src/utils/formatError.ts +112 -0
  105. package/packages/errors/react/tsconfig.json +25 -0
  106. package/packages/errors/react/tsup.config.ts +24 -0
  107. package/packages/errors/solidjs/README.md +1 -0
  108. package/packages/errors/solidjs/package.json +46 -0
  109. package/packages/errors/solidjs/src/components/ErrorDisplay.tsx +179 -0
  110. package/packages/errors/solidjs/src/config.ts +98 -0
  111. package/packages/errors/solidjs/src/hooks/createErrorHandler.ts +107 -0
  112. package/packages/errors/solidjs/src/index.ts +61 -0
  113. package/packages/errors/solidjs/src/types.ts +34 -0
  114. package/packages/errors/solidjs/src/utils/errorPolicy.ts +56 -0
  115. package/packages/errors/solidjs/src/utils/extractAppError.ts +94 -0
  116. package/packages/errors/solidjs/src/utils/formatError.ts +33 -0
  117. package/packages/errors/solidjs/tsconfig.json +26 -0
  118. package/packages/errors/solidjs/tsup.config.ts +21 -0
  119. package/packages/errors/src/index.ts +320 -0
  120. package/packages/errors/tsup.config.ts +12 -0
  121. package/packages/logger/package.json +27 -0
  122. package/packages/logger/react/README.md +1 -0
  123. package/packages/logger/react/package.json +46 -0
  124. package/packages/logger/react/src/index.ts +4 -0
  125. package/packages/logger/react/src/useMetrics.ts +42 -0
  126. package/packages/logger/react/src/usePerformanceLog.ts +61 -0
  127. package/packages/logger/react/tsconfig.json +31 -0
  128. package/packages/logger/react/tsup.config.ts +12 -0
  129. package/packages/logger/solidjs/README.md +1 -0
  130. package/packages/logger/solidjs/package.json +45 -0
  131. package/packages/logger/solidjs/src/createMetrics.ts +37 -0
  132. package/packages/logger/solidjs/src/createPerformanceLog.ts +58 -0
  133. package/packages/logger/solidjs/src/index.ts +4 -0
  134. package/packages/logger/solidjs/tsconfig.json +32 -0
  135. package/packages/logger/solidjs/tsup.config.ts +12 -0
  136. package/packages/logger/src/index.ts +363 -0
  137. package/packages/logger/tsup.config.ts +12 -0
  138. package/packages/perf/package.json +27 -0
  139. package/packages/perf/react/README.md +1 -0
  140. package/packages/perf/react/package.json +59 -0
  141. package/packages/perf/react/src/components/PerformanceDashboard.tsx +257 -0
  142. package/packages/perf/react/src/hooks/useMonitoredQuery.ts +89 -0
  143. package/packages/perf/react/src/hooks/usePerformanceMetrics.ts +78 -0
  144. package/packages/perf/react/src/index.ts +33 -0
  145. package/packages/perf/react/src/services/PerformanceMonitor.ts +313 -0
  146. package/packages/perf/react/src/types.ts +77 -0
  147. package/packages/perf/react/tsconfig.json +25 -0
  148. package/packages/perf/react/tsup.config.ts +19 -0
  149. package/packages/perf/solidjs/README.md +1 -0
  150. package/packages/perf/solidjs/package.json +41 -0
  151. package/packages/perf/solidjs/src/components/PerformanceDashboard.tsx +207 -0
  152. package/packages/perf/solidjs/src/hooks/createPerformanceMetrics.ts +73 -0
  153. package/packages/perf/solidjs/src/index.ts +31 -0
  154. package/packages/perf/solidjs/src/services/PerformanceMonitor.ts +134 -0
  155. package/packages/perf/solidjs/src/types.ts +78 -0
  156. package/packages/perf/solidjs/tsconfig.json +26 -0
  157. package/packages/perf/solidjs/tsup.config.ts +14 -0
  158. package/packages/perf/src/index.ts +410 -0
  159. package/packages/perf/tsup.config.ts +12 -0
  160. package/pnpm-workspace.yaml +2 -0
@@ -0,0 +1,257 @@
1
+ /**
2
+ * @geenius-tools/perf-react — components/PerformanceDashboard.tsx
3
+ *
4
+ * Rich performance dashboard with SSR cache hit rates, query performance,
5
+ * and error metrics. Uses @geenius-ui/react for UI primitives.
6
+ *
7
+ * Uses Tailwind CSS utility classes.
8
+ */
9
+
10
+ import { type FC } from 'react'
11
+
12
+ // ─── Types ──────────────────────────────────────────────────────────────────
13
+
14
+ export interface CacheMetrics {
15
+ hitRate: number
16
+ totalRequests: number
17
+ hits: number
18
+ misses: number
19
+ avgHitTime: number
20
+ avgMissTime: number
21
+ }
22
+
23
+ export interface QueryMetric {
24
+ queryKey: string
25
+ executions: number
26
+ avgDuration: number
27
+ minDuration: number
28
+ maxDuration: number
29
+ errors: number
30
+ cached: boolean
31
+ }
32
+
33
+ export interface ErrorMetric {
34
+ type: string
35
+ message: string
36
+ count: number
37
+ firstSeen: number
38
+ lastSeen: number
39
+ }
40
+
41
+ export interface PerformanceMetricsData {
42
+ score: number
43
+ cache: CacheMetrics
44
+ queries: QueryMetric[]
45
+ errors: ErrorMetric[]
46
+ }
47
+
48
+ export interface PerformanceDashboardProps {
49
+ /** Metrics data to display */
50
+ metrics: PerformanceMetricsData | null
51
+ /** Whether metrics are currently loading */
52
+ isLoading: boolean
53
+ /** Error if loading failed */
54
+ error: Error | null
55
+ /** Callback to refresh metrics */
56
+ onRefresh: () => void
57
+ /** Callback to clear metrics */
58
+ onClear: () => void
59
+ /** Callback to export metrics as JSON string */
60
+ onExport: () => string
61
+ }
62
+
63
+ /**
64
+ * PerformanceDashboard — displays SSR cache hit rates, query performance,
65
+ * and error metrics in a rich dashboard layout.
66
+ *
67
+ * This component is headless regarding data — the caller must provide
68
+ * the metrics data and callbacks. Use with `usePerformanceMetrics` hook.
69
+ */
70
+ export const PerformanceDashboard: FC<PerformanceDashboardProps> = ({
71
+ metrics,
72
+ isLoading,
73
+ error,
74
+ onRefresh,
75
+ onClear,
76
+ onExport,
77
+ }) => {
78
+
79
+ if (isLoading && !metrics) {
80
+ return (
81
+ <div className="flex justify-center py-8">
82
+ <div className="animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full" />
83
+ <span className="ml-3 text-muted-foreground">Loading performance metrics…</span>
84
+ </div>
85
+ )
86
+ }
87
+
88
+ if (error) {
89
+ return (
90
+ <div className="rounded-xl border border-border bg-card p-6">
91
+ <div className="text-red-600">
92
+ Error loading metrics: {error.message}
93
+ </div>
94
+ </div>
95
+ )
96
+ }
97
+
98
+ if (!metrics) return null
99
+
100
+ const downloadMetrics = () => {
101
+ const data = onExport()
102
+ const blob = new Blob([data], { type: 'application/json' })
103
+ const url = URL.createObjectURL(blob)
104
+ const a = document.createElement('a')
105
+ a.href = url
106
+ a.download = `performance-metrics-${Date.now()}.json`
107
+ a.click()
108
+ URL.revokeObjectURL(url)
109
+ }
110
+
111
+ const scoreBadge = (score: number) => {
112
+ if (score >= 90) return { emoji: '🟢', label: 'Excellent', color: 'bg-green-100 text-green-800' }
113
+ if (score >= 70) return { emoji: '🟡', label: 'Good', color: 'bg-yellow-100 text-yellow-800' }
114
+ return { emoji: '🔴', label: 'Needs Improvement', color: 'bg-red-100 text-red-800' }
115
+ }
116
+
117
+ const badge = scoreBadge(metrics.score)
118
+
119
+ return (
120
+ <div className="space-y-6">
121
+ {/* Header */}
122
+ <div className="flex items-center justify-between">
123
+ <div>
124
+ <h1 className="text-2xl font-bold">Performance Monitoring</h1>
125
+ <p className="text-muted-foreground mt-1">SSR Cache, Query Performance, and Error Tracking</p>
126
+ </div>
127
+ <div className="flex gap-2">
128
+ <button type="button" onClick={onRefresh} className="px-4 py-2 rounded-lg border border-border text-sm font-medium hover:bg-bg-muted transition-colors">Refresh</button>
129
+ <button type="button" onClick={downloadMetrics} className="px-4 py-2 rounded-lg border border-border text-sm font-medium hover:bg-bg-muted transition-colors">Export</button>
130
+ <button type="button" onClick={onClear} className="px-4 py-2 rounded-lg border border-border text-sm font-medium hover:bg-bg-muted transition-colors">Clear</button>
131
+ </div>
132
+ </div>
133
+
134
+ {/* Overall Score */}
135
+ <div className="rounded-xl border border-border bg-card p-6">
136
+ <div className="flex items-center justify-between">
137
+ <div>
138
+ <div className="text-sm text-muted-foreground mb-1">Overall Performance Score</div>
139
+ <div className="text-4xl font-bold text-foreground">{metrics.score}</div>
140
+ </div>
141
+ <div className="text-5xl">{badge.emoji}</div>
142
+ </div>
143
+ <div className="mt-4">
144
+ <span className={`px-2 py-1 rounded-full text-xs font-medium ${badge.color}`}>{badge.label}</span>
145
+ </div>
146
+ </div>
147
+
148
+ {/* SSR Cache Metrics */}
149
+ <div>
150
+ <h2 className="text-xl font-semibold mb-4">SSR Cache Performance</h2>
151
+ <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
152
+ <MetricCard label="Cache Hit Rate" value={`${metrics.cache.hitRate.toFixed(1)}%`} badge={metrics.cache.hitRate >= 80 ? 'Excellent' : metrics.cache.hitRate >= 50 ? 'Good' : 'Poor'} badgeColor={metrics.cache.hitRate >= 80 ? 'bg-green-100 text-green-800' : metrics.cache.hitRate >= 50 ? 'bg-yellow-100 text-yellow-800' : 'bg-red-100 text-red-800'} />
153
+ <MetricCard label="Total Requests" value={metrics.cache.totalRequests.toLocaleString()} subtitle={`${metrics.cache.hits} hits / ${metrics.cache.misses} misses`} />
154
+ <MetricCard label="Avg Hit Time" value={`${metrics.cache.avgHitTime.toFixed(0)}`} unit="ms" badge={metrics.cache.avgHitTime < 50 ? 'Fast' : 'Acceptable'} badgeColor={metrics.cache.avgHitTime < 50 ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800'} />
155
+ <MetricCard label="Avg Miss Time" value={`${metrics.cache.avgMissTime.toFixed(0)}`} unit="ms" subtitle={`Time saved: ${(metrics.cache.avgMissTime - metrics.cache.avgHitTime).toFixed(0)}ms`} />
156
+ </div>
157
+ </div>
158
+
159
+ {/* Top Queries */}
160
+ <div>
161
+ <h2 className="text-xl font-semibold mb-4">Top Queries by Executions</h2>
162
+ <div className="rounded-xl border border-border bg-card overflow-x-auto">
163
+ <table className="w-full">
164
+ <thead>
165
+ <tr className="border-b border-border">
166
+ {['Query', 'Executions', 'Avg Duration', 'Errors', 'Cached'].map((h) => (
167
+ <th key={h} className="text-left p-4 text-sm font-semibold text-muted-foreground">{h}</th>
168
+ ))}
169
+ </tr>
170
+ </thead>
171
+ <tbody>
172
+ {metrics.queries.slice(0, 10).map((query, idx) => (
173
+ <tr key={idx} className="border-b border-border/50 hover:bg-surface">
174
+ <td className="p-4 text-sm font-mono text-foreground truncate max-w-xs">{query.queryKey}</td>
175
+ <td className="p-4 text-sm text-foreground">{query.executions}</td>
176
+ <td className="p-4 text-sm text-foreground">
177
+ {query.avgDuration.toFixed(0)}ms
178
+ <span className="text-xs text-muted-foreground ml-2">({query.minDuration.toFixed(0)}-{query.maxDuration.toFixed(0)}ms)</span>
179
+ </td>
180
+ <td className="p-4">
181
+ {query.errors > 0
182
+ ? <span className="px-2 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">{query.errors}</span>
183
+ : <span className="text-sm text-muted-foreground/60">0</span>
184
+ }
185
+ </td>
186
+ <td className="p-4">
187
+ <span className={`px-2 py-0.5 rounded-full text-xs font-medium ${query.cached ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-600'}`}>
188
+ {query.cached ? 'Yes' : 'No'}
189
+ </span>
190
+ </td>
191
+ </tr>
192
+ ))}
193
+ {metrics.queries.length === 0 && (
194
+ <tr><td colSpan={5} className="p-8 text-center text-muted-foreground">No query data available</td></tr>
195
+ )}
196
+ </tbody>
197
+ </table>
198
+ </div>
199
+ </div>
200
+
201
+ {/* Errors */}
202
+ {metrics.errors.length > 0 && (
203
+ <div>
204
+ <h2 className="text-xl font-semibold mb-4">Recent Errors</h2>
205
+ <div className="rounded-xl border border-border bg-card divide-y divide-border">
206
+ {metrics.errors.slice(0, 5).map((err, idx) => (
207
+ <div key={idx} className="p-4">
208
+ <div className="flex items-start justify-between">
209
+ <div className="flex-1">
210
+ <div className="flex items-center gap-2 mb-1">
211
+ <span className="px-2 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">{err.type}</span>
212
+ <span className="text-sm font-semibold text-foreground">{err.message}</span>
213
+ </div>
214
+ <div className="text-xs text-muted-foreground">
215
+ First seen: {new Date(err.firstSeen).toLocaleString()} | Last seen: {new Date(err.lastSeen).toLocaleString()}
216
+ </div>
217
+ </div>
218
+ <div className="text-right ml-4">
219
+ <div className="text-lg font-bold text-red-600">{err.count}</div>
220
+ <div className="text-xs text-muted-foreground">occurrences</div>
221
+ </div>
222
+ </div>
223
+ </div>
224
+ ))}
225
+ </div>
226
+ </div>
227
+ )}
228
+ </div>
229
+ )
230
+ }
231
+
232
+ // ─── MetricCard helper ──────────────────────────────────────────────────────
233
+
234
+ function MetricCard({ label, value, unit, subtitle, badge, badgeColor }: {
235
+ label: string
236
+ value: string
237
+ unit?: string
238
+ subtitle?: string
239
+ badge?: string
240
+ badgeColor?: string
241
+ }) {
242
+ return (
243
+ <div className="rounded-xl border border-border bg-card p-6">
244
+ <div className="text-sm text-muted-foreground mb-1">{label}</div>
245
+ <div className="text-3xl font-bold text-foreground">
246
+ {value}
247
+ {unit && <span className="text-lg text-muted-foreground">{unit}</span>}
248
+ </div>
249
+ {badge && (
250
+ <div className="mt-2">
251
+ <span className={`px-2 py-0.5 rounded-full text-xs font-medium ${badgeColor}`}>{badge}</span>
252
+ </div>
253
+ )}
254
+ {subtitle && <div className="mt-2 text-sm text-muted-foreground">{subtitle}</div>}
255
+ </div>
256
+ )
257
+ }
@@ -0,0 +1,89 @@
1
+ // @geenius-tools/perf-react — src/hooks/useMonitoredQuery.ts
2
+ // TanStack Query wrapper with automatic performance tracking
3
+
4
+ import { useEffect, useRef } from 'react'
5
+ import { PerformanceMonitor } from '../services/PerformanceMonitor'
6
+
7
+ /**
8
+ * Type-safe wrapper types for TanStack Query
9
+ * We use generic types to avoid hard dependency on @tanstack/react-query
10
+ */
11
+ interface MonitoredQueryResult {
12
+ isSuccess: boolean
13
+ isError: boolean
14
+ error: unknown
15
+ isFetched: boolean
16
+ isFetchedAfterMount: boolean
17
+ }
18
+
19
+ interface UseMonitoredQueryOptions {
20
+ queryKey: readonly unknown[]
21
+ }
22
+
23
+ /**
24
+ * Hook that adds performance monitoring to a TanStack Query result
25
+ *
26
+ * @example
27
+ * import { useQuery } from '@tanstack/react-query'
28
+ *
29
+ * const result = useQuery({ queryKey: ['users'], queryFn: fetchUsers })
30
+ * useQueryMonitor(result, { queryKey: ['users'] })
31
+ */
32
+ export function useQueryMonitor(
33
+ result: MonitoredQueryResult,
34
+ options: UseMonitoredQueryOptions,
35
+ ): void {
36
+ const startTimeRef = useRef<number>(0)
37
+ const queryKey = JSON.stringify(options.queryKey)
38
+
39
+ // Start timing before query
40
+ useEffect(() => {
41
+ startTimeRef.current = performance.now()
42
+ }, [queryKey])
43
+
44
+ // Track query performance
45
+ useEffect(() => {
46
+ if (result.isSuccess || result.isError) {
47
+ const duration = performance.now() - startTimeRef.current
48
+
49
+ PerformanceMonitor.trackQuery(
50
+ queryKey,
51
+ duration,
52
+ result.error instanceof Error ? result.error : undefined,
53
+ result.isFetched && !result.isFetchedAfterMount,
54
+ )
55
+
56
+ // Track cache hit/miss
57
+ if (result.isFetched && !result.isFetchedAfterMount) {
58
+ PerformanceMonitor.trackCacheHit({
59
+ queryKey,
60
+ duration,
61
+ timestamp: Date.now(),
62
+ cacheType: 'client',
63
+ })
64
+ } else if (result.isFetchedAfterMount) {
65
+ PerformanceMonitor.trackCacheMiss({
66
+ queryKey,
67
+ duration: 0,
68
+ timestamp: Date.now(),
69
+ cacheType: 'client',
70
+ fetchDuration: duration,
71
+ })
72
+ }
73
+ }
74
+ }, [
75
+ result.isSuccess,
76
+ result.isError,
77
+ queryKey,
78
+ result.error,
79
+ result.isFetched,
80
+ result.isFetchedAfterMount,
81
+ ])
82
+
83
+ // Track errors
84
+ useEffect(() => {
85
+ if (result.error instanceof Error) {
86
+ PerformanceMonitor.trackError(result.error, 'query')
87
+ }
88
+ }, [result.error])
89
+ }
@@ -0,0 +1,78 @@
1
+ // @geenius-tools/perf-react — src/hooks/usePerformanceMetrics.ts
2
+
3
+ import { useState, useEffect, useCallback } from 'react'
4
+ import { PerformanceMonitor } from '../services/PerformanceMonitor'
5
+ import type { PerformanceMetrics } from '../types'
6
+
7
+ export interface UsePerformanceMetricsOptions {
8
+ /** Time range for metrics in milliseconds (default: 24h) */
9
+ timeRange?: number
10
+ /** Polling interval in milliseconds (default: 5000) */
11
+ pollingInterval?: number
12
+ /** Enable auto-refresh (default: true) */
13
+ autoRefresh?: boolean
14
+ }
15
+
16
+ /**
17
+ * React hook for accessing performance metrics
18
+ *
19
+ * @example
20
+ * const { metrics, isLoading, refresh, clearMetrics } = usePerformanceMetrics({
21
+ * timeRange: 60 * 60 * 1000, // 1 hour
22
+ * pollingInterval: 3000,
23
+ * })
24
+ */
25
+ export function usePerformanceMetrics(
26
+ options: UsePerformanceMetricsOptions = {},
27
+ ) {
28
+ const {
29
+ timeRange = 24 * 60 * 60 * 1000,
30
+ pollingInterval = 5000,
31
+ autoRefresh = true,
32
+ } = options
33
+
34
+ const [metrics, setMetrics] = useState<PerformanceMetrics | null>(null)
35
+ const [isLoading, setIsLoading] = useState(true)
36
+ const [error, setError] = useState<Error | null>(null)
37
+
38
+ const fetchMetrics = useCallback(() => {
39
+ try {
40
+ const end = Date.now()
41
+ const start = end - timeRange
42
+ const data = PerformanceMonitor.getMetrics({ start, end })
43
+ setMetrics(data)
44
+ setError(null)
45
+ } catch (err) {
46
+ setError(err instanceof Error ? err : new Error('Failed to fetch metrics'))
47
+ } finally {
48
+ setIsLoading(false)
49
+ }
50
+ }, [timeRange])
51
+
52
+ useEffect(() => {
53
+ fetchMetrics()
54
+
55
+ if (autoRefresh) {
56
+ const interval = setInterval(fetchMetrics, pollingInterval)
57
+ return () => clearInterval(interval)
58
+ }
59
+ }, [fetchMetrics, autoRefresh, pollingInterval])
60
+
61
+ const clearMetrics = useCallback(() => {
62
+ PerformanceMonitor.clear()
63
+ fetchMetrics()
64
+ }, [fetchMetrics])
65
+
66
+ const exportMetrics = useCallback(() => {
67
+ return PerformanceMonitor.exportMetrics()
68
+ }, [])
69
+
70
+ return {
71
+ metrics,
72
+ isLoading,
73
+ error,
74
+ refresh: fetchMetrics,
75
+ clearMetrics,
76
+ exportMetrics,
77
+ }
78
+ }
@@ -0,0 +1,33 @@
1
+ // @geenius-tools/perf-react — src/index.ts
2
+
3
+ // ===== Types =====
4
+ export type {
5
+ CacheMetrics,
6
+ QueryMetrics,
7
+ PageLoadMetrics,
8
+ ErrorMetrics,
9
+ PerformanceMetrics,
10
+ PerformanceConfig,
11
+ CacheHitEvent,
12
+ CacheMissEvent,
13
+ } from './types'
14
+
15
+ // ===== Service =====
16
+ export { PerformanceMonitor } from './services/PerformanceMonitor'
17
+
18
+ // ===== Hooks =====
19
+ export { usePerformanceMetrics } from './hooks/usePerformanceMetrics'
20
+ export type { UsePerformanceMetricsOptions } from './hooks/usePerformanceMetrics'
21
+
22
+ export { useQueryMonitor } from './hooks/useMonitoredQuery'
23
+
24
+ // ===== Components =====
25
+ export { PerformanceDashboard } from './components/PerformanceDashboard'
26
+ export type {
27
+ PerformanceDashboardProps,
28
+ PerformanceMetricsData,
29
+ CacheMetrics as DashboardCacheMetrics,
30
+ QueryMetric,
31
+ ErrorMetric,
32
+ } from './components/PerformanceDashboard'
33
+