@alint-js/config 0.0.4
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/index.d.mts +26 -0
- package/dist/index.mjs +282 -0
- package/package.json +33 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AlintConfig, AlintConfig as AlintConfig$1, ModelSize, ProviderDefinition, ProviderType, RunnerConfig, SetupConfig, SetupConfig as SetupConfig$1, SetupModelDefinition } from "@alint-js/core";
|
|
2
|
+
|
|
3
|
+
//#region src/load-config.d.ts
|
|
4
|
+
declare function loadAlintConfig(cwd: string, configFile?: string): Promise<AlintConfig$1>;
|
|
5
|
+
//#endregion
|
|
6
|
+
//#region src/paths.d.ts
|
|
7
|
+
interface GlobalSetupConfigPathOptions {
|
|
8
|
+
isMacOS?: boolean;
|
|
9
|
+
xdgConfig?: string;
|
|
10
|
+
}
|
|
11
|
+
declare function getGlobalSetupConfigPath(env?: NodeJS.ProcessEnv, options?: GlobalSetupConfigPathOptions): string;
|
|
12
|
+
declare function getProjectSetupConfigPath(cwd: string): string;
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/setup-load.d.ts
|
|
15
|
+
declare const emptySetupConfig: SetupConfig$1;
|
|
16
|
+
declare function loadSetupConfig(filePath: string): Promise<SetupConfig$1>;
|
|
17
|
+
declare function mergeSetupConfigs(...configs: SetupConfig$1[]): SetupConfig$1;
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/setup-toml.d.ts
|
|
20
|
+
declare function parseSetupConfigToml(toml: string): SetupConfig$1;
|
|
21
|
+
declare function stringifySetupConfigToml(config: SetupConfig$1): string;
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/setup-write.d.ts
|
|
24
|
+
declare function writeSetupConfig(filePath: string, config: SetupConfig$1): Promise<void>;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { type AlintConfig, type GlobalSetupConfigPathOptions, type ModelSize, type ProviderDefinition, type ProviderType, type RunnerConfig, type SetupConfig, type SetupModelDefinition, emptySetupConfig, getGlobalSetupConfigPath, getProjectSetupConfigPath, loadAlintConfig, loadSetupConfig, mergeSetupConfigs, parseSetupConfigToml, stringifySetupConfigToml, writeSetupConfig };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { loadConfig } from "c12";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join } from "pathe";
|
|
5
|
+
import { xdgConfig } from "xdg-basedir";
|
|
6
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
7
|
+
import { parse, stringify } from "smol-toml";
|
|
8
|
+
//#region src/load-config.ts
|
|
9
|
+
async function loadAlintConfig(cwd, configFile) {
|
|
10
|
+
return (await loadConfig({
|
|
11
|
+
configFile,
|
|
12
|
+
cwd,
|
|
13
|
+
defaults: {
|
|
14
|
+
plugins: [],
|
|
15
|
+
rules: {}
|
|
16
|
+
},
|
|
17
|
+
dotenv: true,
|
|
18
|
+
name: "alint"
|
|
19
|
+
})).config;
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/paths.ts
|
|
23
|
+
function getGlobalSetupConfigPath(env = process.env, options = {}) {
|
|
24
|
+
return join(env.XDG_CONFIG_HOME ?? resolveDefaultConfigHome(options), "alint", "config.toml");
|
|
25
|
+
}
|
|
26
|
+
function getProjectSetupConfigPath(cwd) {
|
|
27
|
+
return join(cwd, ".alint", "config.toml");
|
|
28
|
+
}
|
|
29
|
+
function resolveDefaultConfigHome(options) {
|
|
30
|
+
if (options.isMacOS ?? process.platform === "darwin") return join(homedir(), ".config");
|
|
31
|
+
return options.xdgConfig ?? xdgConfig ?? join(homedir(), ".config");
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/setup-toml.ts
|
|
35
|
+
const modelSizes = /* @__PURE__ */ new Set([
|
|
36
|
+
"large",
|
|
37
|
+
"medium",
|
|
38
|
+
"small"
|
|
39
|
+
]);
|
|
40
|
+
function parseSetupConfigToml(toml) {
|
|
41
|
+
const rawConfig = parse(toml);
|
|
42
|
+
if (rawConfig.version !== 1) throw new Error("Invalid setup config: version must be 1.");
|
|
43
|
+
if (!Array.isArray(rawConfig.providers)) throw new TypeError("Invalid setup config: providers must be an array.");
|
|
44
|
+
const parsedConfig = {
|
|
45
|
+
providers: rawConfig.providers.map((provider) => parseProvider(provider)),
|
|
46
|
+
version: 1
|
|
47
|
+
};
|
|
48
|
+
if (rawConfig.runner !== void 0) parsedConfig.runner = parseRunner(rawConfig.runner);
|
|
49
|
+
return parsedConfig;
|
|
50
|
+
}
|
|
51
|
+
function stringifySetupConfigToml(config) {
|
|
52
|
+
const stringifiableConfig = {
|
|
53
|
+
providers: config.providers.map(toTomlProvider),
|
|
54
|
+
version: config.version
|
|
55
|
+
};
|
|
56
|
+
if (config.runner !== void 0) stringifiableConfig.runner = toTomlRunner(config.runner);
|
|
57
|
+
return stringify(stringifiableConfig);
|
|
58
|
+
}
|
|
59
|
+
function isModelSize(value) {
|
|
60
|
+
return typeof value === "string" && modelSizes.has(value);
|
|
61
|
+
}
|
|
62
|
+
function isPlainObject(value) {
|
|
63
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
64
|
+
}
|
|
65
|
+
function parseModel(providerId, model) {
|
|
66
|
+
const id = readNonEmptyString(model.id, `provider "${providerId}" model id`);
|
|
67
|
+
const parsedModel = { id };
|
|
68
|
+
if (model.name !== void 0) parsedModel.name = readString(model.name, `model "${id}" name`);
|
|
69
|
+
if (model.aliases !== void 0) parsedModel.aliases = readStringArray(model.aliases, `model "${id}" aliases`);
|
|
70
|
+
if (model.capabilities !== void 0) parsedModel.capabilities = readStringArray(model.capabilities, `model "${id}" capabilities`);
|
|
71
|
+
if (model.size !== void 0) {
|
|
72
|
+
if (!isModelSize(model.size)) throw new Error(`Invalid model "${id}": size must be "small", "medium", or "large".`);
|
|
73
|
+
parsedModel.size = model.size;
|
|
74
|
+
}
|
|
75
|
+
if (model.context_window !== void 0) parsedModel.contextWindow = readFiniteNumber(model.context_window, `model "${id}" context_window`);
|
|
76
|
+
if (model.default_params !== void 0) parsedModel.defaultParams = readRecord(model.default_params, `model "${id}" default_params`);
|
|
77
|
+
return parsedModel;
|
|
78
|
+
}
|
|
79
|
+
function parsePositiveInteger(value, label) {
|
|
80
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) throw new TypeError(`Invalid ${label}: must be a positive integer.`);
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
function parseProvider(provider) {
|
|
84
|
+
const id = readNonEmptyString(provider.id, "provider id");
|
|
85
|
+
if (provider.type !== "openai-compatible") throw new Error(`Invalid provider "${id}": type must be "openai-compatible".`);
|
|
86
|
+
const endpoint = readNonEmptyString(provider.endpoint, `provider "${id}" endpoint`);
|
|
87
|
+
if (!Array.isArray(provider.models)) throw new TypeError(`Invalid provider "${id}": models must be an array.`);
|
|
88
|
+
const parsedProvider = {
|
|
89
|
+
endpoint,
|
|
90
|
+
id,
|
|
91
|
+
models: provider.models.map((model) => parseModel(id, model)),
|
|
92
|
+
type: provider.type
|
|
93
|
+
};
|
|
94
|
+
if (provider.headers !== void 0) parsedProvider.headers = readStringMap(provider.headers, `provider "${id}" headers`);
|
|
95
|
+
return parsedProvider;
|
|
96
|
+
}
|
|
97
|
+
function parseRunner(runner) {
|
|
98
|
+
const parsedRunner = {};
|
|
99
|
+
if (runner.cache !== void 0) parsedRunner.cache = parseRunnerCache(runner.cache);
|
|
100
|
+
if (runner.file_concurrency !== void 0) parsedRunner.fileConcurrency = parsePositiveInteger(runner.file_concurrency, "runner file_concurrency");
|
|
101
|
+
if (runner.rule_concurrency !== void 0) parsedRunner.ruleConcurrency = parsePositiveInteger(runner.rule_concurrency, "runner rule_concurrency");
|
|
102
|
+
if (runner.timeout_ms !== void 0) parsedRunner.timeoutMs = parsePositiveInteger(runner.timeout_ms, "runner timeout_ms");
|
|
103
|
+
return parsedRunner;
|
|
104
|
+
}
|
|
105
|
+
function parseRunnerCache(cache) {
|
|
106
|
+
if (typeof cache === "boolean") return cache;
|
|
107
|
+
if (!isPlainObject(cache)) throw new TypeError("Invalid runner cache: must be a boolean or table.");
|
|
108
|
+
const tomlCache = cache;
|
|
109
|
+
const parsedCache = {};
|
|
110
|
+
if (tomlCache.enabled !== void 0) {
|
|
111
|
+
if (typeof tomlCache.enabled !== "boolean") throw new TypeError("Invalid runner cache enabled: must be a boolean.");
|
|
112
|
+
parsedCache.enabled = tomlCache.enabled;
|
|
113
|
+
}
|
|
114
|
+
if (tomlCache.location !== void 0) parsedCache.location = readNonEmptyString(tomlCache.location, "runner cache location");
|
|
115
|
+
return parsedCache;
|
|
116
|
+
}
|
|
117
|
+
function readFiniteNumber(value, label) {
|
|
118
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new TypeError(`Invalid ${label}: must be a finite number.`);
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
121
|
+
function readNonEmptyString(value, label) {
|
|
122
|
+
const stringValue = readString(value, label);
|
|
123
|
+
if (stringValue.length === 0) throw new Error(`Invalid ${label}: must be a non-empty string.`);
|
|
124
|
+
return stringValue;
|
|
125
|
+
}
|
|
126
|
+
function readRecord(value, label) {
|
|
127
|
+
if (!isPlainObject(value)) throw new Error(`Invalid ${label}: must be a table.`);
|
|
128
|
+
return value;
|
|
129
|
+
}
|
|
130
|
+
function readString(value, label) {
|
|
131
|
+
if (typeof value !== "string") throw new TypeError(`Invalid ${label}: must be a string.`);
|
|
132
|
+
return value;
|
|
133
|
+
}
|
|
134
|
+
function readStringArray(value, label) {
|
|
135
|
+
if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) throw new Error(`Invalid ${label}: must be an array of strings.`);
|
|
136
|
+
return value;
|
|
137
|
+
}
|
|
138
|
+
function readStringMap(value, label) {
|
|
139
|
+
const record = readRecord(value, label);
|
|
140
|
+
if (!Object.values(record).every((item) => typeof item === "string")) throw new Error(`Invalid ${label}: must be a string map.`);
|
|
141
|
+
return record;
|
|
142
|
+
}
|
|
143
|
+
function toTomlModel(model) {
|
|
144
|
+
const tomlModel = { id: model.id };
|
|
145
|
+
if (model.name !== void 0) tomlModel.name = model.name;
|
|
146
|
+
if (model.aliases !== void 0) tomlModel.aliases = model.aliases;
|
|
147
|
+
if (model.capabilities !== void 0) tomlModel.capabilities = model.capabilities;
|
|
148
|
+
if (model.size !== void 0) tomlModel.size = model.size;
|
|
149
|
+
if (model.contextWindow !== void 0) tomlModel.context_window = model.contextWindow;
|
|
150
|
+
if (model.defaultParams !== void 0) tomlModel.default_params = model.defaultParams;
|
|
151
|
+
return tomlModel;
|
|
152
|
+
}
|
|
153
|
+
function toTomlProvider(provider) {
|
|
154
|
+
const tomlProvider = {
|
|
155
|
+
endpoint: provider.endpoint,
|
|
156
|
+
id: provider.id,
|
|
157
|
+
models: provider.models.map(toTomlModel),
|
|
158
|
+
type: provider.type
|
|
159
|
+
};
|
|
160
|
+
if (provider.headers !== void 0) tomlProvider.headers = provider.headers;
|
|
161
|
+
return tomlProvider;
|
|
162
|
+
}
|
|
163
|
+
function toTomlRunner(runner) {
|
|
164
|
+
return {
|
|
165
|
+
cache: toTomlRunnerCache(runner.cache),
|
|
166
|
+
file_concurrency: runner.fileConcurrency,
|
|
167
|
+
rule_concurrency: runner.ruleConcurrency,
|
|
168
|
+
timeout_ms: runner.timeoutMs
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function toTomlRunnerCache(cache) {
|
|
172
|
+
return cache;
|
|
173
|
+
}
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region src/setup-load.ts
|
|
176
|
+
const emptySetupConfig = {
|
|
177
|
+
providers: [],
|
|
178
|
+
version: 1
|
|
179
|
+
};
|
|
180
|
+
async function loadSetupConfig(filePath) {
|
|
181
|
+
try {
|
|
182
|
+
return parseSetupConfigToml(await readFile(filePath, "utf8"));
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (isNodeError(error) && error.code === "ENOENT") return createEmptySetupConfig();
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function mergeSetupConfigs(...configs) {
|
|
189
|
+
let providers = [];
|
|
190
|
+
let runner;
|
|
191
|
+
const providersById = /* @__PURE__ */ new Map();
|
|
192
|
+
for (const config of configs) {
|
|
193
|
+
const configProviders = [];
|
|
194
|
+
if (config.runner !== void 0) runner = {
|
|
195
|
+
...runner ?? {},
|
|
196
|
+
...config.runner
|
|
197
|
+
};
|
|
198
|
+
for (const provider of config.providers) {
|
|
199
|
+
const existingProvider = providersById.get(provider.id);
|
|
200
|
+
if (existingProvider === void 0) {
|
|
201
|
+
const mergedProvider = cloneProvider(provider);
|
|
202
|
+
providers.push(mergedProvider);
|
|
203
|
+
providersById.set(provider.id, mergedProvider);
|
|
204
|
+
configProviders.push(mergedProvider);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
existingProvider.endpoint = provider.endpoint;
|
|
208
|
+
existingProvider.type = provider.type;
|
|
209
|
+
configProviders.push(existingProvider);
|
|
210
|
+
if (provider.headers !== void 0) existingProvider.headers = {
|
|
211
|
+
...existingProvider.headers,
|
|
212
|
+
...provider.headers
|
|
213
|
+
};
|
|
214
|
+
const nextModels = [];
|
|
215
|
+
for (const incomingModel of provider.models) {
|
|
216
|
+
const existingModelIndex = existingProvider.models.findIndex((model) => model.id === incomingModel.id);
|
|
217
|
+
if (existingModelIndex === -1) {
|
|
218
|
+
nextModels.push(cloneModel(incomingModel));
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
const existingModel = existingProvider.models[existingModelIndex];
|
|
222
|
+
existingProvider.models.splice(existingModelIndex, 1);
|
|
223
|
+
nextModels.push(mergeModel(existingModel, incomingModel));
|
|
224
|
+
}
|
|
225
|
+
existingProvider.models = [...nextModels, ...existingProvider.models];
|
|
226
|
+
}
|
|
227
|
+
providers = prioritizeProviders(providers, configProviders);
|
|
228
|
+
}
|
|
229
|
+
const mergedConfig = {
|
|
230
|
+
providers,
|
|
231
|
+
version: 1
|
|
232
|
+
};
|
|
233
|
+
if (runner !== void 0) mergedConfig.runner = runner;
|
|
234
|
+
return mergedConfig;
|
|
235
|
+
}
|
|
236
|
+
function cloneModel(model) {
|
|
237
|
+
const clonedModel = { ...model };
|
|
238
|
+
if (model.aliases !== void 0) clonedModel.aliases = [...model.aliases];
|
|
239
|
+
if (model.capabilities !== void 0) clonedModel.capabilities = [...model.capabilities];
|
|
240
|
+
if (model.defaultParams !== void 0) clonedModel.defaultParams = { ...model.defaultParams };
|
|
241
|
+
return clonedModel;
|
|
242
|
+
}
|
|
243
|
+
function cloneProvider(provider) {
|
|
244
|
+
const clonedProvider = {
|
|
245
|
+
...provider,
|
|
246
|
+
models: provider.models.map(cloneModel)
|
|
247
|
+
};
|
|
248
|
+
if (provider.headers !== void 0) clonedProvider.headers = { ...provider.headers };
|
|
249
|
+
return clonedProvider;
|
|
250
|
+
}
|
|
251
|
+
function createEmptySetupConfig() {
|
|
252
|
+
return {
|
|
253
|
+
providers: [],
|
|
254
|
+
version: 1
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function isNodeError(error) {
|
|
258
|
+
return error instanceof Error && "code" in error;
|
|
259
|
+
}
|
|
260
|
+
function mergeModel(existingModel, incomingModel) {
|
|
261
|
+
const existing = cloneModel(existingModel);
|
|
262
|
+
const incoming = cloneModel(incomingModel);
|
|
263
|
+
return {
|
|
264
|
+
...existing,
|
|
265
|
+
...incoming,
|
|
266
|
+
aliases: incoming.aliases ?? existing.aliases,
|
|
267
|
+
capabilities: incoming.capabilities ?? existing.capabilities,
|
|
268
|
+
defaultParams: incoming.defaultParams ?? existing.defaultParams
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
function prioritizeProviders(providers, prioritizedProviders) {
|
|
272
|
+
const prioritizedProviderIds = new Set(prioritizedProviders.map((provider) => provider.id));
|
|
273
|
+
return [...prioritizedProviders, ...providers.filter((provider) => !prioritizedProviderIds.has(provider.id))];
|
|
274
|
+
}
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/setup-write.ts
|
|
277
|
+
async function writeSetupConfig(filePath, config) {
|
|
278
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
279
|
+
await writeFile(filePath, stringifySetupConfigToml(config), "utf8");
|
|
280
|
+
}
|
|
281
|
+
//#endregion
|
|
282
|
+
export { emptySetupConfig, getGlobalSetupConfigPath, getProjectSetupConfigPath, loadAlintConfig, loadSetupConfig, mergeSetupConfigs, parseSetupConfigToml, stringifySetupConfigToml, writeSetupConfig };
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@alint-js/config",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.4",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/index.d.mts",
|
|
8
|
+
"default": "./dist/index.mjs"
|
|
9
|
+
},
|
|
10
|
+
"./package.json": "./package.json"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"c12": "^3.3.4",
|
|
17
|
+
"pathe": "^2.0.3",
|
|
18
|
+
"smol-toml": "^1.7.0",
|
|
19
|
+
"xdg-basedir": "^5.1.0",
|
|
20
|
+
"@alint-js/core": "0.0.4"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^26.0.1",
|
|
24
|
+
"tsdown": "^0.22.3",
|
|
25
|
+
"typescript": "^6.0.3",
|
|
26
|
+
"vitest": "^4.1.9"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsdown",
|
|
30
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
31
|
+
"test": "vitest run --config vitest.config.ts"
|
|
32
|
+
}
|
|
33
|
+
}
|