hazo_config 1.0.0 → 1.2.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/dist/components/config_editor.d.ts +27 -0
- package/dist/components/config_editor.d.ts.map +1 -0
- package/dist/components/config_editor.js +81 -0
- package/dist/components/config_viewer.d.ts +27 -0
- package/dist/components/config_viewer.d.ts.map +1 -0
- package/dist/components/config_viewer.js +76 -0
- package/dist/components/example_component.d.ts +21 -0
- package/dist/components/example_component.d.ts.map +1 -0
- package/dist/components/example_component.js +10 -0
- package/dist/components/index.d.ts +6 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +5 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/lib/config-loader.d.ts +67 -0
- package/dist/lib/config-loader.d.ts.map +1 -0
- package/dist/lib/config-loader.js +177 -0
- package/dist/lib/index.d.ts +12 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +10 -0
- package/dist/lib/mock_config_provider.d.ts +52 -0
- package/dist/lib/mock_config_provider.d.ts.map +1 -0
- package/dist/lib/mock_config_provider.js +87 -0
- package/dist/lib/types.d.ts +91 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +19 -0
- package/dist/lib/utils.d.ts +8 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +12 -0
- package/package.json +1 -1
- package/src/components/config_editor.tsx +0 -1
- package/src/lib/config-loader.ts +4 -10
- package/src/lib/types.ts +1 -0
- package/tsconfig.build.json +2 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ConfigProvider } from '@/lib/types';
|
|
3
|
+
/**
|
|
4
|
+
* Config editor component props
|
|
5
|
+
*/
|
|
6
|
+
export interface ConfigEditorProps {
|
|
7
|
+
/**
|
|
8
|
+
* The config provider instance
|
|
9
|
+
*/
|
|
10
|
+
config_provider: ConfigProvider;
|
|
11
|
+
/**
|
|
12
|
+
* Additional CSS classes
|
|
13
|
+
*/
|
|
14
|
+
className?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Callback when config is updated
|
|
17
|
+
*/
|
|
18
|
+
on_update?: () => void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Config editor component
|
|
22
|
+
* Provides interface for viewing and editing configuration with refresh and save capabilities
|
|
23
|
+
* @param props - Component props
|
|
24
|
+
* @returns React component
|
|
25
|
+
*/
|
|
26
|
+
export declare const ConfigEditor: React.FC<ConfigEditorProps>;
|
|
27
|
+
//# sourceMappingURL=config_editor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config_editor.d.ts","sourceRoot":"","sources":["../../src/components/config_editor.tsx"],"names":[],"mappings":"AAGA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAElD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAGjD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,eAAe,EAAE,cAAc,CAAA;IAC/B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAmLpD,CAAA"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
// Config editor component for managing configuration
|
|
3
|
+
// Provides a more advanced interface for editing configuration values
|
|
4
|
+
import { useState, useEffect } from 'react';
|
|
5
|
+
import { cn } from '@/lib/utils';
|
|
6
|
+
import { RefreshCw, Save } from 'lucide-react';
|
|
7
|
+
/**
|
|
8
|
+
* Config editor component
|
|
9
|
+
* Provides interface for viewing and editing configuration with refresh and save capabilities
|
|
10
|
+
* @param props - Component props
|
|
11
|
+
* @returns React component
|
|
12
|
+
*/
|
|
13
|
+
export const ConfigEditor = ({ config_provider, className, on_update, }) => {
|
|
14
|
+
const [sections, set_sections] = useState({});
|
|
15
|
+
const [selected_section, set_selected_section] = useState(null);
|
|
16
|
+
const [new_key, set_new_key] = useState('');
|
|
17
|
+
const [new_value, set_new_value] = useState('');
|
|
18
|
+
/**
|
|
19
|
+
* Load configuration from provider
|
|
20
|
+
*/
|
|
21
|
+
const load_config = () => {
|
|
22
|
+
const all_sections = {};
|
|
23
|
+
if ('getAllSections' in config_provider && typeof config_provider.getAllSections === 'function') {
|
|
24
|
+
const all = config_provider.getAllSections();
|
|
25
|
+
Object.assign(all_sections, all);
|
|
26
|
+
}
|
|
27
|
+
set_sections(all_sections);
|
|
28
|
+
if (!selected_section && Object.keys(all_sections).length > 0) {
|
|
29
|
+
set_selected_section(Object.keys(all_sections)[0]);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
load_config();
|
|
34
|
+
}, [config_provider]);
|
|
35
|
+
/**
|
|
36
|
+
* Handle refresh button click
|
|
37
|
+
*/
|
|
38
|
+
const handle_refresh = () => {
|
|
39
|
+
config_provider.refresh();
|
|
40
|
+
load_config();
|
|
41
|
+
if (on_update) {
|
|
42
|
+
on_update();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Handle save button click
|
|
47
|
+
*/
|
|
48
|
+
const handle_save = () => {
|
|
49
|
+
config_provider.save();
|
|
50
|
+
load_config();
|
|
51
|
+
if (on_update) {
|
|
52
|
+
on_update();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Add a new key-value pair to selected section
|
|
57
|
+
*/
|
|
58
|
+
const handle_add_key = () => {
|
|
59
|
+
if (selected_section && new_key && new_value) {
|
|
60
|
+
config_provider.set(selected_section, new_key, new_value);
|
|
61
|
+
set_new_key('');
|
|
62
|
+
set_new_value('');
|
|
63
|
+
load_config();
|
|
64
|
+
if (on_update) {
|
|
65
|
+
on_update();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Update a config value
|
|
71
|
+
*/
|
|
72
|
+
const handle_update_value = (section, key, value) => {
|
|
73
|
+
config_provider.set(section, key, value);
|
|
74
|
+
load_config();
|
|
75
|
+
};
|
|
76
|
+
const current_section_data = selected_section ? sections[selected_section] : null;
|
|
77
|
+
return (_jsxs("div", { className: cn('cls_config_editor space-y-4', className), children: [_jsxs("div", { className: "cls_config_editor_header flex items-center justify-between border-b pb-2", children: [_jsx("h2", { className: "text-xl font-semibold", children: "Configuration Editor" }), _jsxs("div", { className: "flex gap-2", children: [_jsxs("button", { onClick: handle_refresh, className: "px-3 py-1 border rounded hover:bg-muted flex items-center gap-2", "aria-label": "Refresh", children: [_jsx(RefreshCw, { size: 16 }), "Refresh"] }), _jsxs("button", { onClick: handle_save, className: "px-3 py-1 bg-primary text-primary-foreground rounded hover:bg-primary/90 flex items-center gap-2", "aria-label": "Save", children: [_jsx(Save, { size: 16 }), "Save"] })] })] }), _jsxs("div", { className: "cls_config_editor_content grid grid-cols-1 md:grid-cols-3 gap-4", children: [_jsxs("div", { className: "cls_config_sections_list border rounded p-4", children: [_jsx("h3", { className: "font-semibold mb-2", children: "Sections" }), _jsx("div", { className: "space-y-1", children: Object.keys(sections).map((section_name) => (_jsx("button", { onClick: () => set_selected_section(section_name), className: cn('w-full text-left px-2 py-1 rounded text-sm', selected_section === section_name
|
|
78
|
+
? 'bg-primary text-primary-foreground'
|
|
79
|
+
: 'hover:bg-muted'), children: section_name }, section_name))) })] }), _jsx("div", { className: "cls_config_section_content md:col-span-2 border rounded p-4", children: selected_section ? (_jsxs(_Fragment, { children: [_jsx("h3", { className: "font-semibold mb-4", children: selected_section }), _jsxs("div", { className: "space-y-3", children: [current_section_data &&
|
|
80
|
+
Object.entries(current_section_data).map(([key, value]) => (_jsxs("div", { className: "cls_config_item", children: [_jsx("label", { className: "block text-sm font-medium mb-1", children: key }), _jsx("input", { type: "text", value: value, onChange: (e) => handle_update_value(selected_section, key, e.target.value), className: "w-full px-2 py-1 border rounded" })] }, key))), _jsxs("div", { className: "cls_add_new_key border-t pt-3 mt-3", children: [_jsx("h4", { className: "text-sm font-medium mb-2", children: "Add New Key" }), _jsxs("div", { className: "space-y-2", children: [_jsx("input", { type: "text", placeholder: "Key name", value: new_key, onChange: (e) => set_new_key(e.target.value), className: "w-full px-2 py-1 border rounded text-sm" }), _jsx("input", { type: "text", placeholder: "Value", value: new_value, onChange: (e) => set_new_value(e.target.value), className: "w-full px-2 py-1 border rounded text-sm" }), _jsx("button", { onClick: handle_add_key, className: "px-3 py-1 bg-primary text-primary-foreground rounded hover:bg-primary/90 text-sm", children: "Add Key" })] })] })] })] })) : (_jsx("div", { className: "text-center text-muted-foreground py-8", children: "Select a section to view/edit" })) })] })] }));
|
|
81
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ConfigProvider } from '@/lib/types';
|
|
3
|
+
/**
|
|
4
|
+
* Config viewer component props
|
|
5
|
+
*/
|
|
6
|
+
export interface ConfigViewerProps {
|
|
7
|
+
/**
|
|
8
|
+
* The config provider instance
|
|
9
|
+
*/
|
|
10
|
+
config_provider: ConfigProvider;
|
|
11
|
+
/**
|
|
12
|
+
* Additional CSS classes
|
|
13
|
+
*/
|
|
14
|
+
className?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Callback when config is updated
|
|
17
|
+
*/
|
|
18
|
+
on_update?: () => void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Config viewer component
|
|
22
|
+
* Displays configuration sections and allows viewing/editing values
|
|
23
|
+
* @param props - Component props
|
|
24
|
+
* @returns React component
|
|
25
|
+
*/
|
|
26
|
+
export declare const ConfigViewer: React.FC<ConfigViewerProps>;
|
|
27
|
+
//# sourceMappingURL=config_viewer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config_viewer.d.ts","sourceRoot":"","sources":["../../src/components/config_viewer.tsx"],"names":[],"mappings":"AAGA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAElD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAGjD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,eAAe,EAAE,cAAc,CAAA;IAC/B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAwIpD,CAAA"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Config viewer component for displaying configuration data
|
|
3
|
+
// Displays configuration sections and values in a readable format
|
|
4
|
+
import { useState, useEffect } from 'react';
|
|
5
|
+
import { cn } from '@/lib/utils';
|
|
6
|
+
import { Pencil, CheckCircle2, XCircle } from 'lucide-react';
|
|
7
|
+
/**
|
|
8
|
+
* Config viewer component
|
|
9
|
+
* Displays configuration sections and allows viewing/editing values
|
|
10
|
+
* @param props - Component props
|
|
11
|
+
* @returns React component
|
|
12
|
+
*/
|
|
13
|
+
export const ConfigViewer = ({ config_provider, className, on_update, }) => {
|
|
14
|
+
const [sections, set_sections] = useState({});
|
|
15
|
+
const [editing, set_editing] = useState(null);
|
|
16
|
+
const [edit_value, set_edit_value] = useState('');
|
|
17
|
+
/**
|
|
18
|
+
* Load configuration from provider
|
|
19
|
+
*/
|
|
20
|
+
const load_config = () => {
|
|
21
|
+
const all_sections = {};
|
|
22
|
+
// Try to get all sections - if provider has getAllSections, use it
|
|
23
|
+
if ('getAllSections' in config_provider && typeof config_provider.getAllSections === 'function') {
|
|
24
|
+
const all = config_provider.getAllSections();
|
|
25
|
+
Object.assign(all_sections, all);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
// Fallback: try common section names
|
|
29
|
+
const common_sections = ['database', 'logging', 'api', 'server', 'app'];
|
|
30
|
+
for (const section of common_sections) {
|
|
31
|
+
const section_data = config_provider.getSection(section);
|
|
32
|
+
if (section_data) {
|
|
33
|
+
all_sections[section] = section_data;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
set_sections(all_sections);
|
|
38
|
+
};
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
load_config();
|
|
41
|
+
}, [config_provider]);
|
|
42
|
+
/**
|
|
43
|
+
* Start editing a config value
|
|
44
|
+
*/
|
|
45
|
+
const start_edit = (section, key) => {
|
|
46
|
+
const value = config_provider.get(section, key) || '';
|
|
47
|
+
set_editing({ section, key });
|
|
48
|
+
set_edit_value(value);
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Cancel editing
|
|
52
|
+
*/
|
|
53
|
+
const cancel_edit = () => {
|
|
54
|
+
set_editing(null);
|
|
55
|
+
set_edit_value('');
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Save edited value
|
|
59
|
+
*/
|
|
60
|
+
const save_edit = () => {
|
|
61
|
+
if (editing) {
|
|
62
|
+
config_provider.set(editing.section, editing.key, edit_value);
|
|
63
|
+
config_provider.save();
|
|
64
|
+
load_config();
|
|
65
|
+
set_editing(null);
|
|
66
|
+
set_edit_value('');
|
|
67
|
+
if (on_update) {
|
|
68
|
+
on_update();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
return (_jsx("div", { className: cn('cls_config_viewer space-y-4', className), children: Object.keys(sections).length === 0 ? (_jsx("div", { className: "p-4 text-center text-muted-foreground", children: "No configuration sections found" })) : (Object.entries(sections).map(([section_name, section_data]) => (_jsxs("div", { className: "cls_config_section border rounded-lg p-4", children: [_jsx("h3", { className: "text-lg font-semibold mb-3", children: section_name }), _jsx("div", { className: "space-y-2", children: Object.entries(section_data).map(([key, value]) => {
|
|
73
|
+
const is_editing = editing?.section === section_name && editing?.key === key;
|
|
74
|
+
return (_jsx("div", { className: "cls_config_item flex items-center gap-2", children: _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "text-sm font-medium text-muted-foreground", children: key }), is_editing ? (_jsxs("div", { className: "flex items-center gap-2 mt-1", children: [_jsx("input", { type: "text", value: edit_value, onChange: (e) => set_edit_value(e.target.value), className: "flex-1 px-2 py-1 border rounded text-sm", autoFocus: true }), _jsx("button", { onClick: save_edit, className: "text-green-600 hover:text-green-700", "aria-label": "Save", children: _jsx(CheckCircle2, { size: 20 }) }), _jsx("button", { onClick: cancel_edit, className: "text-red-600 hover:text-red-700", "aria-label": "Cancel", children: _jsx(XCircle, { size: 20 }) })] })) : (_jsxs("div", { className: "flex items-center gap-2 mt-1", children: [_jsx("div", { className: "flex-1 px-2 py-1 bg-muted rounded text-sm", children: value || '(empty)' }), _jsx("button", { onClick: () => start_edit(section_name, key), className: "text-blue-600 hover:text-blue-700", "aria-label": "Edit", children: _jsx(Pencil, { size: 18 }) })] }))] }) }, key));
|
|
75
|
+
}) })] }, section_name)))) }));
|
|
76
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Example component props interface
|
|
4
|
+
*/
|
|
5
|
+
export interface ExampleComponentProps {
|
|
6
|
+
/**
|
|
7
|
+
* The title to display
|
|
8
|
+
*/
|
|
9
|
+
title?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Additional CSS classes
|
|
12
|
+
*/
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Example component for demonstrating the component library setup
|
|
17
|
+
* @param props - Component props
|
|
18
|
+
* @returns React component
|
|
19
|
+
*/
|
|
20
|
+
export declare const ExampleComponent: React.FC<ExampleComponentProps>;
|
|
21
|
+
//# sourceMappingURL=example_component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"example_component.d.ts","sourceRoot":"","sources":["../../src/components/example_component.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAY5D,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from '@/lib/utils';
|
|
3
|
+
/**
|
|
4
|
+
* Example component for demonstrating the component library setup
|
|
5
|
+
* @param props - Component props
|
|
6
|
+
* @returns React component
|
|
7
|
+
*/
|
|
8
|
+
export const ExampleComponent = ({ title = 'Example Component', className, }) => {
|
|
9
|
+
return (_jsxs("div", { className: cn('cls_example_component p-4 border rounded-lg', className), children: [_jsx("h2", { className: "text-xl font-semibold", children: title }), _jsx("p", { className: "text-muted-foreground mt-2", children: "This is an example component for the config management library." })] }));
|
|
10
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { ExampleComponent } from './example_component';
|
|
2
|
+
export { ConfigViewer } from './config_viewer';
|
|
3
|
+
export { ConfigEditor } from './config_editor';
|
|
4
|
+
export type { ConfigViewerProps } from './config_viewer';
|
|
5
|
+
export type { ConfigEditorProps } from './config_editor';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACxD,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,OAAO,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
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
|
+
import type { ConfigProvider, HazoConfigOptions } from './types';
|
|
10
|
+
/**
|
|
11
|
+
* HazoConfig class
|
|
12
|
+
*
|
|
13
|
+
* Implements ConfigProvider interface for managing INI configuration files.
|
|
14
|
+
* Provides sync read/write operations with in-memory caching.
|
|
15
|
+
*/
|
|
16
|
+
export declare class HazoConfig implements ConfigProvider {
|
|
17
|
+
private filePath;
|
|
18
|
+
private logger;
|
|
19
|
+
private config;
|
|
20
|
+
/**
|
|
21
|
+
* Constructor
|
|
22
|
+
* @param options - Configuration options including filePath and optional logger
|
|
23
|
+
* @throws Error if file doesn't exist
|
|
24
|
+
*/
|
|
25
|
+
constructor(options: HazoConfigOptions);
|
|
26
|
+
/**
|
|
27
|
+
* Get a configuration value by section and key
|
|
28
|
+
* @param section - The configuration section name
|
|
29
|
+
* @param key - The configuration key name
|
|
30
|
+
* @returns The configuration value, or undefined if not found
|
|
31
|
+
*/
|
|
32
|
+
get(section: string, key: string): string | undefined;
|
|
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
|
+
/**
|
|
40
|
+
* Set a configuration value
|
|
41
|
+
* @param section - The configuration section name
|
|
42
|
+
* @param key - The configuration key name
|
|
43
|
+
* @param value - The value to set
|
|
44
|
+
*/
|
|
45
|
+
set(section: string, key: string, value: string): void;
|
|
46
|
+
/**
|
|
47
|
+
* Save the current configuration to disk
|
|
48
|
+
* Writes all changes immediately to the configuration file
|
|
49
|
+
*/
|
|
50
|
+
save(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Refresh the configuration from disk
|
|
53
|
+
* Reloads the configuration file and updates the in-memory cache
|
|
54
|
+
*/
|
|
55
|
+
refresh(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Get the file path being managed
|
|
58
|
+
* @returns The resolved file path
|
|
59
|
+
*/
|
|
60
|
+
getFilePath(): string;
|
|
61
|
+
/**
|
|
62
|
+
* Get all configuration sections
|
|
63
|
+
* @returns A record of all sections and their key-value pairs
|
|
64
|
+
*/
|
|
65
|
+
getAllSections(): Record<string, Record<string, string>>;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=config-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/lib/config-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAU,MAAM,SAAS,CAAA;AAaxE;;;;;GAKG;AACH,qBAAa,UAAW,YAAW,cAAc;IAC/C,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,MAAM,CAA6C;IAE3D;;;;OAIG;gBACS,OAAO,EAAE,iBAAiB;IAgBtC;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIrD;;;;OAIG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS;IAI/D;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAQtD;;;OAGG;IACH,IAAI,IAAI,IAAI;IAqBZ;;;OAGG;IACH,OAAO,IAAI,IAAI;IAuCf;;;OAGG;IACH,WAAW,IAAI,MAAM;IAIrB;;;OAGG;IACH,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAQzD"}
|
|
@@ -0,0 +1,177 @@
|
|
|
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
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import ini from 'ini';
|
|
12
|
+
import { ConfigErrorCode as EC } from './types';
|
|
13
|
+
/**
|
|
14
|
+
* No-op logger implementation (default when no logger provided)
|
|
15
|
+
*/
|
|
16
|
+
const no_op_logger = {
|
|
17
|
+
debug: () => { },
|
|
18
|
+
info: () => { },
|
|
19
|
+
warn: () => { },
|
|
20
|
+
error: () => { }
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* HazoConfig class
|
|
24
|
+
*
|
|
25
|
+
* Implements ConfigProvider interface for managing INI configuration files.
|
|
26
|
+
* Provides sync read/write operations with in-memory caching.
|
|
27
|
+
*/
|
|
28
|
+
export class HazoConfig {
|
|
29
|
+
/**
|
|
30
|
+
* Constructor
|
|
31
|
+
* @param options - Configuration options including filePath and optional logger
|
|
32
|
+
* @throws Error if file doesn't exist
|
|
33
|
+
*/
|
|
34
|
+
constructor(options) {
|
|
35
|
+
Object.defineProperty(this, "filePath", {
|
|
36
|
+
enumerable: true,
|
|
37
|
+
configurable: true,
|
|
38
|
+
writable: true,
|
|
39
|
+
value: void 0
|
|
40
|
+
});
|
|
41
|
+
Object.defineProperty(this, "logger", {
|
|
42
|
+
enumerable: true,
|
|
43
|
+
configurable: true,
|
|
44
|
+
writable: true,
|
|
45
|
+
value: void 0
|
|
46
|
+
});
|
|
47
|
+
Object.defineProperty(this, "config", {
|
|
48
|
+
enumerable: true,
|
|
49
|
+
configurable: true,
|
|
50
|
+
writable: true,
|
|
51
|
+
value: {}
|
|
52
|
+
});
|
|
53
|
+
this.filePath = path.resolve(options.filePath);
|
|
54
|
+
this.logger = options.logger || no_op_logger;
|
|
55
|
+
// Validate file exists
|
|
56
|
+
if (!fs.existsSync(this.filePath)) {
|
|
57
|
+
const error = new Error(`Configuration file not found: ${this.filePath}`);
|
|
58
|
+
error.code = EC.FILE_NOT_FOUND;
|
|
59
|
+
this.logger.error(`[HazoConfig] Configuration file not found: ${this.filePath}`);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
// Load initial configuration
|
|
63
|
+
this.refresh();
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Get a configuration value by section and key
|
|
67
|
+
* @param section - The configuration section name
|
|
68
|
+
* @param key - The configuration key name
|
|
69
|
+
* @returns The configuration value, or undefined if not found
|
|
70
|
+
*/
|
|
71
|
+
get(section, key) {
|
|
72
|
+
return this.config[section]?.[key];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get an entire configuration section
|
|
76
|
+
* @param section - The configuration section name
|
|
77
|
+
* @returns A record of key-value pairs for the section, or undefined if section doesn't exist
|
|
78
|
+
*/
|
|
79
|
+
getSection(section) {
|
|
80
|
+
return this.config[section] ? { ...this.config[section] } : undefined;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Set a configuration value
|
|
84
|
+
* @param section - The configuration section name
|
|
85
|
+
* @param key - The configuration key name
|
|
86
|
+
* @param value - The value to set
|
|
87
|
+
*/
|
|
88
|
+
set(section, key, value) {
|
|
89
|
+
if (!this.config[section]) {
|
|
90
|
+
this.config[section] = {};
|
|
91
|
+
}
|
|
92
|
+
this.config[section][key] = value;
|
|
93
|
+
this.logger.info(`[HazoConfig] Set config value: ${section}.${key}`, { value });
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Save the current configuration to disk
|
|
97
|
+
* Writes all changes immediately to the configuration file
|
|
98
|
+
*/
|
|
99
|
+
save() {
|
|
100
|
+
try {
|
|
101
|
+
// Convert config object back to INI format
|
|
102
|
+
const iniContent = ini.stringify(this.config, {
|
|
103
|
+
section: '[',
|
|
104
|
+
whitespace: true
|
|
105
|
+
});
|
|
106
|
+
// Write to file
|
|
107
|
+
fs.writeFileSync(this.filePath, iniContent, 'utf-8');
|
|
108
|
+
this.logger.info(`[HazoConfig] Configuration saved to: ${this.filePath}`);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
const configError = new Error(`Failed to save configuration: ${error.message || String(error)}`);
|
|
112
|
+
configError.code = EC.WRITE_ERROR;
|
|
113
|
+
configError.originalError = error;
|
|
114
|
+
this.logger.error(`[HazoConfig] Failed to save configuration: ${this.filePath}`, { error: String(error) });
|
|
115
|
+
throw configError;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Refresh the configuration from disk
|
|
120
|
+
* Reloads the configuration file and updates the in-memory cache
|
|
121
|
+
*/
|
|
122
|
+
refresh() {
|
|
123
|
+
try {
|
|
124
|
+
// Read file content
|
|
125
|
+
const content = fs.readFileSync(this.filePath, 'utf-8');
|
|
126
|
+
// Parse INI content
|
|
127
|
+
const parsed = ini.parse(content);
|
|
128
|
+
// Convert to our internal format (ensure all values are strings)
|
|
129
|
+
this.config = {};
|
|
130
|
+
for (const [section, values] of Object.entries(parsed)) {
|
|
131
|
+
if (typeof values === 'object' && values !== null) {
|
|
132
|
+
this.config[section] = {};
|
|
133
|
+
for (const [key, value] of Object.entries(values)) {
|
|
134
|
+
this.config[section][key] = String(value);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
this.logger.info(`[HazoConfig] Configuration refreshed from: ${this.filePath}`, {
|
|
139
|
+
sections: Object.keys(this.config)
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
// Handle file read errors
|
|
144
|
+
if (error.code === 'ENOENT') {
|
|
145
|
+
const configError = new Error(`Configuration file not found: ${this.filePath}`);
|
|
146
|
+
configError.code = EC.FILE_NOT_FOUND;
|
|
147
|
+
this.logger.error(`[HazoConfig] Configuration file not found: ${this.filePath}`);
|
|
148
|
+
throw configError;
|
|
149
|
+
}
|
|
150
|
+
// Handle parse errors
|
|
151
|
+
const configError = new Error(`Failed to parse configuration: ${error.message || String(error)}`);
|
|
152
|
+
configError.code = EC.PARSE_ERROR;
|
|
153
|
+
configError.originalError = error;
|
|
154
|
+
this.logger.error(`[HazoConfig] Failed to parse configuration: ${this.filePath}`, { error: String(error) });
|
|
155
|
+
throw configError;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get the file path being managed
|
|
160
|
+
* @returns The resolved file path
|
|
161
|
+
*/
|
|
162
|
+
getFilePath() {
|
|
163
|
+
return this.filePath;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Get all configuration sections
|
|
167
|
+
* @returns A record of all sections and their key-value pairs
|
|
168
|
+
*/
|
|
169
|
+
getAllSections() {
|
|
170
|
+
// Return a deep copy to prevent external modification
|
|
171
|
+
const result = {};
|
|
172
|
+
for (const [section, values] of Object.entries(this.config)) {
|
|
173
|
+
result[section] = { ...values };
|
|
174
|
+
}
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
+
export { HazoConfig } from './config-loader';
|
|
9
|
+
export { MockConfigProvider } from './mock_config_provider';
|
|
10
|
+
export { ConfigErrorCode } from './types';
|
|
11
|
+
export type { ConfigProvider, HazoConfigOptions, Logger, HazoConfigError } from './types';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,YAAY,EACV,cAAc,EACd,iBAAiB,EACjB,MAAM,EACN,eAAe,EAChB,MAAM,SAAS,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
export { HazoConfig } from './config-loader';
|
|
9
|
+
export { MockConfigProvider } from './mock_config_provider';
|
|
10
|
+
export { ConfigErrorCode } from './types';
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ConfigProvider } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Mock config provider that stores configuration in memory
|
|
4
|
+
* Useful for testing and Storybook demonstrations where file system is not available
|
|
5
|
+
*/
|
|
6
|
+
export declare class MockConfigProvider implements ConfigProvider {
|
|
7
|
+
private config;
|
|
8
|
+
/**
|
|
9
|
+
* Constructor
|
|
10
|
+
* @param initial_config - Optional initial configuration data
|
|
11
|
+
*/
|
|
12
|
+
constructor(initial_config?: Record<string, Record<string, string>>);
|
|
13
|
+
/**
|
|
14
|
+
* Get a configuration value by section and key
|
|
15
|
+
* @param section - The configuration section name
|
|
16
|
+
* @param key - The configuration key name
|
|
17
|
+
* @returns The configuration value, or undefined if not found
|
|
18
|
+
*/
|
|
19
|
+
get(section: string, key: string): string | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Get an entire configuration section
|
|
22
|
+
* @param section - The configuration section name
|
|
23
|
+
* @returns A record of key-value pairs for the section, or undefined if section doesn't exist
|
|
24
|
+
*/
|
|
25
|
+
getSection(section: string): Record<string, string> | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Set a configuration value
|
|
28
|
+
* @param section - The configuration section name
|
|
29
|
+
* @param key - The configuration key name
|
|
30
|
+
* @param value - The value to set
|
|
31
|
+
*/
|
|
32
|
+
set(section: string, key: string, value: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* Save the current configuration (no-op for mock, but maintains interface compatibility)
|
|
35
|
+
*/
|
|
36
|
+
save(): void;
|
|
37
|
+
/**
|
|
38
|
+
* Refresh the configuration (no-op for mock, but maintains interface compatibility)
|
|
39
|
+
*/
|
|
40
|
+
refresh(): void;
|
|
41
|
+
/**
|
|
42
|
+
* Get all configuration sections
|
|
43
|
+
* @returns A record of all sections and their key-value pairs
|
|
44
|
+
*/
|
|
45
|
+
getAllSections(): Record<string, Record<string, string>>;
|
|
46
|
+
/**
|
|
47
|
+
* Reset the configuration to initial state or empty
|
|
48
|
+
* @param new_config - Optional new configuration to set
|
|
49
|
+
*/
|
|
50
|
+
reset(new_config?: Record<string, Record<string, string>>): void;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=mock_config_provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock_config_provider.d.ts","sourceRoot":"","sources":["../../src/lib/mock_config_provider.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C;;;GAGG;AACH,qBAAa,kBAAmB,YAAW,cAAc;IACvD,OAAO,CAAC,MAAM,CAA6C;IAE3D;;;OAGG;gBACS,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAMnE;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIrD;;;;OAIG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS;IAI/D;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAOtD;;OAEG;IACH,IAAI,IAAI,IAAI;IAIZ;;OAEG;IACH,OAAO,IAAI,IAAI;IAIf;;;OAGG;IACH,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAQxD;;;OAGG;IACH,KAAK,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,IAAI;CAOjE"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Mock config provider for browser/Storybook testing
|
|
2
|
+
// Implements ConfigProvider interface using in-memory storage instead of file system
|
|
3
|
+
/**
|
|
4
|
+
* Mock config provider that stores configuration in memory
|
|
5
|
+
* Useful for testing and Storybook demonstrations where file system is not available
|
|
6
|
+
*/
|
|
7
|
+
export class MockConfigProvider {
|
|
8
|
+
/**
|
|
9
|
+
* Constructor
|
|
10
|
+
* @param initial_config - Optional initial configuration data
|
|
11
|
+
*/
|
|
12
|
+
constructor(initial_config) {
|
|
13
|
+
Object.defineProperty(this, "config", {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
configurable: true,
|
|
16
|
+
writable: true,
|
|
17
|
+
value: {}
|
|
18
|
+
});
|
|
19
|
+
if (initial_config) {
|
|
20
|
+
this.config = JSON.parse(JSON.stringify(initial_config)); // Deep copy
|
|
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, key) {
|
|
30
|
+
return this.config[section]?.[key];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get an entire configuration section
|
|
34
|
+
* @param section - The configuration section name
|
|
35
|
+
* @returns A record of key-value pairs for the section, or undefined if section doesn't exist
|
|
36
|
+
*/
|
|
37
|
+
getSection(section) {
|
|
38
|
+
return this.config[section] ? { ...this.config[section] } : undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Set a configuration value
|
|
42
|
+
* @param section - The configuration section name
|
|
43
|
+
* @param key - The configuration key name
|
|
44
|
+
* @param value - The value to set
|
|
45
|
+
*/
|
|
46
|
+
set(section, key, value) {
|
|
47
|
+
if (!this.config[section]) {
|
|
48
|
+
this.config[section] = {};
|
|
49
|
+
}
|
|
50
|
+
this.config[section][key] = value;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Save the current configuration (no-op for mock, but maintains interface compatibility)
|
|
54
|
+
*/
|
|
55
|
+
save() {
|
|
56
|
+
// No-op for mock provider
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Refresh the configuration (no-op for mock, but maintains interface compatibility)
|
|
60
|
+
*/
|
|
61
|
+
refresh() {
|
|
62
|
+
// No-op for mock provider
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get all configuration sections
|
|
66
|
+
* @returns A record of all sections and their key-value pairs
|
|
67
|
+
*/
|
|
68
|
+
getAllSections() {
|
|
69
|
+
const result = {};
|
|
70
|
+
for (const [section, values] of Object.entries(this.config)) {
|
|
71
|
+
result[section] = { ...values };
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Reset the configuration to initial state or empty
|
|
77
|
+
* @param new_config - Optional new configuration to set
|
|
78
|
+
*/
|
|
79
|
+
reset(new_config) {
|
|
80
|
+
if (new_config) {
|
|
81
|
+
this.config = JSON.parse(JSON.stringify(new_config));
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
this.config = {};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
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
|
+
* Logger interface for dependency injection
|
|
11
|
+
* Matches the Logger interface from hazo_connect for consistency
|
|
12
|
+
*/
|
|
13
|
+
export interface Logger {
|
|
14
|
+
debug(message: string, data?: any): void;
|
|
15
|
+
info(message: string, data?: any): void;
|
|
16
|
+
warn(message: string, data?: any): void;
|
|
17
|
+
error(message: string, data?: any): void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* ConfigProvider interface
|
|
21
|
+
*
|
|
22
|
+
* Defines the contract for configuration providers that can be used
|
|
23
|
+
* by other components (like hazo_connect) to access configuration values.
|
|
24
|
+
* This allows for dependency injection and makes components testable.
|
|
25
|
+
*/
|
|
26
|
+
export interface ConfigProvider {
|
|
27
|
+
/**
|
|
28
|
+
* Get a configuration value by section and key
|
|
29
|
+
* @param section - The configuration section name
|
|
30
|
+
* @param key - The configuration key name
|
|
31
|
+
* @returns The configuration value, or undefined if not found
|
|
32
|
+
*/
|
|
33
|
+
get(section: string, key: string): string | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Get an entire configuration section
|
|
36
|
+
* @param section - The configuration section name
|
|
37
|
+
* @returns A record of key-value pairs for the section, or undefined if section doesn't exist
|
|
38
|
+
*/
|
|
39
|
+
getSection(section: string): Record<string, string> | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Set a configuration value
|
|
42
|
+
* @param section - The configuration section name
|
|
43
|
+
* @param key - The configuration key name
|
|
44
|
+
* @param value - The value to set
|
|
45
|
+
*/
|
|
46
|
+
set(section: string, key: string, value: string): void;
|
|
47
|
+
/**
|
|
48
|
+
* Save the current configuration to disk
|
|
49
|
+
* Writes all changes immediately to the configuration file
|
|
50
|
+
*/
|
|
51
|
+
save(): void;
|
|
52
|
+
/**
|
|
53
|
+
* Refresh the configuration from disk
|
|
54
|
+
* Reloads the configuration file and updates the in-memory cache
|
|
55
|
+
*/
|
|
56
|
+
refresh(): void;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Options for initializing HazoConfig
|
|
60
|
+
*/
|
|
61
|
+
export interface HazoConfigOptions {
|
|
62
|
+
/**
|
|
63
|
+
* Path to the configuration file (required)
|
|
64
|
+
* Can be absolute or relative to process.cwd()
|
|
65
|
+
*/
|
|
66
|
+
filePath: string;
|
|
67
|
+
/**
|
|
68
|
+
* Optional logger instance for logging operations
|
|
69
|
+
*/
|
|
70
|
+
logger?: Logger;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Error codes for configuration operations
|
|
74
|
+
*/
|
|
75
|
+
export declare enum ConfigErrorCode {
|
|
76
|
+
FILE_NOT_FOUND = "HAZO_CONFIG_FILE_NOT_FOUND",
|
|
77
|
+
READ_ERROR = "HAZO_CONFIG_READ_ERROR",
|
|
78
|
+
WRITE_ERROR = "HAZO_CONFIG_WRITE_ERROR",
|
|
79
|
+
PARSE_ERROR = "HAZO_CONFIG_PARSE_ERROR",
|
|
80
|
+
VALIDATION_ERROR = "HAZO_CONFIG_VALIDATION_ERROR"
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Standardized error response for configuration operations
|
|
84
|
+
*/
|
|
85
|
+
export interface HazoConfigError {
|
|
86
|
+
code: string;
|
|
87
|
+
message: string;
|
|
88
|
+
filePath?: string;
|
|
89
|
+
originalError?: any;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;GAGG;AACH,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI,CAAA;IACxC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI,CAAA;IACvC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI,CAAA;IACvC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI,CAAA;CACzC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IAErD;;;;OAIG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;IAE/D;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IAEtD;;;OAGG;IACH,IAAI,IAAI,IAAI,CAAA;IAEZ;;;OAGG;IACH,OAAO,IAAI,IAAI,CAAA;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAA;IAEhB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;GAEG;AACH,oBAAY,eAAe;IACzB,cAAc,+BAA+B;IAC7C,UAAU,2BAA2B;IACrC,WAAW,4BAA4B;IACvC,WAAW,4BAA4B;IACvC,gBAAgB,iCAAiC;CAClD;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,GAAG,CAAA;CACpB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
* Error codes for configuration operations
|
|
11
|
+
*/
|
|
12
|
+
export var ConfigErrorCode;
|
|
13
|
+
(function (ConfigErrorCode) {
|
|
14
|
+
ConfigErrorCode["FILE_NOT_FOUND"] = "HAZO_CONFIG_FILE_NOT_FOUND";
|
|
15
|
+
ConfigErrorCode["READ_ERROR"] = "HAZO_CONFIG_READ_ERROR";
|
|
16
|
+
ConfigErrorCode["WRITE_ERROR"] = "HAZO_CONFIG_WRITE_ERROR";
|
|
17
|
+
ConfigErrorCode["PARSE_ERROR"] = "HAZO_CONFIG_PARSE_ERROR";
|
|
18
|
+
ConfigErrorCode["VALIDATION_ERROR"] = "HAZO_CONFIG_VALIDATION_ERROR";
|
|
19
|
+
})(ConfigErrorCode || (ConfigErrorCode = {}));
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ClassValue } from "clsx";
|
|
2
|
+
/**
|
|
3
|
+
* Merges class names using clsx and tailwind-merge
|
|
4
|
+
* @param inputs - Class values to merge
|
|
5
|
+
* @returns Merged class string
|
|
6
|
+
*/
|
|
7
|
+
export declare function cn(...inputs: ClassValue[]): string;
|
|
8
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,UAAU,EAAQ,MAAM,MAAM,CAAA;AAG5C;;;;GAIG;AACH,wBAAgB,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,UAEzC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Utility functions for the component library
|
|
2
|
+
// Provides class name merging functionality using clsx and tailwind-merge
|
|
3
|
+
import { clsx } from "clsx";
|
|
4
|
+
import { twMerge } from "tailwind-merge";
|
|
5
|
+
/**
|
|
6
|
+
* Merges class names using clsx and tailwind-merge
|
|
7
|
+
* @param inputs - Class values to merge
|
|
8
|
+
* @returns Merged class string
|
|
9
|
+
*/
|
|
10
|
+
export function cn(...inputs) {
|
|
11
|
+
return twMerge(clsx(inputs));
|
|
12
|
+
}
|
package/package.json
CHANGED
|
@@ -37,7 +37,6 @@ export const ConfigEditor: React.FC<ConfigEditorProps> = ({
|
|
|
37
37
|
}) => {
|
|
38
38
|
const [sections, set_sections] = useState<Record<string, Record<string, string>>>({})
|
|
39
39
|
const [selected_section, set_selected_section] = useState<string | null>(null)
|
|
40
|
-
const [new_section_name, set_new_section_name] = useState<string>('')
|
|
41
40
|
const [new_key, set_new_key] = useState<string>('')
|
|
42
41
|
const [new_value, set_new_value] = useState<string>('')
|
|
43
42
|
|
package/src/lib/config-loader.ts
CHANGED
|
@@ -16,7 +16,8 @@ import { ConfigErrorCode as EC } from './types'
|
|
|
16
16
|
/**
|
|
17
17
|
* No-op logger implementation (default when no logger provided)
|
|
18
18
|
*/
|
|
19
|
-
const
|
|
19
|
+
const no_op_logger: Logger = {
|
|
20
|
+
debug: () => {},
|
|
20
21
|
info: () => {},
|
|
21
22
|
warn: () => {},
|
|
22
23
|
error: () => {}
|
|
@@ -32,7 +33,6 @@ export class HazoConfig implements ConfigProvider {
|
|
|
32
33
|
private filePath: string
|
|
33
34
|
private logger: Logger
|
|
34
35
|
private config: Record<string, Record<string, string>> = {}
|
|
35
|
-
private originalContent: string = ''
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* Constructor
|
|
@@ -41,7 +41,7 @@ export class HazoConfig implements ConfigProvider {
|
|
|
41
41
|
*/
|
|
42
42
|
constructor(options: HazoConfigOptions) {
|
|
43
43
|
this.filePath = path.resolve(options.filePath)
|
|
44
|
-
this.logger = options.logger ||
|
|
44
|
+
this.logger = options.logger || no_op_logger
|
|
45
45
|
|
|
46
46
|
// Validate file exists
|
|
47
47
|
if (!fs.existsSync(this.filePath)) {
|
|
@@ -97,16 +97,12 @@ export class HazoConfig implements ConfigProvider {
|
|
|
97
97
|
// Convert config object back to INI format
|
|
98
98
|
const iniContent = ini.stringify(this.config, {
|
|
99
99
|
section: '[',
|
|
100
|
-
whitespace: true
|
|
101
|
-
newline: '\n'
|
|
100
|
+
whitespace: true
|
|
102
101
|
})
|
|
103
102
|
|
|
104
103
|
// Write to file
|
|
105
104
|
fs.writeFileSync(this.filePath, iniContent, 'utf-8')
|
|
106
105
|
|
|
107
|
-
// Update original content for future reference
|
|
108
|
-
this.originalContent = iniContent
|
|
109
|
-
|
|
110
106
|
this.logger.info(`[HazoConfig] Configuration saved to: ${this.filePath}`)
|
|
111
107
|
} catch (error: any) {
|
|
112
108
|
const configError: any = new Error(`Failed to save configuration: ${error.message || String(error)}`)
|
|
@@ -125,8 +121,6 @@ export class HazoConfig implements ConfigProvider {
|
|
|
125
121
|
try {
|
|
126
122
|
// Read file content
|
|
127
123
|
const content = fs.readFileSync(this.filePath, 'utf-8')
|
|
128
|
-
this.originalContent = content
|
|
129
|
-
|
|
130
124
|
// Parse INI content
|
|
131
125
|
const parsed = ini.parse(content)
|
|
132
126
|
|
package/src/lib/types.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* Matches the Logger interface from hazo_connect for consistency
|
|
13
13
|
*/
|
|
14
14
|
export interface Logger {
|
|
15
|
+
debug(message: string, data?: any): void
|
|
15
16
|
info(message: string, data?: any): void
|
|
16
17
|
warn(message: string, data?: any): void
|
|
17
18
|
error(message: string, data?: any): void
|
package/tsconfig.build.json
CHANGED