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.
@@ -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
+
@@ -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
+
@@ -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
+
@@ -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
+ /** @type {import('tailwindcss').Config} */
2
+ module.exports = {
3
+ darkMode: ["class"],
4
+ content: [
5
+ "./src/**/*.{js,ts,jsx,tsx}",
6
+ "./.storybook/**/*.{js,ts,jsx,tsx}",
7
+ ],
8
+ theme: {
9
+ extend: {},
10
+ },
11
+ plugins: [],
12
+ }
13
+
@@ -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
+