@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,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,12 @@
1
+ import { defineConfig } from 'tsup'
2
+
3
+ export default defineConfig({
4
+ entry: {
5
+ index: 'src/index.ts',
6
+ },
7
+ format: ['esm'],
8
+ dts: true,
9
+ clean: true,
10
+ sourcemap: true,
11
+ })
12
+
@@ -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,12 @@
1
+ import { defineConfig } from 'tsup'
2
+
3
+ export default defineConfig({
4
+ entry: {
5
+ index: 'src/index.ts',
6
+ },
7
+ format: ['esm'],
8
+ dts: true,
9
+ clean: true,
10
+ sourcemap: true,
11
+ })
12
+
@@ -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
+ }