hazo_config 1.0.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/.cursor/rules/general.mdc +51 -0
- package/.storybook/main.ts +33 -0
- package/.storybook/preview.ts +19 -0
- package/CODE_REVIEW.md +81 -0
- package/README.md +92 -0
- package/components.json +21 -0
- package/package.json +64 -0
- package/postcss.config.js +7 -0
- package/src/components/config_editor.stories.tsx +116 -0
- package/src/components/config_editor.tsx +214 -0
- package/src/components/config_viewer.stories.tsx +126 -0
- package/src/components/config_viewer.tsx +170 -0
- package/src/components/example_component.stories.tsx +59 -0
- package/src/components/example_component.tsx +39 -0
- package/src/components/index.ts +9 -0
- package/src/index.ts +7 -0
- package/src/lib/README.md +160 -0
- package/src/lib/config-loader.ts +186 -0
- package/src/lib/index.ts +18 -0
- package/src/lib/mock_config_provider.ts +93 -0
- package/src/lib/types.ts +100 -0
- package/src/lib/utils.ts +15 -0
- package/src/styles/globals.css +61 -0
- package/tailwind.config.js +13 -0
- package/tsconfig.build.json +13 -0
- package/tsconfig.json +30 -0
- package/tsconfig.node.json +15 -0
- package/vite.config.ts +14 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# HazoConfig
|
|
2
|
+
|
|
3
|
+
A lightweight, independent configuration management utility for INI files. Provides a unified interface for reading and writing configuration files with in-memory caching and sync operations.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Sync Operations**: All read/write operations are synchronous
|
|
8
|
+
- **In-Memory Caching**: Configuration is cached in memory for fast access
|
|
9
|
+
- **ConfigProvider Interface**: Standardized interface for dependency injection
|
|
10
|
+
- **File Validation**: Throws error if configuration file doesn't exist on initialization
|
|
11
|
+
- **Zero Dependencies**: Only uses Node.js built-ins and the `ini` package
|
|
12
|
+
- **Logger Support**: Optional logger injection for debugging
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
This component is designed to be extracted as a standalone library. To use it in your project:
|
|
17
|
+
|
|
18
|
+
1. Copy the `hazo_config` directory to your project
|
|
19
|
+
2. Install the `ini` package: `npm install ini`
|
|
20
|
+
3. Import and use as shown below
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
### Basic Usage
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { HazoConfig } from './hazo_config'
|
|
28
|
+
|
|
29
|
+
// Initialize with file path
|
|
30
|
+
const config = new HazoConfig({
|
|
31
|
+
filePath: 'config.ini'
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// Read a value
|
|
35
|
+
const apiKey = config.get('gemini', 'api_key')
|
|
36
|
+
|
|
37
|
+
// Read an entire section
|
|
38
|
+
const geminiConfig = config.getSection('gemini')
|
|
39
|
+
|
|
40
|
+
// Set a value
|
|
41
|
+
config.set('gemini', 'api_key', 'new-api-key')
|
|
42
|
+
|
|
43
|
+
// Save changes to disk
|
|
44
|
+
config.save()
|
|
45
|
+
|
|
46
|
+
// Refresh from disk (reloads file)
|
|
47
|
+
config.refresh()
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### With Logger
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { HazoConfig } from './hazo_config'
|
|
54
|
+
|
|
55
|
+
const logger = {
|
|
56
|
+
info: (msg, data) => console.log(msg, data),
|
|
57
|
+
warn: (msg, data) => console.warn(msg, data),
|
|
58
|
+
error: (msg, data) => console.error(msg, data)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const config = new HazoConfig({
|
|
62
|
+
filePath: 'config.ini',
|
|
63
|
+
logger
|
|
64
|
+
})
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Using ConfigProvider Interface
|
|
68
|
+
|
|
69
|
+
The `ConfigProvider` interface allows other components to consume configuration without knowing the implementation:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import type { ConfigProvider } from './hazo_config'
|
|
73
|
+
|
|
74
|
+
function myFunction(configProvider: ConfigProvider) {
|
|
75
|
+
const value = configProvider.get('section', 'key')
|
|
76
|
+
// Use value...
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## API Reference
|
|
81
|
+
|
|
82
|
+
### HazoConfig Class
|
|
83
|
+
|
|
84
|
+
#### Constructor
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
constructor(options: HazoConfigOptions)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
- `options.filePath` (required): Path to the INI configuration file
|
|
91
|
+
- `options.logger` (optional): Logger instance implementing the Logger interface
|
|
92
|
+
|
|
93
|
+
Throws error if file doesn't exist.
|
|
94
|
+
|
|
95
|
+
#### Methods
|
|
96
|
+
|
|
97
|
+
- `get(section: string, key: string): string | undefined` - Get a configuration value
|
|
98
|
+
- `getSection(section: string): Record<string, string> | undefined` - Get an entire section
|
|
99
|
+
- `set(section: string, key: string, value: string): void` - Set a configuration value
|
|
100
|
+
- `save(): void` - Save all changes to disk immediately
|
|
101
|
+
- `refresh(): void` - Reload configuration from disk
|
|
102
|
+
- `getFilePath(): string` - Get the resolved file path
|
|
103
|
+
- `getAllSections(): Record<string, Record<string, string>>` - Get all configuration sections
|
|
104
|
+
|
|
105
|
+
### ConfigProvider Interface
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
interface ConfigProvider {
|
|
109
|
+
get(section: string, key: string): string | undefined
|
|
110
|
+
getSection(section: string): Record<string, string> | undefined
|
|
111
|
+
set(section: string, key: string, value: string): void
|
|
112
|
+
save(): void
|
|
113
|
+
refresh(): void
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Error Handling
|
|
118
|
+
|
|
119
|
+
HazoConfig throws errors with the following error codes:
|
|
120
|
+
|
|
121
|
+
- `HAZO_CONFIG_FILE_NOT_FOUND`: Configuration file doesn't exist
|
|
122
|
+
- `HAZO_CONFIG_READ_ERROR`: Failed to read configuration file
|
|
123
|
+
- `HAZO_CONFIG_WRITE_ERROR`: Failed to write configuration file
|
|
124
|
+
- `HAZO_CONFIG_PARSE_ERROR`: Failed to parse INI content
|
|
125
|
+
- `HAZO_CONFIG_VALIDATION_ERROR`: Configuration validation failed
|
|
126
|
+
|
|
127
|
+
## Extraction Guide
|
|
128
|
+
|
|
129
|
+
To extract this component as a standalone library:
|
|
130
|
+
|
|
131
|
+
1. Copy the entire `hazo_config` directory
|
|
132
|
+
2. Create a `package.json` with:
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"name": "hazo-config",
|
|
136
|
+
"version": "1.0.0",
|
|
137
|
+
"main": "index.ts",
|
|
138
|
+
"dependencies": {
|
|
139
|
+
"ini": "^4.0.0"
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
3. The component has zero dependencies on the parent project
|
|
144
|
+
4. All types and interfaces are self-contained
|
|
145
|
+
|
|
146
|
+
## Integration with Other Components
|
|
147
|
+
|
|
148
|
+
This component is designed to work with other Hazo components like `hazo_connect`. The `ConfigProvider` interface allows components to accept configuration without tight coupling:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import type { ConfigProvider } from 'hazo-config'
|
|
152
|
+
import { createHazoConnect } from 'hazo-connect'
|
|
153
|
+
|
|
154
|
+
const config = new HazoConfig({ filePath: 'config.ini' })
|
|
155
|
+
const adapter = createHazoConnect({
|
|
156
|
+
configProvider: config, // Pass ConfigProvider
|
|
157
|
+
// ... other options
|
|
158
|
+
})
|
|
159
|
+
```
|
|
160
|
+
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Purpose: Main implementation of HazoConfig
|
|
3
|
+
*
|
|
4
|
+
* This file provides the HazoConfig class that implements the ConfigProvider
|
|
5
|
+
* interface. It handles reading and writing INI configuration files with
|
|
6
|
+
* in-memory caching, sync operations, and preservation of file formatting.
|
|
7
|
+
* Zero dependencies - only Node.js built-ins and the 'ini' package.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from 'fs'
|
|
11
|
+
import path from 'path'
|
|
12
|
+
import ini from 'ini'
|
|
13
|
+
import type { ConfigProvider, HazoConfigOptions, Logger } from './types'
|
|
14
|
+
import { ConfigErrorCode as EC } from './types'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* No-op logger implementation (default when no logger provided)
|
|
18
|
+
*/
|
|
19
|
+
const noOpLogger: Logger = {
|
|
20
|
+
info: () => {},
|
|
21
|
+
warn: () => {},
|
|
22
|
+
error: () => {}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* HazoConfig class
|
|
27
|
+
*
|
|
28
|
+
* Implements ConfigProvider interface for managing INI configuration files.
|
|
29
|
+
* Provides sync read/write operations with in-memory caching.
|
|
30
|
+
*/
|
|
31
|
+
export class HazoConfig implements ConfigProvider {
|
|
32
|
+
private filePath: string
|
|
33
|
+
private logger: Logger
|
|
34
|
+
private config: Record<string, Record<string, string>> = {}
|
|
35
|
+
private originalContent: string = ''
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Constructor
|
|
39
|
+
* @param options - Configuration options including filePath and optional logger
|
|
40
|
+
* @throws Error if file doesn't exist
|
|
41
|
+
*/
|
|
42
|
+
constructor(options: HazoConfigOptions) {
|
|
43
|
+
this.filePath = path.resolve(options.filePath)
|
|
44
|
+
this.logger = options.logger || noOpLogger
|
|
45
|
+
|
|
46
|
+
// Validate file exists
|
|
47
|
+
if (!fs.existsSync(this.filePath)) {
|
|
48
|
+
const error: any = new Error(`Configuration file not found: ${this.filePath}`)
|
|
49
|
+
error.code = EC.FILE_NOT_FOUND
|
|
50
|
+
this.logger.error(`[HazoConfig] Configuration file not found: ${this.filePath}`)
|
|
51
|
+
throw error
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Load initial configuration
|
|
55
|
+
this.refresh()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get a configuration value by section and key
|
|
60
|
+
* @param section - The configuration section name
|
|
61
|
+
* @param key - The configuration key name
|
|
62
|
+
* @returns The configuration value, or undefined if not found
|
|
63
|
+
*/
|
|
64
|
+
get(section: string, key: string): string | undefined {
|
|
65
|
+
return this.config[section]?.[key]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get an entire configuration section
|
|
70
|
+
* @param section - The configuration section name
|
|
71
|
+
* @returns A record of key-value pairs for the section, or undefined if section doesn't exist
|
|
72
|
+
*/
|
|
73
|
+
getSection(section: string): Record<string, string> | undefined {
|
|
74
|
+
return this.config[section] ? { ...this.config[section] } : undefined
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Set a configuration value
|
|
79
|
+
* @param section - The configuration section name
|
|
80
|
+
* @param key - The configuration key name
|
|
81
|
+
* @param value - The value to set
|
|
82
|
+
*/
|
|
83
|
+
set(section: string, key: string, value: string): void {
|
|
84
|
+
if (!this.config[section]) {
|
|
85
|
+
this.config[section] = {}
|
|
86
|
+
}
|
|
87
|
+
this.config[section][key] = value
|
|
88
|
+
this.logger.info(`[HazoConfig] Set config value: ${section}.${key}`, { value })
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Save the current configuration to disk
|
|
93
|
+
* Writes all changes immediately to the configuration file
|
|
94
|
+
*/
|
|
95
|
+
save(): void {
|
|
96
|
+
try {
|
|
97
|
+
// Convert config object back to INI format
|
|
98
|
+
const iniContent = ini.stringify(this.config, {
|
|
99
|
+
section: '[',
|
|
100
|
+
whitespace: true,
|
|
101
|
+
newline: '\n'
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
// Write to file
|
|
105
|
+
fs.writeFileSync(this.filePath, iniContent, 'utf-8')
|
|
106
|
+
|
|
107
|
+
// Update original content for future reference
|
|
108
|
+
this.originalContent = iniContent
|
|
109
|
+
|
|
110
|
+
this.logger.info(`[HazoConfig] Configuration saved to: ${this.filePath}`)
|
|
111
|
+
} catch (error: any) {
|
|
112
|
+
const configError: any = new Error(`Failed to save configuration: ${error.message || String(error)}`)
|
|
113
|
+
configError.code = EC.WRITE_ERROR
|
|
114
|
+
configError.originalError = error
|
|
115
|
+
this.logger.error(`[HazoConfig] Failed to save configuration: ${this.filePath}`, { error: String(error) })
|
|
116
|
+
throw configError
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Refresh the configuration from disk
|
|
122
|
+
* Reloads the configuration file and updates the in-memory cache
|
|
123
|
+
*/
|
|
124
|
+
refresh(): void {
|
|
125
|
+
try {
|
|
126
|
+
// Read file content
|
|
127
|
+
const content = fs.readFileSync(this.filePath, 'utf-8')
|
|
128
|
+
this.originalContent = content
|
|
129
|
+
|
|
130
|
+
// Parse INI content
|
|
131
|
+
const parsed = ini.parse(content)
|
|
132
|
+
|
|
133
|
+
// Convert to our internal format (ensure all values are strings)
|
|
134
|
+
this.config = {}
|
|
135
|
+
for (const [section, values] of Object.entries(parsed)) {
|
|
136
|
+
if (typeof values === 'object' && values !== null) {
|
|
137
|
+
this.config[section] = {}
|
|
138
|
+
for (const [key, value] of Object.entries(values)) {
|
|
139
|
+
this.config[section][key] = String(value)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
this.logger.info(`[HazoConfig] Configuration refreshed from: ${this.filePath}`, {
|
|
145
|
+
sections: Object.keys(this.config)
|
|
146
|
+
})
|
|
147
|
+
} catch (error: any) {
|
|
148
|
+
// Handle file read errors
|
|
149
|
+
if (error.code === 'ENOENT') {
|
|
150
|
+
const configError: any = new Error(`Configuration file not found: ${this.filePath}`)
|
|
151
|
+
configError.code = EC.FILE_NOT_FOUND
|
|
152
|
+
this.logger.error(`[HazoConfig] Configuration file not found: ${this.filePath}`)
|
|
153
|
+
throw configError
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Handle parse errors
|
|
157
|
+
const configError: any = new Error(`Failed to parse configuration: ${error.message || String(error)}`)
|
|
158
|
+
configError.code = EC.PARSE_ERROR
|
|
159
|
+
configError.originalError = error
|
|
160
|
+
this.logger.error(`[HazoConfig] Failed to parse configuration: ${this.filePath}`, { error: String(error) })
|
|
161
|
+
throw configError
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Get the file path being managed
|
|
167
|
+
* @returns The resolved file path
|
|
168
|
+
*/
|
|
169
|
+
getFilePath(): string {
|
|
170
|
+
return this.filePath
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Get all configuration sections
|
|
175
|
+
* @returns A record of all sections and their key-value pairs
|
|
176
|
+
*/
|
|
177
|
+
getAllSections(): Record<string, Record<string, string>> {
|
|
178
|
+
// Return a deep copy to prevent external modification
|
|
179
|
+
const result: Record<string, Record<string, string>> = {}
|
|
180
|
+
for (const [section, values] of Object.entries(this.config)) {
|
|
181
|
+
result[section] = { ...values }
|
|
182
|
+
}
|
|
183
|
+
return result
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
package/src/lib/index.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Purpose: Main export for the HazoConfig library.
|
|
3
|
+
*
|
|
4
|
+
* This file re-exports all public interfaces and classes for the HazoConfig
|
|
5
|
+
* configuration management utility. It serves as the primary entry point
|
|
6
|
+
* for consumers of the library.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export { HazoConfig } from './config-loader'
|
|
10
|
+
export { MockConfigProvider } from './mock_config_provider'
|
|
11
|
+
export { ConfigErrorCode } from './types'
|
|
12
|
+
export type {
|
|
13
|
+
ConfigProvider,
|
|
14
|
+
HazoConfigOptions,
|
|
15
|
+
Logger,
|
|
16
|
+
HazoConfigError
|
|
17
|
+
} from './types'
|
|
18
|
+
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Mock config provider for browser/Storybook testing
|
|
2
|
+
// Implements ConfigProvider interface using in-memory storage instead of file system
|
|
3
|
+
|
|
4
|
+
import type { ConfigProvider } from './types'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Mock config provider that stores configuration in memory
|
|
8
|
+
* Useful for testing and Storybook demonstrations where file system is not available
|
|
9
|
+
*/
|
|
10
|
+
export class MockConfigProvider implements ConfigProvider {
|
|
11
|
+
private config: Record<string, Record<string, string>> = {}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Constructor
|
|
15
|
+
* @param initial_config - Optional initial configuration data
|
|
16
|
+
*/
|
|
17
|
+
constructor(initial_config?: Record<string, Record<string, string>>) {
|
|
18
|
+
if (initial_config) {
|
|
19
|
+
this.config = JSON.parse(JSON.stringify(initial_config)) // Deep copy
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get a configuration value by section and key
|
|
25
|
+
* @param section - The configuration section name
|
|
26
|
+
* @param key - The configuration key name
|
|
27
|
+
* @returns The configuration value, or undefined if not found
|
|
28
|
+
*/
|
|
29
|
+
get(section: string, key: string): string | undefined {
|
|
30
|
+
return this.config[section]?.[key]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get an entire configuration section
|
|
35
|
+
* @param section - The configuration section name
|
|
36
|
+
* @returns A record of key-value pairs for the section, or undefined if section doesn't exist
|
|
37
|
+
*/
|
|
38
|
+
getSection(section: string): Record<string, string> | undefined {
|
|
39
|
+
return this.config[section] ? { ...this.config[section] } : undefined
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Set a configuration value
|
|
44
|
+
* @param section - The configuration section name
|
|
45
|
+
* @param key - The configuration key name
|
|
46
|
+
* @param value - The value to set
|
|
47
|
+
*/
|
|
48
|
+
set(section: string, key: string, value: string): void {
|
|
49
|
+
if (!this.config[section]) {
|
|
50
|
+
this.config[section] = {}
|
|
51
|
+
}
|
|
52
|
+
this.config[section][key] = value
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Save the current configuration (no-op for mock, but maintains interface compatibility)
|
|
57
|
+
*/
|
|
58
|
+
save(): void {
|
|
59
|
+
// No-op for mock provider
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Refresh the configuration (no-op for mock, but maintains interface compatibility)
|
|
64
|
+
*/
|
|
65
|
+
refresh(): void {
|
|
66
|
+
// No-op for mock provider
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get all configuration sections
|
|
71
|
+
* @returns A record of all sections and their key-value pairs
|
|
72
|
+
*/
|
|
73
|
+
getAllSections(): Record<string, Record<string, string>> {
|
|
74
|
+
const result: Record<string, Record<string, string>> = {}
|
|
75
|
+
for (const [section, values] of Object.entries(this.config)) {
|
|
76
|
+
result[section] = { ...values }
|
|
77
|
+
}
|
|
78
|
+
return result
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Reset the configuration to initial state or empty
|
|
83
|
+
* @param new_config - Optional new configuration to set
|
|
84
|
+
*/
|
|
85
|
+
reset(new_config?: Record<string, Record<string, string>>): void {
|
|
86
|
+
if (new_config) {
|
|
87
|
+
this.config = JSON.parse(JSON.stringify(new_config))
|
|
88
|
+
} else {
|
|
89
|
+
this.config = {}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
package/src/lib/types.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Purpose: Core interfaces and types for the HazoConfig component.
|
|
3
|
+
*
|
|
4
|
+
* This file defines the fundamental building blocks for creating a flexible and
|
|
5
|
+
* extensible configuration management utility. It includes interfaces for the
|
|
6
|
+
* ConfigProvider pattern, configuration options, and error types, ensuring
|
|
7
|
+
* loose coupling and reusability across different projects.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Logger interface for dependency injection
|
|
12
|
+
* Matches the Logger interface from hazo_connect for consistency
|
|
13
|
+
*/
|
|
14
|
+
export interface Logger {
|
|
15
|
+
info(message: string, data?: any): void
|
|
16
|
+
warn(message: string, data?: any): void
|
|
17
|
+
error(message: string, data?: any): void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* ConfigProvider interface
|
|
22
|
+
*
|
|
23
|
+
* Defines the contract for configuration providers that can be used
|
|
24
|
+
* by other components (like hazo_connect) to access configuration values.
|
|
25
|
+
* This allows for dependency injection and makes components testable.
|
|
26
|
+
*/
|
|
27
|
+
export interface ConfigProvider {
|
|
28
|
+
/**
|
|
29
|
+
* Get a configuration value by section and key
|
|
30
|
+
* @param section - The configuration section name
|
|
31
|
+
* @param key - The configuration key name
|
|
32
|
+
* @returns The configuration value, or undefined if not found
|
|
33
|
+
*/
|
|
34
|
+
get(section: string, key: string): string | undefined
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get an entire configuration section
|
|
38
|
+
* @param section - The configuration section name
|
|
39
|
+
* @returns A record of key-value pairs for the section, or undefined if section doesn't exist
|
|
40
|
+
*/
|
|
41
|
+
getSection(section: string): Record<string, string> | undefined
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Set a configuration value
|
|
45
|
+
* @param section - The configuration section name
|
|
46
|
+
* @param key - The configuration key name
|
|
47
|
+
* @param value - The value to set
|
|
48
|
+
*/
|
|
49
|
+
set(section: string, key: string, value: string): void
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Save the current configuration to disk
|
|
53
|
+
* Writes all changes immediately to the configuration file
|
|
54
|
+
*/
|
|
55
|
+
save(): void
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Refresh the configuration from disk
|
|
59
|
+
* Reloads the configuration file and updates the in-memory cache
|
|
60
|
+
*/
|
|
61
|
+
refresh(): void
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Options for initializing HazoConfig
|
|
66
|
+
*/
|
|
67
|
+
export interface HazoConfigOptions {
|
|
68
|
+
/**
|
|
69
|
+
* Path to the configuration file (required)
|
|
70
|
+
* Can be absolute or relative to process.cwd()
|
|
71
|
+
*/
|
|
72
|
+
filePath: string
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Optional logger instance for logging operations
|
|
76
|
+
*/
|
|
77
|
+
logger?: Logger
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Error codes for configuration operations
|
|
82
|
+
*/
|
|
83
|
+
export enum ConfigErrorCode {
|
|
84
|
+
FILE_NOT_FOUND = 'HAZO_CONFIG_FILE_NOT_FOUND',
|
|
85
|
+
READ_ERROR = 'HAZO_CONFIG_READ_ERROR',
|
|
86
|
+
WRITE_ERROR = 'HAZO_CONFIG_WRITE_ERROR',
|
|
87
|
+
PARSE_ERROR = 'HAZO_CONFIG_PARSE_ERROR',
|
|
88
|
+
VALIDATION_ERROR = 'HAZO_CONFIG_VALIDATION_ERROR'
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Standardized error response for configuration operations
|
|
93
|
+
*/
|
|
94
|
+
export interface HazoConfigError {
|
|
95
|
+
code: string
|
|
96
|
+
message: string
|
|
97
|
+
filePath?: string
|
|
98
|
+
originalError?: any
|
|
99
|
+
}
|
|
100
|
+
|
package/src/lib/utils.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Utility functions for the component library
|
|
2
|
+
// Provides class name merging functionality using clsx and tailwind-merge
|
|
3
|
+
|
|
4
|
+
import { type ClassValue, clsx } from "clsx"
|
|
5
|
+
import { twMerge } from "tailwind-merge"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Merges class names using clsx and tailwind-merge
|
|
9
|
+
* @param inputs - Class values to merge
|
|
10
|
+
* @returns Merged class string
|
|
11
|
+
*/
|
|
12
|
+
export function cn(...inputs: ClassValue[]) {
|
|
13
|
+
return twMerge(clsx(inputs))
|
|
14
|
+
}
|
|
15
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
@tailwind base;
|
|
2
|
+
@tailwind components;
|
|
3
|
+
@tailwind utilities;
|
|
4
|
+
|
|
5
|
+
@layer base {
|
|
6
|
+
:root {
|
|
7
|
+
--background: 0 0% 100%;
|
|
8
|
+
--foreground: 222.2 84% 4.9%;
|
|
9
|
+
--card: 0 0% 100%;
|
|
10
|
+
--card-foreground: 222.2 84% 4.9%;
|
|
11
|
+
--popover: 0 0% 100%;
|
|
12
|
+
--popover-foreground: 222.2 84% 4.9%;
|
|
13
|
+
--primary: 222.2 47.4% 11.2%;
|
|
14
|
+
--primary-foreground: 210 40% 98%;
|
|
15
|
+
--secondary: 210 40% 96.1%;
|
|
16
|
+
--secondary-foreground: 222.2 47.4% 11.2%;
|
|
17
|
+
--muted: 210 40% 96.1%;
|
|
18
|
+
--muted-foreground: 215.4 16.3% 46.9%;
|
|
19
|
+
--accent: 210 40% 96.1%;
|
|
20
|
+
--accent-foreground: 222.2 47.4% 11.2%;
|
|
21
|
+
--destructive: 0 84.2% 60.2%;
|
|
22
|
+
--destructive-foreground: 210 40% 98%;
|
|
23
|
+
--border: 214.3 31.8% 91.4%;
|
|
24
|
+
--input: 214.3 31.8% 91.4%;
|
|
25
|
+
--ring: 222.2 84% 4.9%;
|
|
26
|
+
--radius: 0.5rem;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.dark {
|
|
30
|
+
--background: 222.2 84% 4.9%;
|
|
31
|
+
--foreground: 210 40% 98%;
|
|
32
|
+
--card: 222.2 84% 4.9%;
|
|
33
|
+
--card-foreground: 210 40% 98%;
|
|
34
|
+
--popover: 222.2 84% 4.9%;
|
|
35
|
+
--popover-foreground: 210 40% 98%;
|
|
36
|
+
--primary: 210 40% 98%;
|
|
37
|
+
--primary-foreground: 222.2 47.4% 11.2%;
|
|
38
|
+
--secondary: 217.2 32.6% 17.5%;
|
|
39
|
+
--secondary-foreground: 210 40% 98%;
|
|
40
|
+
--muted: 217.2 32.6% 17.5%;
|
|
41
|
+
--muted-foreground: 215 20.2% 65.1%;
|
|
42
|
+
--accent: 217.2 32.6% 17.5%;
|
|
43
|
+
--accent-foreground: 210 40% 98%;
|
|
44
|
+
--destructive: 0 62.8% 30.6%;
|
|
45
|
+
--destructive-foreground: 210 40% 98%;
|
|
46
|
+
--border: 217.2 32.6% 17.5%;
|
|
47
|
+
--input: 217.2 32.6% 17.5%;
|
|
48
|
+
--ring: 212.7 26.8% 83.9%;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@layer base {
|
|
53
|
+
* {
|
|
54
|
+
border-color: hsl(var(--border));
|
|
55
|
+
}
|
|
56
|
+
body {
|
|
57
|
+
background-color: hsl(var(--background));
|
|
58
|
+
color: hsl(var(--foreground));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"noEmit": false,
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"declarationMap": true,
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src"
|
|
9
|
+
},
|
|
10
|
+
"include": ["src"],
|
|
11
|
+
"exclude": ["**/*.stories.tsx", "**/*.test.tsx", "**/*.test.ts"]
|
|
12
|
+
}
|
|
13
|
+
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"moduleResolution": "bundler",
|
|
9
|
+
"allowImportingTsExtensions": true,
|
|
10
|
+
"resolveJsonModule": true,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"jsx": "react-jsx",
|
|
14
|
+
"strict": true,
|
|
15
|
+
"noUnusedLocals": true,
|
|
16
|
+
"noUnusedParameters": true,
|
|
17
|
+
"noFallthroughCasesInSwitch": true,
|
|
18
|
+
"declaration": true,
|
|
19
|
+
"declarationMap": true,
|
|
20
|
+
"outDir": "./dist",
|
|
21
|
+
"rootDir": "./src",
|
|
22
|
+
"baseUrl": ".",
|
|
23
|
+
"paths": {
|
|
24
|
+
"@/*": ["./src/*"]
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"include": ["src"],
|
|
28
|
+
"references": [{ "path": "./tsconfig.node.json" }]
|
|
29
|
+
}
|
|
30
|
+
|