@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.
- package/.changeset/config.json +11 -0
- package/.env.example +2 -0
- package/.github/CODEOWNERS +1 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +16 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +11 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +10 -0
- package/.github/dependabot.yml +11 -0
- package/.github/workflows/ci.yml +23 -0
- package/.github/workflows/release.yml +29 -0
- package/.node-version +1 -0
- package/.nvmrc +1 -0
- package/.prettierrc +7 -0
- package/.project/ACCOUNT.yaml +4 -0
- package/.project/IDEAS.yaml +7 -0
- package/.project/PROJECT.yaml +11 -0
- package/.project/ROADMAP.yaml +15 -0
- package/CHANGELOG.md +16 -0
- package/CODE_OF_CONDUCT.md +26 -0
- package/CONTRIBUTING.md +69 -0
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/SECURITY.md +18 -0
- package/SUPPORT.md +14 -0
- package/package.json +75 -0
- package/packages/convex/shared/README.md +1 -0
- package/packages/convex/shared/package.json +42 -0
- package/packages/convex/shared/src/audit/index.ts +5 -0
- package/packages/convex/shared/src/audit/presets.ts +165 -0
- package/packages/convex/shared/src/audit/schema.ts +85 -0
- package/packages/convex/shared/src/audit/write.ts +102 -0
- package/packages/convex/shared/src/extract.ts +75 -0
- package/packages/convex/shared/src/index.ts +41 -0
- package/packages/convex/shared/src/messages.ts +45 -0
- package/packages/convex/shared/src/security.ts +112 -0
- package/packages/convex/shared/src/throw.ts +184 -0
- package/packages/convex/shared/src/types.ts +57 -0
- package/packages/convex/shared/src/utils.ts +58 -0
- package/packages/convex/shared/tsconfig.json +28 -0
- package/packages/convex/shared/tsup.config.ts +12 -0
- package/packages/devtools/package.json +27 -0
- package/packages/devtools/react/README.md +1 -0
- package/packages/devtools/react/package.json +53 -0
- package/packages/devtools/react/src/components/DesignPreview.tsx +59 -0
- package/packages/devtools/react/src/components/DesignSwitcherDropdown.tsx +99 -0
- package/packages/devtools/react/src/components/DevSidebar.tsx +247 -0
- package/packages/devtools/react/src/components/DevToolbar.tsx +242 -0
- package/packages/devtools/react/src/components/GitHubIssueDialog.tsx +402 -0
- package/packages/devtools/react/src/components/InspectorOverlay.tsx +312 -0
- package/packages/devtools/react/src/components/PageLoadWaterfall.tsx +144 -0
- package/packages/devtools/react/src/components/PerformancePanel.tsx +330 -0
- package/packages/devtools/react/src/context/DevModeContext.tsx +226 -0
- package/packages/devtools/react/src/context/PerformanceContext.tsx +143 -0
- package/packages/devtools/react/src/data/designs.ts +13 -0
- package/packages/devtools/react/src/hooks/useGitHubLabels.ts +47 -0
- package/packages/devtools/react/src/hooks/useVirtualList.ts +124 -0
- package/packages/devtools/react/src/index.ts +77 -0
- package/packages/devtools/react/src/panels/ConvexSpy.tsx +130 -0
- package/packages/devtools/react/src/panels/DatabaseSeeder.tsx +116 -0
- package/packages/devtools/react/src/panels/DevModePhase2.tsx +191 -0
- package/packages/devtools/react/src/panels/DevModePhase3.tsx +234 -0
- package/packages/devtools/react/src/panels/FeatureFlagsToggle.tsx +104 -0
- package/packages/devtools/react/src/panels/QuickRouteJump.tsx +152 -0
- package/packages/devtools/react/src/services/github-service.ts +247 -0
- package/packages/devtools/react/tsconfig.json +31 -0
- package/packages/devtools/react/tsup.config.ts +18 -0
- package/packages/devtools/solidjs/README.md +1 -0
- package/packages/devtools/solidjs/package.json +49 -0
- package/packages/devtools/solidjs/src/components/DesignPreview.tsx +51 -0
- package/packages/devtools/solidjs/src/components/DesignSwitcherDropdown.tsx +95 -0
- package/packages/devtools/solidjs/src/components/DevSidebar.tsx +247 -0
- package/packages/devtools/solidjs/src/components/DevToolbar.tsx +242 -0
- package/packages/devtools/solidjs/src/components/GitHubIssueDialog.tsx +400 -0
- package/packages/devtools/solidjs/src/components/InspectorOverlay.tsx +311 -0
- package/packages/devtools/solidjs/src/components/PageLoadWaterfall.tsx +144 -0
- package/packages/devtools/solidjs/src/components/PerformancePanel.tsx +330 -0
- package/packages/devtools/solidjs/src/context/DevModeContext.tsx +216 -0
- package/packages/devtools/solidjs/src/context/PerformanceContext.tsx +135 -0
- package/packages/devtools/solidjs/src/data/designs.ts +13 -0
- package/packages/devtools/solidjs/src/hooks/createGitHubLabels.ts +47 -0
- package/packages/devtools/solidjs/src/index.ts +64 -0
- package/packages/devtools/solidjs/src/services/github-service.ts +247 -0
- package/packages/devtools/solidjs/tsconfig.json +21 -0
- package/packages/devtools/src/index.ts +377 -0
- package/packages/devtools/tsup.config.ts +12 -0
- package/packages/env/package.json +30 -0
- package/packages/env/src/index.ts +264 -0
- package/packages/env/tsup.config.ts +12 -0
- package/packages/errors/package.json +27 -0
- package/packages/errors/react/README.md +1 -0
- package/packages/errors/react/package.json +72 -0
- package/packages/errors/react/src/analytics.ts +16 -0
- package/packages/errors/react/src/components/ErrorBoundary.tsx +248 -0
- package/packages/errors/react/src/components/ErrorDisplay.tsx +328 -0
- package/packages/errors/react/src/components/ValidationErrors.tsx +102 -0
- package/packages/errors/react/src/config.ts +199 -0
- package/packages/errors/react/src/constants.ts +74 -0
- package/packages/errors/react/src/hooks/useErrorBoundary.ts +92 -0
- package/packages/errors/react/src/hooks/useErrorHandler.ts +87 -0
- package/packages/errors/react/src/index.ts +96 -0
- package/packages/errors/react/src/types.ts +102 -0
- package/packages/errors/react/src/utils/errorMessages.ts +35 -0
- package/packages/errors/react/src/utils/errorPolicy.ts +139 -0
- package/packages/errors/react/src/utils/extractAppError.ts +174 -0
- package/packages/errors/react/src/utils/formatError.ts +112 -0
- package/packages/errors/react/tsconfig.json +25 -0
- package/packages/errors/react/tsup.config.ts +24 -0
- package/packages/errors/solidjs/README.md +1 -0
- package/packages/errors/solidjs/package.json +46 -0
- package/packages/errors/solidjs/src/components/ErrorDisplay.tsx +179 -0
- package/packages/errors/solidjs/src/config.ts +98 -0
- package/packages/errors/solidjs/src/hooks/createErrorHandler.ts +107 -0
- package/packages/errors/solidjs/src/index.ts +61 -0
- package/packages/errors/solidjs/src/types.ts +34 -0
- package/packages/errors/solidjs/src/utils/errorPolicy.ts +56 -0
- package/packages/errors/solidjs/src/utils/extractAppError.ts +94 -0
- package/packages/errors/solidjs/src/utils/formatError.ts +33 -0
- package/packages/errors/solidjs/tsconfig.json +26 -0
- package/packages/errors/solidjs/tsup.config.ts +21 -0
- package/packages/errors/src/index.ts +320 -0
- package/packages/errors/tsup.config.ts +12 -0
- package/packages/logger/package.json +27 -0
- package/packages/logger/react/README.md +1 -0
- package/packages/logger/react/package.json +46 -0
- package/packages/logger/react/src/index.ts +4 -0
- package/packages/logger/react/src/useMetrics.ts +42 -0
- package/packages/logger/react/src/usePerformanceLog.ts +61 -0
- package/packages/logger/react/tsconfig.json +31 -0
- package/packages/logger/react/tsup.config.ts +12 -0
- package/packages/logger/solidjs/README.md +1 -0
- package/packages/logger/solidjs/package.json +45 -0
- package/packages/logger/solidjs/src/createMetrics.ts +37 -0
- package/packages/logger/solidjs/src/createPerformanceLog.ts +58 -0
- package/packages/logger/solidjs/src/index.ts +4 -0
- package/packages/logger/solidjs/tsconfig.json +32 -0
- package/packages/logger/solidjs/tsup.config.ts +12 -0
- package/packages/logger/src/index.ts +363 -0
- package/packages/logger/tsup.config.ts +12 -0
- package/packages/perf/package.json +27 -0
- package/packages/perf/react/README.md +1 -0
- package/packages/perf/react/package.json +59 -0
- package/packages/perf/react/src/components/PerformanceDashboard.tsx +257 -0
- package/packages/perf/react/src/hooks/useMonitoredQuery.ts +89 -0
- package/packages/perf/react/src/hooks/usePerformanceMetrics.ts +78 -0
- package/packages/perf/react/src/index.ts +33 -0
- package/packages/perf/react/src/services/PerformanceMonitor.ts +313 -0
- package/packages/perf/react/src/types.ts +77 -0
- package/packages/perf/react/tsconfig.json +25 -0
- package/packages/perf/react/tsup.config.ts +19 -0
- package/packages/perf/solidjs/README.md +1 -0
- package/packages/perf/solidjs/package.json +41 -0
- package/packages/perf/solidjs/src/components/PerformanceDashboard.tsx +207 -0
- package/packages/perf/solidjs/src/hooks/createPerformanceMetrics.ts +73 -0
- package/packages/perf/solidjs/src/index.ts +31 -0
- package/packages/perf/solidjs/src/services/PerformanceMonitor.ts +134 -0
- package/packages/perf/solidjs/src/types.ts +78 -0
- package/packages/perf/solidjs/tsconfig.json +26 -0
- package/packages/perf/solidjs/tsup.config.ts +14 -0
- package/packages/perf/src/index.ts +410 -0
- package/packages/perf/tsup.config.ts +12 -0
- package/pnpm-workspace.yaml +2 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @geenius-tools/devtools — Development tools and utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for development, debugging, and development-time configuration.
|
|
5
|
+
* Includes feature flags, mock data, and debugging helpers.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Feature flag configuration
|
|
10
|
+
*/
|
|
11
|
+
export interface FeatureFlag {
|
|
12
|
+
/** Flag name */
|
|
13
|
+
name: string
|
|
14
|
+
/** Whether flag is enabled */
|
|
15
|
+
enabled: boolean
|
|
16
|
+
/** When flag was created */
|
|
17
|
+
createdAt?: Date
|
|
18
|
+
/** When flag will be removed */
|
|
19
|
+
expiresAt?: Date
|
|
20
|
+
/** Optional description */
|
|
21
|
+
description?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Feature flags manager
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const flags = new FeatureFlagManager()
|
|
30
|
+
* flags.set('newUI', true)
|
|
31
|
+
* if (flags.isEnabled('newUI')) {
|
|
32
|
+
* // Use new UI
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export class FeatureFlagManager {
|
|
37
|
+
private flags: Map<string, FeatureFlag> = new Map()
|
|
38
|
+
private listeners: Set<(flag: string, enabled: boolean) => void> = new Set()
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Sets a feature flag
|
|
42
|
+
*/
|
|
43
|
+
set(name: string, enabled: boolean, description?: string) {
|
|
44
|
+
this.flags.set(name, {
|
|
45
|
+
name,
|
|
46
|
+
enabled,
|
|
47
|
+
description,
|
|
48
|
+
createdAt: new Date(),
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
// Notify listeners
|
|
52
|
+
for (const listener of this.listeners) {
|
|
53
|
+
listener(name, enabled)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Checks if feature is enabled
|
|
59
|
+
*/
|
|
60
|
+
isEnabled(name: string): boolean {
|
|
61
|
+
return this.flags.get(name)?.enabled ?? false
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Gets flag info
|
|
66
|
+
*/
|
|
67
|
+
getFlag(name: string): FeatureFlag | undefined {
|
|
68
|
+
return this.flags.get(name)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Gets all flags
|
|
73
|
+
*/
|
|
74
|
+
getAllFlags(): FeatureFlag[] {
|
|
75
|
+
return Array.from(this.flags.values())
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Removes a flag
|
|
80
|
+
*/
|
|
81
|
+
remove(name: string) {
|
|
82
|
+
this.flags.delete(name)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Clears all flags
|
|
87
|
+
*/
|
|
88
|
+
clear() {
|
|
89
|
+
this.flags.clear()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Listens for flag changes
|
|
94
|
+
*/
|
|
95
|
+
onChange(callback: (name: string, enabled: boolean) => void): () => void {
|
|
96
|
+
this.listeners.add(callback)
|
|
97
|
+
return () => this.listeners.delete(callback)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Type-safe feature flag helper
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* type AppFlags = 'newUI' | 'betaFeatures' | 'darkMode'
|
|
107
|
+
* const flags = createTypedFlags<AppFlags>()
|
|
108
|
+
* flags.set('newUI', true)
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
export function createTypedFlags<T extends string = string>() {
|
|
112
|
+
const manager = new FeatureFlagManager()
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
set: (name: T, enabled: boolean) => manager.set(name, enabled),
|
|
116
|
+
isEnabled: (name: T) => manager.isEnabled(name),
|
|
117
|
+
getFlag: (name: T) => manager.getFlag(name),
|
|
118
|
+
getAllFlags: () => manager.getAllFlags(),
|
|
119
|
+
remove: (name: T) => manager.remove(name),
|
|
120
|
+
clear: () => manager.clear(),
|
|
121
|
+
onChange: (callback: (name: T, enabled: boolean) => void) =>
|
|
122
|
+
manager.onChange(callback as any),
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Development mode detector
|
|
128
|
+
*
|
|
129
|
+
* @returns True if running in development
|
|
130
|
+
*/
|
|
131
|
+
export function isDevelopment(): boolean {
|
|
132
|
+
if (typeof process !== 'undefined' && process.env) {
|
|
133
|
+
return (
|
|
134
|
+
process.env.NODE_ENV === 'development' ||
|
|
135
|
+
process.env.DEBUG === 'true' ||
|
|
136
|
+
false
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (typeof window !== 'undefined') {
|
|
141
|
+
return (
|
|
142
|
+
window.location.hostname === 'localhost' ||
|
|
143
|
+
window.location.hostname === '127.0.0.1'
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return false
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Production mode detector
|
|
152
|
+
*
|
|
153
|
+
* @returns True if running in production
|
|
154
|
+
*/
|
|
155
|
+
export function isProduction(): boolean {
|
|
156
|
+
if (typeof process !== 'undefined' && process.env) {
|
|
157
|
+
return process.env.NODE_ENV === 'production'
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return !isDevelopment()
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Debug helper for logging in dev mode only
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```ts
|
|
168
|
+
* devLog('user state:', user)
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
export function devLog(...args: any[]) {
|
|
172
|
+
if (isDevelopment()) {
|
|
173
|
+
console.log('[DEV]', ...args)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Debug warning in dev mode only
|
|
179
|
+
*/
|
|
180
|
+
export function devWarn(...args: any[]) {
|
|
181
|
+
if (isDevelopment()) {
|
|
182
|
+
console.warn('[DEV WARN]', ...args)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Assertion utility for development
|
|
188
|
+
*
|
|
189
|
+
* @throws Error if condition is false in development
|
|
190
|
+
*/
|
|
191
|
+
export function devAssert(condition: boolean, message: string) {
|
|
192
|
+
if (isDevelopment() && !condition) {
|
|
193
|
+
throw new Error(`[DEV ASSERTION] ${message}`)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Creates a mock data generator
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* ```ts
|
|
202
|
+
* const mockUsers = generateMocks(10, () => ({
|
|
203
|
+
* id: faker.string.uuid(),
|
|
204
|
+
* name: faker.person.fullName(),
|
|
205
|
+
* email: faker.internet.email()
|
|
206
|
+
* }))
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
export function generateMocks<T>(count: number, generator: () => T): T[] {
|
|
210
|
+
return Array.from({ length: count }, () => generator())
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Measures React component render time
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```ts
|
|
218
|
+
* function MyComponent() {
|
|
219
|
+
* devMeasureRender('MyComponent')
|
|
220
|
+
* return <div>...</div>
|
|
221
|
+
* }
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
export function devMeasureRender(componentName: string) {
|
|
225
|
+
if (!isDevelopment()) return
|
|
226
|
+
|
|
227
|
+
const start = performance.now()
|
|
228
|
+
|
|
229
|
+
return () => {
|
|
230
|
+
const duration = performance.now() - start
|
|
231
|
+
if (duration > 16) {
|
|
232
|
+
// Longer than 60fps frame
|
|
233
|
+
console.warn(
|
|
234
|
+
`[PERF] ${componentName} render took ${duration.toFixed(2)}ms`
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Deep object comparison for development assertions
|
|
242
|
+
*
|
|
243
|
+
* @returns True if objects are deeply equal
|
|
244
|
+
*/
|
|
245
|
+
export function deepEqual(a: unknown, b: unknown): boolean {
|
|
246
|
+
if (a === b) return true
|
|
247
|
+
|
|
248
|
+
if (
|
|
249
|
+
typeof a !== 'object' ||
|
|
250
|
+
typeof b !== 'object' ||
|
|
251
|
+
a === null ||
|
|
252
|
+
b === null
|
|
253
|
+
) {
|
|
254
|
+
return false
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const keysA = Object.keys(a)
|
|
258
|
+
const keysB = Object.keys(b)
|
|
259
|
+
|
|
260
|
+
if (keysA.length !== keysB.length) return false
|
|
261
|
+
|
|
262
|
+
for (const key of keysA) {
|
|
263
|
+
if (!deepEqual((a as any)[key], (b as any)[key])) {
|
|
264
|
+
return false
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return true
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Creates a performance monitor that logs slow operations
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* ```ts
|
|
276
|
+
* const monitor = createSlowOpMonitor(100) // log if > 100ms
|
|
277
|
+
* await monitor.track('api-call', async () => {
|
|
278
|
+
* return fetch('/api/data')
|
|
279
|
+
* })
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
export function createSlowOpMonitor(thresholdMs: number) {
|
|
283
|
+
return {
|
|
284
|
+
track: async <T,>(name: string, fn: () => Promise<T>): Promise<T> => {
|
|
285
|
+
const start = performance.now()
|
|
286
|
+
const result = await fn()
|
|
287
|
+
const duration = performance.now() - start
|
|
288
|
+
|
|
289
|
+
if (duration > thresholdMs) {
|
|
290
|
+
console.warn(
|
|
291
|
+
`[SLOW OP] ${name} took ${duration.toFixed(2)}ms (threshold: ${thresholdMs}ms)`
|
|
292
|
+
)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return result
|
|
296
|
+
},
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Type assertion helper
|
|
302
|
+
*
|
|
303
|
+
* @throws Error if value is undefined/null in development
|
|
304
|
+
*
|
|
305
|
+
* @example
|
|
306
|
+
* ```ts
|
|
307
|
+
* const user = devAssertExists(data.user, 'user')
|
|
308
|
+
* ```
|
|
309
|
+
*/
|
|
310
|
+
export function devAssertExists<T>(value: T | null | undefined, name: string): T {
|
|
311
|
+
if (isDevelopment() && (value === null || value === undefined)) {
|
|
312
|
+
throw new Error(`Expected ${name} to exist, got ${value}`)
|
|
313
|
+
}
|
|
314
|
+
return value as T
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Global devtools window object
|
|
319
|
+
*/
|
|
320
|
+
export interface DevtoolsWindow {
|
|
321
|
+
__GEENIUS_DEVTOOLS__?: {
|
|
322
|
+
flags: FeatureFlagManager
|
|
323
|
+
isDevelopment: () => boolean
|
|
324
|
+
devLog: (...args: any[]) => void
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Initializes global devtools object (browser only)
|
|
330
|
+
*
|
|
331
|
+
* @example
|
|
332
|
+
* ```ts
|
|
333
|
+
* initGlobalDevtools()
|
|
334
|
+
* window.__GEENIUS_DEVTOOLS__.devLog('debug info')
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
337
|
+
export function initGlobalDevtools() {
|
|
338
|
+
if (typeof window === 'undefined') return
|
|
339
|
+
|
|
340
|
+
const flags = new FeatureFlagManager()
|
|
341
|
+
|
|
342
|
+
;(window as any).__GEENIUS_DEVTOOLS__ = {
|
|
343
|
+
flags,
|
|
344
|
+
isDevelopment,
|
|
345
|
+
devLog,
|
|
346
|
+
devWarn,
|
|
347
|
+
devAssert,
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Initializes devtools from environment
|
|
353
|
+
* Useful for loading feature flags from build-time environment variables
|
|
354
|
+
*
|
|
355
|
+
* @example
|
|
356
|
+
* ```ts
|
|
357
|
+
* const flags = createTypedFlags<'newUI' | 'beta'>()
|
|
358
|
+
* initDevtoolsFromEnv(flags, ['FEATURE_NEW_UI', 'FEATURE_BETA'])
|
|
359
|
+
* ```
|
|
360
|
+
*/
|
|
361
|
+
export function initDevtoolsFromEnv<T extends string>(
|
|
362
|
+
flags: ReturnType<typeof createTypedFlags<T>>,
|
|
363
|
+
envKeys: string[]
|
|
364
|
+
) {
|
|
365
|
+
for (const key of envKeys) {
|
|
366
|
+
const enabled =
|
|
367
|
+
typeof process !== 'undefined' &&
|
|
368
|
+
process.env[key] === 'true'
|
|
369
|
+
|
|
370
|
+
if (enabled) {
|
|
371
|
+
const flagName = key
|
|
372
|
+
.replace(/^FEATURE_/, '')
|
|
373
|
+
.toLowerCase() as T
|
|
374
|
+
flags.set(flagName, true)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@geenius-tools/env",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Environment variable validation for Geenius projects",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup",
|
|
16
|
+
"type-check": "tsc --noEmit",
|
|
17
|
+
"clean": "rm -rf dist .turbo"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"zod": "^3.22.4"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"typescript": "~6.0.2",
|
|
24
|
+
"tsup": "^8.0.1"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"license": "MIT"
|
|
30
|
+
}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @geenius-tools/env — Environment variable validation and management
|
|
3
|
+
*
|
|
4
|
+
* Provides schema-based environment variable validation using Zod.
|
|
5
|
+
* Validates at runtime and provides type-safe access to env vars.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { z, ZodSchema } from 'zod/v4'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Environment variable schema definition
|
|
12
|
+
*/
|
|
13
|
+
export type EnvSchema = Record<string, ZodSchema>
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Configuration for environment validation
|
|
17
|
+
*/
|
|
18
|
+
export interface EnvConfig {
|
|
19
|
+
/** Whether to throw on validation error (default: true) */
|
|
20
|
+
throw?: boolean
|
|
21
|
+
/** Prefix to add to all env var names (e.g., 'REACT_APP_') */
|
|
22
|
+
prefix?: string
|
|
23
|
+
/** Default values for optional variables */
|
|
24
|
+
defaults?: Record<string, unknown>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Validates environment variables against a Zod schema
|
|
29
|
+
*
|
|
30
|
+
* @param schema Zod schema defining expected environment variables
|
|
31
|
+
* @param config Validation configuration options
|
|
32
|
+
* @returns Validated environment variables as typed object
|
|
33
|
+
* @throws Error if validation fails and throw option is true
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* const envSchema = {
|
|
38
|
+
* DATABASE_URL: z.string().url(),
|
|
39
|
+
* API_KEY: z.string(),
|
|
40
|
+
* DEBUG: z.enum(['true', 'false']).optional(),
|
|
41
|
+
* }
|
|
42
|
+
*
|
|
43
|
+
* const env = validateEnv(envSchema, { throw: true })
|
|
44
|
+
* // env is now { DATABASE_URL: string, API_KEY: string, DEBUG?: string }
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export function validateEnv<T extends EnvSchema>(
|
|
48
|
+
schema: T,
|
|
49
|
+
config: EnvConfig = {}
|
|
50
|
+
): Partial<{ [K in keyof T]: unknown }> {
|
|
51
|
+
const { prefix = '', defaults = {}, throw: shouldThrow = true } = config
|
|
52
|
+
|
|
53
|
+
const envVars: Record<string, unknown> = {}
|
|
54
|
+
|
|
55
|
+
// Collect env variables with prefix
|
|
56
|
+
for (const key of Object.keys(schema)) {
|
|
57
|
+
const prefixedKey = prefix ? `${prefix}${key}` : key
|
|
58
|
+
const value = process.env[prefixedKey] ?? defaults[key]
|
|
59
|
+
|
|
60
|
+
if (value !== undefined) {
|
|
61
|
+
envVars[key] = value
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Validate using zod
|
|
66
|
+
const schemaObj = z.object(schema as Record<string, ZodSchema>)
|
|
67
|
+
const result = schemaObj.safeParse(envVars)
|
|
68
|
+
|
|
69
|
+
if (!result.success) {
|
|
70
|
+
const errors = result.error.errors
|
|
71
|
+
.map((e) => `${e.path.join('.')}: ${e.message}`)
|
|
72
|
+
.join(', ')
|
|
73
|
+
|
|
74
|
+
const message = `Environment validation failed: ${errors}`
|
|
75
|
+
|
|
76
|
+
if (shouldThrow) {
|
|
77
|
+
throw new Error(message)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.error(message)
|
|
81
|
+
return {}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return result.data
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Creates a reusable environment validator
|
|
89
|
+
*
|
|
90
|
+
* @param schema Zod schema
|
|
91
|
+
* @param config Default configuration
|
|
92
|
+
* @returns Validator function
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* const validator = createEnvValidator(
|
|
97
|
+
* { DATABASE_URL: z.string().url() },
|
|
98
|
+
* { throw: true }
|
|
99
|
+
* )
|
|
100
|
+
* const env = validator()
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
export function createEnvValidator<T extends EnvSchema>(
|
|
104
|
+
schema: T,
|
|
105
|
+
config: EnvConfig = {}
|
|
106
|
+
) {
|
|
107
|
+
return () => validateEnv(schema, config)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Gets a single environment variable with optional validation
|
|
112
|
+
*
|
|
113
|
+
* @param key Environment variable key
|
|
114
|
+
* @param schema Optional Zod schema for validation
|
|
115
|
+
* @param defaultValue Default value if not found
|
|
116
|
+
* @returns Environment variable value or default
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```ts
|
|
120
|
+
* const apiKey = getEnv('API_KEY', z.string().min(1))
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export function getEnv<T = string>(
|
|
124
|
+
key: string,
|
|
125
|
+
schema?: ZodSchema,
|
|
126
|
+
defaultValue?: T
|
|
127
|
+
): T | undefined {
|
|
128
|
+
const value = process.env[key] ?? defaultValue
|
|
129
|
+
|
|
130
|
+
if (!value) return undefined
|
|
131
|
+
|
|
132
|
+
if (schema) {
|
|
133
|
+
const result = schema.safeParse(value)
|
|
134
|
+
if (!result.success) {
|
|
135
|
+
throw new Error(`Invalid env var ${key}: ${result.error.message}`)
|
|
136
|
+
}
|
|
137
|
+
return result.data as T
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return value as T
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Checks if environment variable exists and is set
|
|
145
|
+
*
|
|
146
|
+
* @param key Variable name
|
|
147
|
+
* @returns True if variable exists
|
|
148
|
+
*/
|
|
149
|
+
export function hasEnv(key: string): boolean {
|
|
150
|
+
return key in process.env
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Gets multiple environment variables at once
|
|
155
|
+
*
|
|
156
|
+
* @param keys List of environment variable keys
|
|
157
|
+
* @returns Object with available values
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```ts
|
|
161
|
+
* const { API_KEY, DEBUG } = getEnvs(['API_KEY', 'DEBUG'])
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
export function getEnvs(keys: string[]): Record<string, string | undefined> {
|
|
165
|
+
return keys.reduce(
|
|
166
|
+
(acc, key) => {
|
|
167
|
+
acc[key] = process.env[key]
|
|
168
|
+
return acc
|
|
169
|
+
},
|
|
170
|
+
{} as Record<string, string | undefined>
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Asserts that all required environment variables are set
|
|
176
|
+
*
|
|
177
|
+
* @param requiredKeys List of required variable names
|
|
178
|
+
* @throws Error if any required variable is missing
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```ts
|
|
182
|
+
* requireEnv(['DATABASE_URL', 'API_KEY'])
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
export function requireEnv(requiredKeys: string[]): void {
|
|
186
|
+
const missing = requiredKeys.filter((key) => !(key in process.env))
|
|
187
|
+
|
|
188
|
+
if (missing.length > 0) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
`Missing required environment variables: ${missing.join(', ')}`
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Parses environment variable as JSON
|
|
197
|
+
*
|
|
198
|
+
* @param key Environment variable key
|
|
199
|
+
* @param defaultValue Default object if parsing fails
|
|
200
|
+
* @returns Parsed JSON object
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```ts
|
|
204
|
+
* const config = parseEnvJson('APP_CONFIG', { theme: 'light' })
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
export function parseEnvJson<T = unknown>(
|
|
208
|
+
key: string,
|
|
209
|
+
defaultValue?: T
|
|
210
|
+
): T | undefined {
|
|
211
|
+
const value = process.env[key]
|
|
212
|
+
|
|
213
|
+
if (!value) return defaultValue
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
return JSON.parse(value) as T
|
|
217
|
+
} catch {
|
|
218
|
+
console.warn(`Failed to parse ${key} as JSON`)
|
|
219
|
+
return defaultValue
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Parses environment variable as boolean
|
|
225
|
+
*
|
|
226
|
+
* @param key Environment variable key
|
|
227
|
+
* @param defaultValue Default value
|
|
228
|
+
* @returns Boolean value
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* ```ts
|
|
232
|
+
* const debug = parseEnvBool('DEBUG', false)
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
export function parseEnvBool(key: string, defaultValue = false): boolean {
|
|
236
|
+
const value = process.env[key]
|
|
237
|
+
|
|
238
|
+
if (!value) return defaultValue
|
|
239
|
+
|
|
240
|
+
return ['true', '1', 'yes', 'on'].includes(value.toLowerCase())
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Parses environment variable as number
|
|
245
|
+
*
|
|
246
|
+
* @param key Environment variable key
|
|
247
|
+
* @param defaultValue Default value
|
|
248
|
+
* @returns Numeric value
|
|
249
|
+
*
|
|
250
|
+
* @example
|
|
251
|
+
* ```ts
|
|
252
|
+
* const port = parseEnvNumber('PORT', 3000)
|
|
253
|
+
* ```
|
|
254
|
+
*/
|
|
255
|
+
export function parseEnvNumber(key: string, defaultValue = 0): number {
|
|
256
|
+
const value = process.env[key]
|
|
257
|
+
|
|
258
|
+
if (!value) return defaultValue
|
|
259
|
+
|
|
260
|
+
const num = Number(value)
|
|
261
|
+
return isNaN(num) ? defaultValue : num
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export type { ZodSchema } from 'zod/v4'
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@geenius-tools/errors",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Error handling utilities for Geenius projects",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup",
|
|
16
|
+
"type-check": "tsc --noEmit",
|
|
17
|
+
"clean": "rm -rf dist .turbo"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"typescript": "~6.0.2",
|
|
21
|
+
"tsup": "^8.0.1"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT"
|
|
27
|
+
}
|