@obinexusmk2/hypernum 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +256 -0
- package/dist/index.cjs +3425 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +1449 -0
- package/dist/index.js +3284 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.js +8 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/types/config/config-loader.d.ts +56 -0
- package/dist/types/config/config-loader.d.ts.map +1 -0
- package/dist/types/config/config-parser.d.ts +28 -0
- package/dist/types/config/config-parser.d.ts.map +1 -0
- package/dist/types/config/config-resolver.d.ts +21 -0
- package/dist/types/config/config-resolver.d.ts.map +1 -0
- package/dist/types/config/config-source.d.ts +27 -0
- package/dist/types/config/config-source.d.ts.map +1 -0
- package/dist/types/config/index.d.ts +68 -0
- package/dist/types/config/index.d.ts.map +1 -0
- package/dist/types/core/common.d.ts +169 -0
- package/dist/types/core/common.d.ts.map +1 -0
- package/dist/types/core/config.d.ts +197 -0
- package/dist/types/core/config.d.ts.map +1 -0
- package/dist/types/core/constants.d.ts +88 -0
- package/dist/types/core/constants.d.ts.map +1 -0
- package/dist/types/core/errors.d.ts +97 -0
- package/dist/types/core/errors.d.ts.map +1 -0
- package/dist/types/core/hypernum.d.ts +60 -0
- package/dist/types/core/hypernum.d.ts.map +1 -0
- package/dist/types/core/index.d.ts +6 -0
- package/dist/types/core/index.d.ts.map +1 -0
- package/dist/types/index.d.ts +33 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/operations/arithmetic.d.ts +72 -0
- package/dist/types/operations/arithmetic.d.ts.map +1 -0
- package/dist/types/operations/bitwise.d.ts +98 -0
- package/dist/types/operations/bitwise.d.ts.map +1 -0
- package/dist/types/operations/comparison.d.ts +94 -0
- package/dist/types/operations/comparison.d.ts.map +1 -0
- package/dist/types/operations/conversion.d.ts +79 -0
- package/dist/types/operations/conversion.d.ts.map +1 -0
- package/dist/types/operations/factorial.d.ts +58 -0
- package/dist/types/operations/factorial.d.ts.map +1 -0
- package/dist/types/operations/index.d.ts +6 -0
- package/dist/types/operations/index.d.ts.map +1 -0
- package/dist/types/operations/power.d.ts +49 -0
- package/dist/types/operations/power.d.ts.map +1 -0
- package/dist/types/storage/Heap.d.ts +95 -0
- package/dist/types/storage/Heap.d.ts.map +1 -0
- package/dist/types/storage/index.d.ts +2 -0
- package/dist/types/storage/index.d.ts.map +1 -0
- package/dist/types/structures/ackermann.d.ts +74 -0
- package/dist/types/structures/ackermann.d.ts.map +1 -0
- package/dist/types/structures/big-array.d.ts +102 -0
- package/dist/types/structures/big-array.d.ts.map +1 -0
- package/dist/types/structures/index.d.ts +5 -0
- package/dist/types/structures/index.d.ts.map +1 -0
- package/dist/types/structures/number-tree.d.ts +114 -0
- package/dist/types/structures/number-tree.d.ts.map +1 -0
- package/dist/types/structures/power-tower.d.ts +74 -0
- package/dist/types/structures/power-tower.d.ts.map +1 -0
- package/dist/types/utils/formatting.d.ts +45 -0
- package/dist/types/utils/formatting.d.ts.map +1 -0
- package/dist/types/utils/index.d.ts +5 -0
- package/dist/types/utils/index.d.ts.map +1 -0
- package/dist/types/utils/parser.d.ts +39 -0
- package/dist/types/utils/parser.d.ts.map +1 -0
- package/dist/types/utils/precision.d.ts +57 -0
- package/dist/types/utils/precision.d.ts.map +1 -0
- package/dist/types/utils/validation.d.ts +28 -0
- package/dist/types/utils/validation.d.ts.map +1 -0
- package/package.json +164 -0
- package/rollup.config.js +162 -0
- package/src/config/config-loader.ts +226 -0
- package/src/config/config-parser.ts +161 -0
- package/src/config/config-resolver.ts +52 -0
- package/src/config/config-source.ts +32 -0
- package/src/config/index.ts +159 -0
- package/src/core/common.ts +185 -0
- package/src/core/config.ts +393 -0
- package/src/core/constants.ts +102 -0
- package/src/core/errors.ts +203 -0
- package/src/core/hypernum.ts +241 -0
- package/src/core/index.ts +5 -0
- package/src/index.ts +183 -0
- package/src/operations/arithmetic.ts +333 -0
- package/src/operations/bitwise.ts +367 -0
- package/src/operations/comparison.ts +272 -0
- package/src/operations/conversion.ts +400 -0
- package/src/operations/factorial.ts +279 -0
- package/src/operations/index.ts +5 -0
- package/src/operations/power.ts +316 -0
- package/src/storage/Heap.ts +238 -0
- package/src/storage/index.ts +1 -0
- package/src/structures/ackermann.ts +233 -0
- package/src/structures/big-array.ts +306 -0
- package/src/structures/index.ts +4 -0
- package/src/structures/number-tree.ts +404 -0
- package/src/structures/power-tower.ts +278 -0
- package/src/types/common.d.ts +357 -0
- package/src/types/core.d.ts +161 -0
- package/src/types/index.d.ts +2 -0
- package/src/utils/formatting.ts +246 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/parser.ts +245 -0
- package/src/utils/precision.ts +217 -0
- package/src/utils/validation.ts +183 -0
- package/tsconfig.json +84 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* src/config/config-loader.ts
|
|
4
|
+
* Responsible for finding and loading configuration files
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { HypernumConfig, mergeConfig } from "@/core";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { ConfigParser } from "./config-parser";
|
|
10
|
+
import { ConfigSource } from "./config-source";
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
export interface ConfigSearchOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Whether to search in parent directories
|
|
17
|
+
*/
|
|
18
|
+
searchParentDirs?: boolean;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Additional paths to search
|
|
22
|
+
*/
|
|
23
|
+
additionalPaths?: string[];
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Base directory to start searching from
|
|
27
|
+
*/
|
|
28
|
+
baseDir?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class ConfigLoader {
|
|
32
|
+
private readonly DEFAULT_CONFIG_FILES = [
|
|
33
|
+
'.hypernumrc',
|
|
34
|
+
'.hypernumrc.json',
|
|
35
|
+
'.hypernumrc.js',
|
|
36
|
+
'hypernum.config.js',
|
|
37
|
+
'hypernum.config.json',
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
private readonly DEFAULT_CONFIG_PATHS = [
|
|
41
|
+
process.cwd(),
|
|
42
|
+
path.join(process.cwd(), 'config'),
|
|
43
|
+
path.resolve(process.env['HOME'] || process.env['USERPROFILE'] || '', '.hypernum')
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
private configParser: ConfigParser;
|
|
47
|
+
private cachedConfig: HypernumConfig | null = null;
|
|
48
|
+
|
|
49
|
+
constructor() {
|
|
50
|
+
this.configParser = new ConfigParser();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Finds configuration files in standard locations
|
|
55
|
+
*/
|
|
56
|
+
public findConfigFiles(options: ConfigSearchOptions = {}): Map<ConfigSource, string> {
|
|
57
|
+
const {
|
|
58
|
+
searchParentDirs = true,
|
|
59
|
+
additionalPaths = [],
|
|
60
|
+
baseDir = process.cwd()
|
|
61
|
+
} = options;
|
|
62
|
+
|
|
63
|
+
const searchPaths = [
|
|
64
|
+
...this.DEFAULT_CONFIG_PATHS,
|
|
65
|
+
...additionalPaths
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const configFiles = new Map<ConfigSource, string>();
|
|
69
|
+
|
|
70
|
+
// Search for configuration files
|
|
71
|
+
for (const searchPath of searchPaths) {
|
|
72
|
+
for (const filename of this.DEFAULT_CONFIG_FILES) {
|
|
73
|
+
const filePath = path.join(searchPath, filename);
|
|
74
|
+
|
|
75
|
+
if (fs.existsSync(filePath)) {
|
|
76
|
+
const source = this.getConfigSourceFromFilename(filename);
|
|
77
|
+
configFiles.set(source, filePath);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Search parent directories if enabled
|
|
83
|
+
if (searchParentDirs && baseDir !== path.parse(baseDir).root) {
|
|
84
|
+
let currentDir = baseDir;
|
|
85
|
+
let parentDir = path.dirname(currentDir);
|
|
86
|
+
|
|
87
|
+
while (currentDir !== parentDir) {
|
|
88
|
+
for (const filename of this.DEFAULT_CONFIG_FILES) {
|
|
89
|
+
const filePath = path.join(currentDir, filename);
|
|
90
|
+
|
|
91
|
+
if (fs.existsSync(filePath)) {
|
|
92
|
+
const source = this.getConfigSourceFromFilename(filename);
|
|
93
|
+
configFiles.set(source, filePath);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
currentDir = parentDir;
|
|
98
|
+
parentDir = path.dirname(currentDir);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return configFiles;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Determines the config source type from filename
|
|
107
|
+
*/
|
|
108
|
+
public getConfigSourceFromFilename(filename: string): ConfigSource {
|
|
109
|
+
if (filename.endsWith('.js')) {
|
|
110
|
+
return ConfigSource.JS_FILE;
|
|
111
|
+
} else if (filename.endsWith('.json')) {
|
|
112
|
+
return ConfigSource.JSON_FILE;
|
|
113
|
+
} else if (filename.startsWith('.hypernumrc')) {
|
|
114
|
+
return ConfigSource.RC_FILE;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return ConfigSource.JSON_FILE; // Default fallback
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Loads configuration from a file
|
|
122
|
+
*/
|
|
123
|
+
public loadConfigFromFile(filePath: string): Partial<HypernumConfig> {
|
|
124
|
+
try {
|
|
125
|
+
const extension = path.extname(filePath);
|
|
126
|
+
|
|
127
|
+
switch (extension) {
|
|
128
|
+
case '.js':
|
|
129
|
+
return this.configParser.parseJsConfig(filePath);
|
|
130
|
+
case '.json':
|
|
131
|
+
return this.configParser.parseJsonConfig(filePath);
|
|
132
|
+
default:
|
|
133
|
+
if (filePath.includes('.hypernumrc')) {
|
|
134
|
+
return this.configParser.parseRcConfig(filePath);
|
|
135
|
+
}
|
|
136
|
+
throw new Error(`Unsupported config file extension: ${extension}`);
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error(`Error loading config from ${filePath}:`, error);
|
|
140
|
+
return {};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Loads configuration from all available sources
|
|
146
|
+
*/
|
|
147
|
+
public loadConfig(options: ConfigSearchOptions = {}): HypernumConfig {
|
|
148
|
+
// Return cached config if available
|
|
149
|
+
if (this.cachedConfig) {
|
|
150
|
+
return this.cachedConfig;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const configFiles = this.findConfigFiles(options);
|
|
154
|
+
const configs: Partial<HypernumConfig>[] = [];
|
|
155
|
+
|
|
156
|
+
// Load environment variables first (highest priority)
|
|
157
|
+
const envConfig = this.loadEnvConfig();
|
|
158
|
+
if (Object.keys(envConfig).length > 0) {
|
|
159
|
+
configs.push(envConfig);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Load from config files in order of priority
|
|
163
|
+
for (const source of [ConfigSource.RC_FILE, ConfigSource.JSON_FILE, ConfigSource.JS_FILE]) {
|
|
164
|
+
const filePath = configFiles.get(source);
|
|
165
|
+
if (filePath) {
|
|
166
|
+
const fileConfig = this.loadConfigFromFile(filePath);
|
|
167
|
+
configs.push(fileConfig);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Merge configurations with default config as base
|
|
172
|
+
let finalConfig = {} as HypernumConfig;
|
|
173
|
+
for (const config of configs.reverse()) {
|
|
174
|
+
finalConfig = mergeConfig({ ...finalConfig, ...config });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Cache the configuration
|
|
178
|
+
this.cachedConfig = finalConfig;
|
|
179
|
+
return finalConfig;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Loads environment variables as configuration
|
|
184
|
+
*/
|
|
185
|
+
public loadEnvConfig(): Partial<HypernumConfig> {
|
|
186
|
+
const envConfig: Partial<HypernumConfig> = {};
|
|
187
|
+
// Load environment variables here and populate envConfig
|
|
188
|
+
// Example: envConfig.someKey = process.env.SOME_ENV_VAR;
|
|
189
|
+
return envConfig;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Saves configuration to a file
|
|
195
|
+
*/
|
|
196
|
+
public saveConfig(config: HypernumConfig, filePath: string): void {
|
|
197
|
+
try {
|
|
198
|
+
const extension = path.extname(filePath);
|
|
199
|
+
|
|
200
|
+
switch (extension) {
|
|
201
|
+
case '.js':
|
|
202
|
+
const jsContent = `module.exports = ${this.configParser.stringifyConfig(config)};`;
|
|
203
|
+
fs.writeFileSync(filePath, jsContent, 'utf-8');
|
|
204
|
+
break;
|
|
205
|
+
case '.json':
|
|
206
|
+
fs.writeFileSync(filePath, this.configParser.stringifyConfig(config), 'utf-8');
|
|
207
|
+
break;
|
|
208
|
+
default:
|
|
209
|
+
if (filePath.includes('.hypernumrc')) {
|
|
210
|
+
fs.writeFileSync(filePath, this.configParser.stringifyConfig(config), 'utf-8');
|
|
211
|
+
} else {
|
|
212
|
+
throw new Error(`Unsupported config file extension: ${extension}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.error(`Error saving config to ${filePath}:`, error);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Clears the configuration cache
|
|
222
|
+
*/
|
|
223
|
+
public clearCache(): void {
|
|
224
|
+
this.cachedConfig = null;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* src/config/config-parser.ts
|
|
5
|
+
* Parses configuration files in different formats
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { HypernumConfig } from "@/core";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export class ConfigParser {
|
|
14
|
+
/**
|
|
15
|
+
* Parses a JSON configuration file
|
|
16
|
+
*/
|
|
17
|
+
public parseJsonConfig(filePath: string): Partial<HypernumConfig> {
|
|
18
|
+
try {
|
|
19
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
20
|
+
return JSON.parse(content) as Partial<HypernumConfig>;
|
|
21
|
+
} catch (error) {
|
|
22
|
+
console.error(`Error parsing JSON config file ${filePath}:`, error);
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Parses a JavaScript configuration file
|
|
29
|
+
*/
|
|
30
|
+
public parseJsConfig(filePath: string): Partial<HypernumConfig> {
|
|
31
|
+
try {
|
|
32
|
+
// Use dynamic import for ESM compatibility
|
|
33
|
+
if (path.isAbsolute(filePath)) {
|
|
34
|
+
// For Node.js environments
|
|
35
|
+
const config = require(filePath);
|
|
36
|
+
return config.default || config;
|
|
37
|
+
} else {
|
|
38
|
+
// For browser environments or when relative path is used
|
|
39
|
+
const absolutePath = path.resolve(process.cwd(), filePath);
|
|
40
|
+
const config = require(absolutePath);
|
|
41
|
+
return config.default || config;
|
|
42
|
+
}
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error(`Error parsing JS config file ${filePath}:`, error);
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Parses an RC configuration file
|
|
51
|
+
*/
|
|
52
|
+
public parseRcConfig(filePath: string): Partial<HypernumConfig> {
|
|
53
|
+
try {
|
|
54
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
55
|
+
|
|
56
|
+
// Try to parse as JSON first
|
|
57
|
+
try {
|
|
58
|
+
return JSON.parse(content) as Partial<HypernumConfig>;
|
|
59
|
+
} catch {
|
|
60
|
+
// Not valid JSON, try to parse as JS if it has JS-like syntax
|
|
61
|
+
if (content.includes('module.exports') || content.includes('export default')) {
|
|
62
|
+
// Create a temporary JS file to require
|
|
63
|
+
const tempFilePath = `${filePath}.temp.js`;
|
|
64
|
+
fs.writeFileSync(tempFilePath, content);
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const config = require(tempFilePath);
|
|
68
|
+
fs.unlinkSync(tempFilePath); // Clean up temp file
|
|
69
|
+
return config.default || config;
|
|
70
|
+
} catch (jsError) {
|
|
71
|
+
fs.unlinkSync(tempFilePath); // Clean up temp file even on error
|
|
72
|
+
throw jsError;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Handle INI-style RC files
|
|
77
|
+
return this.parseIniStyleConfig(content);
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error(`Error parsing RC config file ${filePath}:`, error);
|
|
81
|
+
return {};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Parses an INI-style configuration
|
|
87
|
+
*/
|
|
88
|
+
private parseIniStyleConfig(content: string): Partial<HypernumConfig> {
|
|
89
|
+
const config: Record<string, any> = {};
|
|
90
|
+
let currentSection = '';
|
|
91
|
+
|
|
92
|
+
const lines = content.split('\n');
|
|
93
|
+
|
|
94
|
+
for (const line of lines) {
|
|
95
|
+
const trimmedLine = line.trim();
|
|
96
|
+
|
|
97
|
+
// Skip comments and empty lines
|
|
98
|
+
if (trimmedLine.startsWith('#') || trimmedLine.startsWith(';') || !trimmedLine) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Section header
|
|
103
|
+
const sectionMatch = trimmedLine.match(/^\[([^\]]+)\]$/);
|
|
104
|
+
if (sectionMatch) {
|
|
105
|
+
if (sectionMatch[1]) {
|
|
106
|
+
currentSection = sectionMatch[1];
|
|
107
|
+
if (!config[currentSection]) {
|
|
108
|
+
config[currentSection] = {};
|
|
109
|
+
}
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Key-value pair
|
|
115
|
+
const keyValueMatch = trimmedLine.match(/^([^=]+)=(.*)$/);
|
|
116
|
+
if (keyValueMatch) {
|
|
117
|
+
const key = keyValueMatch[1]?.trim() || '';
|
|
118
|
+
let value: any = keyValueMatch[2] ? keyValueMatch[2].trim() : '';
|
|
119
|
+
|
|
120
|
+
// Try to parse the value
|
|
121
|
+
if (value === 'true' || value === 'false') {
|
|
122
|
+
value = value === 'true';
|
|
123
|
+
} else if (!isNaN(Number(value))) {
|
|
124
|
+
value = Number(value);
|
|
125
|
+
} else if (value.startsWith('"') && value.endsWith('"')) {
|
|
126
|
+
value = value.slice(1, -1);
|
|
127
|
+
} else if (value.startsWith("'") && value.endsWith("'")) {
|
|
128
|
+
value = value.slice(1, -1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (currentSection) {
|
|
132
|
+
config[currentSection][key] = value;
|
|
133
|
+
} else {
|
|
134
|
+
config[key] = value;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return config as Partial<HypernumConfig>;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Stringifies a configuration object for saving
|
|
144
|
+
*/
|
|
145
|
+
public stringifyConfig(config: HypernumConfig): string {
|
|
146
|
+
// Use a custom replacer function to handle BigInt and circular references
|
|
147
|
+
const seen = new WeakSet();
|
|
148
|
+
return JSON.stringify(config, (_, value) => {
|
|
149
|
+
if (typeof value === 'bigint') {
|
|
150
|
+
return value.toString() + 'n';
|
|
151
|
+
}
|
|
152
|
+
if (typeof value === 'object' && value !== null) {
|
|
153
|
+
if (seen.has(value)) {
|
|
154
|
+
return '[Circular]';
|
|
155
|
+
}
|
|
156
|
+
seen.add(value);
|
|
157
|
+
}
|
|
158
|
+
return value;
|
|
159
|
+
}, 2);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/config/config-resolver.ts
|
|
3
|
+
* Resolves configuration from multiple sources
|
|
4
|
+
*/
|
|
5
|
+
import { HypernumConfig, mergeConfig } from '../core/config';
|
|
6
|
+
import { ConfigSource } from './config-source';
|
|
7
|
+
|
|
8
|
+
export class ConfigResolver {
|
|
9
|
+
/**
|
|
10
|
+
* Default precedence order for configuration sources
|
|
11
|
+
*/
|
|
12
|
+
private readonly DEFAULT_PRECEDENCE: ConfigSource[] = [
|
|
13
|
+
ConfigSource.INLINE_CONFIG, // Highest priority
|
|
14
|
+
ConfigSource.ENV_VARIABLES,
|
|
15
|
+
ConfigSource.RC_FILE,
|
|
16
|
+
ConfigSource.JSON_FILE,
|
|
17
|
+
ConfigSource.JS_FILE // Lowest priority
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Resolves configuration from multiple sources
|
|
22
|
+
*/
|
|
23
|
+
public resolveConfig(configs: Map<ConfigSource, Partial<HypernumConfig>>): HypernumConfig {
|
|
24
|
+
// Sort configurations by precedence
|
|
25
|
+
const sortedEntries = Array.from(configs.entries())
|
|
26
|
+
.sort((a, b) => {
|
|
27
|
+
const indexA = this.DEFAULT_PRECEDENCE.indexOf(a[0]);
|
|
28
|
+
const indexB = this.DEFAULT_PRECEDENCE.indexOf(b[0]);
|
|
29
|
+
return indexA - indexB;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Merge configurations in order (low to high priority)
|
|
33
|
+
let finalConfig = {} as HypernumConfig;
|
|
34
|
+
for (const [_, config] of sortedEntries) {
|
|
35
|
+
finalConfig = mergeConfig({ ...finalConfig, ...config });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return finalConfig;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Validates a configuration source
|
|
43
|
+
*/
|
|
44
|
+
public validateConfigSource(config: Partial<HypernumConfig>): boolean {
|
|
45
|
+
// Basic validation for now - can be extended with schema validation
|
|
46
|
+
if (!config || typeof config !== 'object') {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/config/config-source.ts
|
|
3
|
+
* Enum defining possible configuration sources
|
|
4
|
+
*/
|
|
5
|
+
export enum ConfigSource {
|
|
6
|
+
/**
|
|
7
|
+
* Environment variables with HYPERNUM_ prefix
|
|
8
|
+
*/
|
|
9
|
+
ENV_VARIABLES = 'ENV_VARIABLES',
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* RC file (.hypernumrc, .hypernumrc.json, etc.)
|
|
13
|
+
*/
|
|
14
|
+
RC_FILE = 'RC_FILE',
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* JSON configuration file (hypernum.config.json)
|
|
18
|
+
*/
|
|
19
|
+
JSON_FILE = 'JSON_FILE',
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* JavaScript configuration file (hypernum.config.js)
|
|
23
|
+
*/
|
|
24
|
+
JS_FILE = 'JS_FILE',
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Inline configuration passed to createHypernum()
|
|
28
|
+
*/
|
|
29
|
+
INLINE_CONFIG = 'INLINE_CONFIG'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/config/index.ts
|
|
3
|
+
* Main configuration module
|
|
4
|
+
*/
|
|
5
|
+
import { HypernumConfig,DEFAULT_BASIC_CONFIG, DEFAULT_FULL_CONFIG } from '../core/config';
|
|
6
|
+
import { ConfigLoader } from './config-loader';
|
|
7
|
+
import { ConfigParser } from './config-parser';
|
|
8
|
+
import { ConfigResolver } from './config-resolver';
|
|
9
|
+
import { ConfigSource } from './config-source';
|
|
10
|
+
|
|
11
|
+
interface ConfigurationOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Path to a configuration file to use
|
|
14
|
+
*/
|
|
15
|
+
configFile?: string;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Whether to search for configuration files in parent directories
|
|
19
|
+
*/
|
|
20
|
+
searchParentDirs?: boolean;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Additional paths to search for configuration files
|
|
24
|
+
*/
|
|
25
|
+
additionalSearchPaths?: string[];
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Whether to use environment variables
|
|
29
|
+
*/
|
|
30
|
+
useEnvVars?: boolean;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Whether to cache the configuration
|
|
34
|
+
*/
|
|
35
|
+
cacheConfig?: boolean;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Inline configuration to use as a base
|
|
39
|
+
*/
|
|
40
|
+
baseConfig?: Partial<HypernumConfig>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Main configuration module for Hypernum
|
|
45
|
+
*/
|
|
46
|
+
export class ConfigurationModule {
|
|
47
|
+
public configLoader: ConfigLoader;
|
|
48
|
+
public configParser: ConfigParser;
|
|
49
|
+
public configResolver: ConfigResolver;
|
|
50
|
+
public cachedConfig: HypernumConfig | null = null;
|
|
51
|
+
|
|
52
|
+
constructor() {
|
|
53
|
+
this.configLoader = new ConfigLoader();
|
|
54
|
+
this.configParser = new ConfigParser();
|
|
55
|
+
this.configResolver = new ConfigResolver();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Gets the configuration, loading from all available sources
|
|
60
|
+
*/
|
|
61
|
+
public getConfig(options: ConfigurationOptions = {}): HypernumConfig {
|
|
62
|
+
const {
|
|
63
|
+
configFile,
|
|
64
|
+
searchParentDirs = true,
|
|
65
|
+
additionalSearchPaths = [],
|
|
66
|
+
useEnvVars = true,
|
|
67
|
+
cacheConfig = true,
|
|
68
|
+
baseConfig = {}
|
|
69
|
+
} = options;
|
|
70
|
+
|
|
71
|
+
// Return cached config if available and caching is enabled
|
|
72
|
+
if (this.cachedConfig && cacheConfig) {
|
|
73
|
+
return this.cachedConfig;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const configs = new Map<ConfigSource, Partial<HypernumConfig>>();
|
|
77
|
+
|
|
78
|
+
// Add base config (lowest priority)
|
|
79
|
+
if (Object.keys(baseConfig).length > 0) {
|
|
80
|
+
configs.set(ConfigSource.INLINE_CONFIG, baseConfig);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Load from specific file if provided
|
|
84
|
+
if (configFile) {
|
|
85
|
+
const fileConfig = this.configLoader.loadConfigFromFile(configFile);
|
|
86
|
+
const source = this.configLoader.getConfigSourceFromFilename(configFile);
|
|
87
|
+
if (this.configResolver.validateConfigSource(fileConfig)) {
|
|
88
|
+
configs.set(source, fileConfig);
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
// Search for configuration files
|
|
92
|
+
const configFiles = this.configLoader.findConfigFiles({
|
|
93
|
+
searchParentDirs,
|
|
94
|
+
additionalPaths: additionalSearchPaths
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
for (const [source, filePath] of configFiles.entries()) {
|
|
98
|
+
const fileConfig = this.configLoader.loadConfigFromFile(filePath);
|
|
99
|
+
if (this.configResolver.validateConfigSource(fileConfig)) {
|
|
100
|
+
configs.set(source, fileConfig);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Load environment variables if enabled
|
|
106
|
+
if (useEnvVars) {
|
|
107
|
+
const envConfig = this.configLoader.loadEnvConfig();
|
|
108
|
+
if (Object.keys(envConfig).length > 0) {
|
|
109
|
+
configs.set(ConfigSource.ENV_VARIABLES, envConfig);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Resolve the final configuration
|
|
114
|
+
const finalConfig = this.configResolver.resolveConfig(configs);
|
|
115
|
+
|
|
116
|
+
// Cache the configuration if caching is enabled
|
|
117
|
+
if (cacheConfig) {
|
|
118
|
+
this.cachedConfig = finalConfig;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return finalConfig;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Initializes configuration from a file
|
|
126
|
+
*/
|
|
127
|
+
public initializeFromFile(filePath: string, options: ConfigurationOptions = {}): HypernumConfig {
|
|
128
|
+
return this.getConfig({
|
|
129
|
+
...options,
|
|
130
|
+
configFile: filePath
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Creates a default configuration
|
|
136
|
+
*/
|
|
137
|
+
public createDefaultConfig(type: 'basic' | 'full'): HypernumConfig {
|
|
138
|
+
return type === 'basic' ? DEFAULT_BASIC_CONFIG : DEFAULT_FULL_CONFIG;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Saves configuration to a file
|
|
143
|
+
*/
|
|
144
|
+
public saveConfigToFile(config: HypernumConfig, filePath: string): void {
|
|
145
|
+
this.configLoader.saveConfig(config, filePath);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
public getConfigSourceFromFilename(filename: string): ConfigSource {
|
|
149
|
+
return this.configLoader.getConfigSourceFromFilename(filename);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Clears the configuration cache
|
|
154
|
+
*/
|
|
155
|
+
public clearCache(): void {
|
|
156
|
+
this.cachedConfig = null;
|
|
157
|
+
this.configLoader.clearCache();
|
|
158
|
+
}
|
|
159
|
+
}
|