@alint-js/config 0.0.21 → 0.0.22
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 +93 -1
- package/dist/index.mjs +446 -14
- package/package.json +7 -7
package/dist/index.d.mts
CHANGED
|
@@ -69,6 +69,98 @@ declare function getProjectPluginStorePath(cwd: string): string;
|
|
|
69
69
|
declare function getProjectSetupConfigPath(cwd: string): string;
|
|
70
70
|
declare function getStatsDir(env?: NodeJS.ProcessEnv, options?: GlobalSetupConfigPathOptions): string;
|
|
71
71
|
//#endregion
|
|
72
|
+
//#region src/plugins/declarative/types.d.ts
|
|
73
|
+
declare const builtInAgentNames: readonly ["basic-structured", "basic-coding-agent"];
|
|
74
|
+
declare const declarativeRuleFilePattern = "**/rule.alint.{toml,yaml,yml,json,jsonc,json5}";
|
|
75
|
+
type BuiltInAgentName = typeof builtInAgentNames[number];
|
|
76
|
+
interface DeclarativeFinding {
|
|
77
|
+
confidence?: 'high' | 'low' | 'medium';
|
|
78
|
+
filePath?: string;
|
|
79
|
+
line: number;
|
|
80
|
+
message: string;
|
|
81
|
+
suggestion?: string;
|
|
82
|
+
}
|
|
83
|
+
interface DeclarativeFindingResponse {
|
|
84
|
+
findings: DeclarativeFinding[];
|
|
85
|
+
}
|
|
86
|
+
declare const declarativeFindingSchema: import("valibot").SchemaWithPipe<readonly [import("valibot").ObjectSchema<{
|
|
87
|
+
readonly confidence: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").PicklistSchema<["high", "medium", "low"], undefined>, import("valibot").DescriptionAction<"high" | "medium" | "low", "Confidence in this finding. Use exactly \"high\", \"medium\", or \"low\" when confidence is known.">]>, undefined>;
|
|
88
|
+
readonly filePath: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").StringSchema<undefined>, import("valibot").DescriptionAction<string, "Path to the file that owns this finding. Omit when the finding belongs to the reviewed target file.">]>, undefined>;
|
|
89
|
+
readonly line: import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<undefined>, import("valibot").DescriptionAction<number, "Use the left-column line number from the numbered source block.">]>;
|
|
90
|
+
readonly message: import("valibot").SchemaWithPipe<readonly [import("valibot").StringSchema<undefined>, import("valibot").DescriptionAction<string, "Human-readable diagnostic message describing the issue.">]>;
|
|
91
|
+
readonly suggestion: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").StringSchema<undefined>, import("valibot").DescriptionAction<string, "Concrete remediation direction for the finding.">]>, undefined>;
|
|
92
|
+
}, undefined>, import("valibot").DescriptionAction<{
|
|
93
|
+
confidence?: "high" | "medium" | "low" | undefined;
|
|
94
|
+
filePath?: string | undefined;
|
|
95
|
+
line: number;
|
|
96
|
+
message: string;
|
|
97
|
+
suggestion?: string | undefined;
|
|
98
|
+
}, "One declarative rule finding.">]>;
|
|
99
|
+
declare const declarativeFindingResponseSchema: import("valibot").SchemaWithPipe<readonly [import("valibot").ObjectSchema<{
|
|
100
|
+
readonly findings: import("valibot").SchemaWithPipe<readonly [import("valibot").ArraySchema<import("valibot").SchemaWithPipe<readonly [import("valibot").ObjectSchema<{
|
|
101
|
+
readonly confidence: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").PicklistSchema<["high", "medium", "low"], undefined>, import("valibot").DescriptionAction<"high" | "medium" | "low", "Confidence in this finding. Use exactly \"high\", \"medium\", or \"low\" when confidence is known.">]>, undefined>;
|
|
102
|
+
readonly filePath: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").StringSchema<undefined>, import("valibot").DescriptionAction<string, "Path to the file that owns this finding. Omit when the finding belongs to the reviewed target file.">]>, undefined>;
|
|
103
|
+
readonly line: import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<undefined>, import("valibot").DescriptionAction<number, "Use the left-column line number from the numbered source block.">]>;
|
|
104
|
+
readonly message: import("valibot").SchemaWithPipe<readonly [import("valibot").StringSchema<undefined>, import("valibot").DescriptionAction<string, "Human-readable diagnostic message describing the issue.">]>;
|
|
105
|
+
readonly suggestion: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").StringSchema<undefined>, import("valibot").DescriptionAction<string, "Concrete remediation direction for the finding.">]>, undefined>;
|
|
106
|
+
}, undefined>, import("valibot").DescriptionAction<{
|
|
107
|
+
confidence?: "high" | "medium" | "low" | undefined;
|
|
108
|
+
filePath?: string | undefined;
|
|
109
|
+
line: number;
|
|
110
|
+
message: string;
|
|
111
|
+
suggestion?: string | undefined;
|
|
112
|
+
}, "One declarative rule finding.">]>, undefined>, import("valibot").DescriptionAction<{
|
|
113
|
+
confidence?: "high" | "medium" | "low" | undefined;
|
|
114
|
+
filePath?: string | undefined;
|
|
115
|
+
line: number;
|
|
116
|
+
message: string;
|
|
117
|
+
suggestion?: string | undefined;
|
|
118
|
+
}[], "All findings for the declarative rule. Return an empty array when there are no issues.">]>;
|
|
119
|
+
}, undefined>, import("valibot").DescriptionAction<{
|
|
120
|
+
findings: {
|
|
121
|
+
confidence?: "high" | "medium" | "low" | undefined;
|
|
122
|
+
filePath?: string | undefined;
|
|
123
|
+
line: number;
|
|
124
|
+
message: string;
|
|
125
|
+
suggestion?: string | undefined;
|
|
126
|
+
}[];
|
|
127
|
+
}, "Structured declarative rule findings.">]>;
|
|
128
|
+
interface DeclarativeRuleDefinition {
|
|
129
|
+
builtInAgent: BuiltInAgentName;
|
|
130
|
+
excludeFiles: string[];
|
|
131
|
+
filePath: string;
|
|
132
|
+
includeFiles?: string[];
|
|
133
|
+
instruction: string;
|
|
134
|
+
name: string;
|
|
135
|
+
}
|
|
136
|
+
declare function isBuiltInAgentName(value: unknown): value is BuiltInAgentName;
|
|
137
|
+
//#endregion
|
|
138
|
+
//#region src/plugins/declarative/parse.d.ts
|
|
139
|
+
interface LoadDeclarativeRulesOptions {
|
|
140
|
+
alias: string;
|
|
141
|
+
root: string;
|
|
142
|
+
}
|
|
143
|
+
declare function hasDeclarativeRuleFiles(root: string): Promise<boolean>;
|
|
144
|
+
declare function loadDeclarativeRules(options: LoadDeclarativeRulesOptions): Promise<DeclarativeRuleDefinition[]>;
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region src/plugins/declarative/plugin.d.ts
|
|
147
|
+
interface CreateDeclarativePluginOptions {
|
|
148
|
+
rules: readonly DeclarativeRuleDefinition[];
|
|
149
|
+
}
|
|
150
|
+
declare function createDeclarativePlugin(options: CreateDeclarativePluginOptions): PluginDefinition;
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/plugins/declarative/scope.d.ts
|
|
153
|
+
interface CreateReportScopeOptions {
|
|
154
|
+
cwd: string;
|
|
155
|
+
excludeFiles: readonly string[];
|
|
156
|
+
includeFiles?: readonly string[];
|
|
157
|
+
targetFilePath?: string;
|
|
158
|
+
}
|
|
159
|
+
interface ReportScope {
|
|
160
|
+
canReport: (filePath: string | undefined) => boolean;
|
|
161
|
+
}
|
|
162
|
+
declare function createReportScope(options: CreateReportScopeOptions): ReportScope;
|
|
163
|
+
//#endregion
|
|
72
164
|
//#region src/plugins/types.d.ts
|
|
73
165
|
interface DirectoryPluginLockEntry {
|
|
74
166
|
alias: string;
|
|
@@ -153,4 +245,4 @@ declare function stringifySetupConfigToml(config: SetupConfig$1): string;
|
|
|
153
245
|
//#region src/setup/write.d.ts
|
|
154
246
|
declare function writeSetupConfig(filePath: string, config: SetupConfig$1): Promise<void>;
|
|
155
247
|
//#endregion
|
|
156
|
-
export { type AlintConfig, type DirectoryPluginLockEntry, type GlobalSetupConfigPathOptions, type ModelSize, type ParseStaticConfigOptions, type ParsedDirectoryPluginLockEntry, type ParsedPluginLockEntry, type ParsedPluginLockFile, type ParsedPluginSpecifier, type ParsedRegistryPluginLockEntry, type ParsedStaticConfig, type ParsedStaticConfigGroup, type PluginLockEntry, type PluginLockFile, type ProviderDefinition, type ProviderType, type RegistryPluginLockEntry, type RunnerConfig, type RunnerStatsConfig, type SetupConfig, type SetupModelDefinition, type StaticConfigInput, type StaticConfigItem, type StaticPluginInstallOptions, type StaticPluginInstallResult, type StaticPluginReference, type StaticPluginResolver, type ToAlintConfigOptions, createEmptyPluginLockFile, emptySetupConfig, getGlobalSetupConfigPath, getProjectPluginLockPath, getProjectPluginStorePath, getProjectSetupConfigPath, getStatsDir, ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated, installStaticPlugins, listMissing, listUnresolved, loadAlintConfig, loadPluginLockFile, loadSetupConfig, loadStaticConfig, mergeSetupConfigs, parsePluginLockFile, parsePluginSpecifier, parseSetupConfigToml, parseStaticConfig, stringifySetupConfigToml, toAlintConfig, writePluginLockFile, writeSetupConfig };
|
|
248
|
+
export { type AlintConfig, BuiltInAgentName, CreateDeclarativePluginOptions, CreateReportScopeOptions, DeclarativeFinding, DeclarativeFindingResponse, DeclarativeRuleDefinition, type DirectoryPluginLockEntry, type GlobalSetupConfigPathOptions, LoadDeclarativeRulesOptions, type ModelSize, type ParseStaticConfigOptions, type ParsedDirectoryPluginLockEntry, type ParsedPluginLockEntry, type ParsedPluginLockFile, type ParsedPluginSpecifier, type ParsedRegistryPluginLockEntry, type ParsedStaticConfig, type ParsedStaticConfigGroup, type PluginLockEntry, type PluginLockFile, type ProviderDefinition, type ProviderType, type RegistryPluginLockEntry, ReportScope, type RunnerConfig, type RunnerStatsConfig, type SetupConfig, type SetupModelDefinition, type StaticConfigInput, type StaticConfigItem, type StaticPluginInstallOptions, type StaticPluginInstallResult, type StaticPluginReference, type StaticPluginResolver, type ToAlintConfigOptions, builtInAgentNames, createDeclarativePlugin, createEmptyPluginLockFile, createReportScope, declarativeFindingResponseSchema, declarativeFindingSchema, declarativeRuleFilePattern, emptySetupConfig, getGlobalSetupConfigPath, getProjectPluginLockPath, getProjectPluginStorePath, getProjectSetupConfigPath, getStatsDir, hasDeclarativeRuleFiles, ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated, installStaticPlugins, isBuiltInAgentName, listMissing, listUnresolved, loadAlintConfig, loadDeclarativeRules, loadPluginLockFile, loadSetupConfig, loadStaticConfig, mergeSetupConfigs, parsePluginLockFile, parsePluginSpecifier, parseSetupConfigToml, parseStaticConfig, stringifySetupConfigToml, toAlintConfig, writePluginLockFile, writeSetupConfig };
|
package/dist/index.mjs
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import { dirname, isAbsolute, join, normalize, posix, relative, resolve, sep, win32 } from "node:path";
|
|
1
|
+
import { basename, dirname, isAbsolute, join, normalize, posix, relative, resolve, sep, win32 } from "node:path";
|
|
2
2
|
import { loadConfig } from "c12";
|
|
3
3
|
import { createJiti } from "jiti/static";
|
|
4
4
|
import { createHash, randomUUID } from "node:crypto";
|
|
5
|
-
import { mkdir, readFile, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
6
|
-
import { array, boolean, literal, looseObject, optional, parse, picklist, record, strictObject, string, tuple, union, unknown, variant } from "valibot";
|
|
5
|
+
import { access, mkdir, open, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
6
|
+
import { array, boolean, check, description, literal, looseObject, number, object, optional, parse, picklist, pipe, record, strictObject, string, tuple, union, unknown, variant } from "valibot";
|
|
7
7
|
import process from "node:process";
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
|
-
import { dirname as dirname$1, extname, join as join$1 } from "pathe";
|
|
9
|
+
import { dirname as dirname$1, extname, join as join$1, normalize as normalize$1 } from "pathe";
|
|
10
10
|
import { xdgConfig } from "xdg-basedir";
|
|
11
|
-
import { isError } from "@moeru/std/error";
|
|
12
|
-
import {
|
|
11
|
+
import { errorMessageFrom, isError } from "@moeru/std/error";
|
|
12
|
+
import { formatOutputLanguageInstruction, formatSourceWithLineNumbers, generateStructured, toolParametersFromSchema } from "@alint-js/core/structured-output";
|
|
13
|
+
import { createTools, listFiles } from "@alint-js/tools-fs";
|
|
14
|
+
import { rawTool } from "@xsai/tool";
|
|
15
|
+
import { chat, hasToolCall, or, stepCountAtLeast, user } from "apeira";
|
|
13
16
|
import { Buffer } from "node:buffer";
|
|
17
|
+
import { minimatch } from "minimatch";
|
|
18
|
+
import { exports } from "resolve.exports";
|
|
14
19
|
import { createWriteStream } from "node:fs";
|
|
15
20
|
import { Readable } from "node:stream";
|
|
16
21
|
import { pipeline } from "node:stream/promises";
|
|
@@ -88,6 +93,388 @@ function parseIntegrity(integrity, specifier) {
|
|
|
88
93
|
};
|
|
89
94
|
}
|
|
90
95
|
//#endregion
|
|
96
|
+
//#region src/plugins/declarative/types.ts
|
|
97
|
+
const builtInAgentNames = ["basic-structured", "basic-coding-agent"];
|
|
98
|
+
const declarativeRuleFilePattern = "**/rule.alint.{toml,yaml,yml,json,jsonc,json5}";
|
|
99
|
+
const declarativeFindingSchema = pipe(object({
|
|
100
|
+
confidence: optional(pipe(picklist([
|
|
101
|
+
"high",
|
|
102
|
+
"medium",
|
|
103
|
+
"low"
|
|
104
|
+
]), description("Confidence in this finding. Use exactly \"high\", \"medium\", or \"low\" when confidence is known."))),
|
|
105
|
+
filePath: optional(pipe(string(), description("Path to the file that owns this finding. Omit when the finding belongs to the reviewed target file."))),
|
|
106
|
+
line: pipe(number(), description("Use the left-column line number from the numbered source block.")),
|
|
107
|
+
message: pipe(string(), description("Human-readable diagnostic message describing the issue.")),
|
|
108
|
+
suggestion: optional(pipe(string(), description("Concrete remediation direction for the finding.")))
|
|
109
|
+
}), description("One declarative rule finding."));
|
|
110
|
+
const declarativeFindingResponseSchema = pipe(object({ findings: pipe(array(declarativeFindingSchema), description("All findings for the declarative rule. Return an empty array when there are no issues.")) }), description("Structured declarative rule findings."));
|
|
111
|
+
function isBuiltInAgentName(value) {
|
|
112
|
+
return builtInAgentNames.includes(value);
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/plugins/declarative/parse.ts
|
|
116
|
+
const declarativeRuleFileNamePattern = /^rule\.alint\.(?:toml|ya?ml|json|jsonc|json5)$/;
|
|
117
|
+
const declarativeRuleNamePattern = /^[\w.-]+$/;
|
|
118
|
+
const nonEmptyStringSchema = pipe(string(), check((value) => value.trim() !== "", "must be a non-empty string"));
|
|
119
|
+
const declarativeRuleFileSchema = object({
|
|
120
|
+
builtInAgent: picklist(builtInAgentNames),
|
|
121
|
+
excludeFiles: optional(array(string())),
|
|
122
|
+
includeFiles: optional(array(string())),
|
|
123
|
+
instruction: nonEmptyStringSchema,
|
|
124
|
+
name: pipe(nonEmptyStringSchema, check((value) => declarativeRuleNamePattern.test(value), "Declarative rule \"name\" must contain only letters, numbers, dots, underscores, and hyphens."))
|
|
125
|
+
});
|
|
126
|
+
async function hasDeclarativeRuleFiles(root) {
|
|
127
|
+
return (await findDeclarativeRuleFiles(root)).length > 0;
|
|
128
|
+
}
|
|
129
|
+
async function loadDeclarativeRules(options) {
|
|
130
|
+
const files = await findDeclarativeRuleFiles(options.root);
|
|
131
|
+
if (files.length === 0) throw new Error(`Directory plugin "${options.alias}" must contain package.json or rule.alint.* files.`);
|
|
132
|
+
const rules = [];
|
|
133
|
+
const filesByName = /* @__PURE__ */ new Map();
|
|
134
|
+
for (const file of files) {
|
|
135
|
+
const rule = await loadDeclarativeRuleFile(file);
|
|
136
|
+
const duplicateFile = filesByName.get(rule.name);
|
|
137
|
+
if (duplicateFile !== void 0) throw new Error(`Duplicate declarative rule name "${rule.name}" in ${file} and ${duplicateFile}.`);
|
|
138
|
+
filesByName.set(rule.name, file);
|
|
139
|
+
rules.push(rule);
|
|
140
|
+
}
|
|
141
|
+
return rules;
|
|
142
|
+
}
|
|
143
|
+
async function collectDeclarativeRuleFiles(directory, files) {
|
|
144
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
145
|
+
await Promise.all(entries.map(async (entry) => {
|
|
146
|
+
const path = join(directory, entry.name);
|
|
147
|
+
if (entry.isDirectory()) {
|
|
148
|
+
await collectDeclarativeRuleFiles(path, files);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (entry.isFile() && declarativeRuleFileNamePattern.test(entry.name)) files.push(path);
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
async function findDeclarativeRuleFiles(root) {
|
|
155
|
+
const files = [];
|
|
156
|
+
await collectDeclarativeRuleFiles(root, files);
|
|
157
|
+
return files.sort((left, right) => left.localeCompare(right));
|
|
158
|
+
}
|
|
159
|
+
function isPlainObject$3(value) {
|
|
160
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
161
|
+
}
|
|
162
|
+
async function loadDeclarativeRuleFile(filePath) {
|
|
163
|
+
const input = await loadDeclarativeRuleFileContent(filePath);
|
|
164
|
+
if (!isPlainObject$3(input)) throw new Error(`Declarative rule file ${filePath} must contain an object.`);
|
|
165
|
+
const parsed = parseDeclarativeRuleFileInput(input, filePath);
|
|
166
|
+
return {
|
|
167
|
+
builtInAgent: parsed.builtInAgent,
|
|
168
|
+
excludeFiles: parsed.excludeFiles ?? [],
|
|
169
|
+
filePath,
|
|
170
|
+
includeFiles: parsed.includeFiles,
|
|
171
|
+
instruction: parsed.instruction,
|
|
172
|
+
name: parsed.name
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
async function loadDeclarativeRuleFileContent(filePath) {
|
|
176
|
+
try {
|
|
177
|
+
return (await loadConfig({
|
|
178
|
+
configFile: basename(filePath),
|
|
179
|
+
cwd: dirname(filePath),
|
|
180
|
+
envName: false,
|
|
181
|
+
extend: false,
|
|
182
|
+
packageJson: false,
|
|
183
|
+
rcFile: false
|
|
184
|
+
})).config;
|
|
185
|
+
} catch (error) {
|
|
186
|
+
throw new Error(`Could not parse declarative rule file ${filePath}: ${errorMessageFrom(error) ?? "Unknown error"}`, { cause: error });
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function parseDeclarativeRuleFileInput(input, filePath) {
|
|
190
|
+
try {
|
|
191
|
+
return parse(declarativeRuleFileSchema, input);
|
|
192
|
+
} catch (error) {
|
|
193
|
+
if (typeof input.builtInAgent === "string" && !isBuiltInAgentName(input.builtInAgent)) throw new Error(`Unknown builtInAgent "${input.builtInAgent}" in ${filePath}.`, { cause: error });
|
|
194
|
+
throw new Error(`Invalid declarative rule file ${filePath}: ${errorMessageFrom(error) ?? "Unknown error"}`, { cause: error });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/plugins/declarative/scope.ts
|
|
199
|
+
function createReportScope(options) {
|
|
200
|
+
const includeFiles = options.includeFiles;
|
|
201
|
+
const excludeFiles = options.excludeFiles;
|
|
202
|
+
const rawTargetFilePath = options.targetFilePath;
|
|
203
|
+
const hasTargetFilePath = rawTargetFilePath !== void 0;
|
|
204
|
+
const targetFilePath = rawTargetFilePath === void 0 ? void 0 : toScopedRelativePath(options.cwd, rawTargetFilePath);
|
|
205
|
+
return { canReport(filePath) {
|
|
206
|
+
if (filePath === void 0) return !hasTargetFilePath && includeFiles === void 0;
|
|
207
|
+
const relativePath = toScopedRelativePath(options.cwd, filePath);
|
|
208
|
+
if (relativePath === void 0) return false;
|
|
209
|
+
if (!(includeFiles === void 0 ? targetFilePath !== void 0 && relativePath === targetFilePath : includeFiles.some((pattern) => minimatch(relativePath, pattern, { dot: true })))) return false;
|
|
210
|
+
return !excludeFiles.some((pattern) => minimatch(relativePath, pattern, { dot: true }));
|
|
211
|
+
} };
|
|
212
|
+
}
|
|
213
|
+
function isOutsideScopePath(path) {
|
|
214
|
+
return path === ".." || path.startsWith("../") || isAbsolute(path) || win32.isAbsolute(path) || /^[A-Za-z]:/u.test(path);
|
|
215
|
+
}
|
|
216
|
+
function normalizePath(path) {
|
|
217
|
+
return normalize$1(path).replace(/^\.\//u, "");
|
|
218
|
+
}
|
|
219
|
+
function toScopedRelativePath(cwd, filePath) {
|
|
220
|
+
const path = normalizePath(relative(cwd, filePath));
|
|
221
|
+
if (isOutsideScopePath(path)) return;
|
|
222
|
+
return path;
|
|
223
|
+
}
|
|
224
|
+
//#endregion
|
|
225
|
+
//#region src/agents/presets/basic-structured.ts
|
|
226
|
+
const maxSupplementalFileBytes = 64 * 1024;
|
|
227
|
+
const maxSupplementalFiles = 12;
|
|
228
|
+
function createBasicStructuredRule(rule) {
|
|
229
|
+
return {
|
|
230
|
+
cache: rule.includeFiles === void 0 || rule.includeFiles.length === 0,
|
|
231
|
+
create: (ctx) => ({ async onTarget(target) {
|
|
232
|
+
if (target.kind !== "file") return;
|
|
233
|
+
const model = await ctx.model();
|
|
234
|
+
const supplementalFiles = await collectSupplementalFiles({
|
|
235
|
+
cwd: ctx.cwd,
|
|
236
|
+
includeFiles: rule.includeFiles,
|
|
237
|
+
logger: ctx.logger,
|
|
238
|
+
targetFilePath: target.file.path
|
|
239
|
+
});
|
|
240
|
+
const { findings } = await generateStructured({
|
|
241
|
+
createMessages: (retryFeedback) => createStructuredMessagesSync({
|
|
242
|
+
cwd: ctx.cwd,
|
|
243
|
+
includeFiles: rule.includeFiles,
|
|
244
|
+
instruction: rule.instruction,
|
|
245
|
+
outputLanguage: ctx.outputLanguage,
|
|
246
|
+
retryFeedback,
|
|
247
|
+
ruleFilePath: rule.filePath,
|
|
248
|
+
sourceText: target.file.text,
|
|
249
|
+
supplementalFiles,
|
|
250
|
+
targetFilePath: target.file.path
|
|
251
|
+
}),
|
|
252
|
+
logger: ctx.logger,
|
|
253
|
+
metering: ctx.metering,
|
|
254
|
+
model,
|
|
255
|
+
operation: `declarative-${rule.name}-structured`,
|
|
256
|
+
schema: declarativeFindingResponseSchema
|
|
257
|
+
});
|
|
258
|
+
reportDeclarativeFindings({
|
|
259
|
+
ctx,
|
|
260
|
+
excludeFiles: rule.excludeFiles,
|
|
261
|
+
findings,
|
|
262
|
+
includeFiles: rule.includeFiles,
|
|
263
|
+
targetFilePath: target.file.path
|
|
264
|
+
});
|
|
265
|
+
} })
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function createStructuredMessagesSync(options) {
|
|
269
|
+
return [
|
|
270
|
+
{
|
|
271
|
+
content: options.instruction,
|
|
272
|
+
role: "system"
|
|
273
|
+
},
|
|
274
|
+
...options.retryFeedback ? [{
|
|
275
|
+
content: options.retryFeedback,
|
|
276
|
+
role: "user"
|
|
277
|
+
}] : [],
|
|
278
|
+
{
|
|
279
|
+
content: [
|
|
280
|
+
formatOutputLanguageInstruction(options.outputLanguage),
|
|
281
|
+
`Rule file path: ${options.ruleFilePath}`,
|
|
282
|
+
`Reviewed target file path: ${options.targetFilePath}`,
|
|
283
|
+
formatSupplementalFiles(options.cwd, options.supplementalFiles ?? []),
|
|
284
|
+
`Reviewed target source with line numbers:\n\n${formatSourceWithLineNumbers(options.sourceText)}`
|
|
285
|
+
].filter(Boolean).join("\n\n"),
|
|
286
|
+
role: "user"
|
|
287
|
+
}
|
|
288
|
+
];
|
|
289
|
+
}
|
|
290
|
+
function reportDeclarativeFindings(options) {
|
|
291
|
+
const scope = createReportScope({
|
|
292
|
+
cwd: options.ctx.cwd,
|
|
293
|
+
excludeFiles: options.excludeFiles,
|
|
294
|
+
includeFiles: options.includeFiles,
|
|
295
|
+
targetFilePath: options.targetFilePath
|
|
296
|
+
});
|
|
297
|
+
for (const finding of options.findings) {
|
|
298
|
+
const filePath = resolveFindingFilePath(options.ctx.cwd, options.targetFilePath, finding.filePath);
|
|
299
|
+
if (!scope.canReport(filePath)) {
|
|
300
|
+
options.ctx.logger.debug("Ignoring out-of-scope declarative finding", {
|
|
301
|
+
filePath,
|
|
302
|
+
message: finding.message
|
|
303
|
+
});
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
const evidence = createFindingEvidence(finding);
|
|
307
|
+
options.ctx.report({
|
|
308
|
+
...evidence ? { evidence } : {},
|
|
309
|
+
filePath,
|
|
310
|
+
loc: { start: {
|
|
311
|
+
column: 0,
|
|
312
|
+
line: finding.line
|
|
313
|
+
} },
|
|
314
|
+
message: finding.message
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
async function collectSupplementalFiles(options) {
|
|
319
|
+
if (options.includeFiles === void 0 || options.includeFiles.length === 0) return [];
|
|
320
|
+
const targetFilePath = resolve(options.targetFilePath);
|
|
321
|
+
const filePaths = (await listFiles(options.cwd, { patterns: options.includeFiles })).map((filePath) => resolve(filePath)).filter((filePath) => filePath !== targetFilePath).sort().slice(0, maxSupplementalFiles);
|
|
322
|
+
const files = [];
|
|
323
|
+
for (const filePath of filePaths) {
|
|
324
|
+
const content = await readSupplementalFile(filePath, options.logger);
|
|
325
|
+
if (content === void 0) continue;
|
|
326
|
+
files.push({
|
|
327
|
+
content,
|
|
328
|
+
filePath
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
return files;
|
|
332
|
+
}
|
|
333
|
+
function createFindingEvidence(finding) {
|
|
334
|
+
const evidence = {
|
|
335
|
+
...finding.confidence ? { confidence: finding.confidence } : {},
|
|
336
|
+
...finding.suggestion ? { suggestion: finding.suggestion } : {}
|
|
337
|
+
};
|
|
338
|
+
return Object.keys(evidence).length > 0 ? evidence : void 0;
|
|
339
|
+
}
|
|
340
|
+
function formatSupplementalFiles(cwd, files) {
|
|
341
|
+
if (files.length === 0) return;
|
|
342
|
+
return ["Supplemental files:", ...files.map((file) => [`File: ${relative(cwd, file.filePath)}`, file.content].join("\n\n"))].join("\n\n");
|
|
343
|
+
}
|
|
344
|
+
async function readSupplementalFile(filePath, logger) {
|
|
345
|
+
let handle;
|
|
346
|
+
try {
|
|
347
|
+
handle = await open(filePath, "r");
|
|
348
|
+
const buffer = Buffer.alloc(65537);
|
|
349
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
350
|
+
if (bytesRead > maxSupplementalFileBytes) {
|
|
351
|
+
logger?.debug("Skipping oversized declarative supplemental file", {
|
|
352
|
+
filePath,
|
|
353
|
+
maxBytes: maxSupplementalFileBytes
|
|
354
|
+
});
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
return buffer.subarray(0, bytesRead).toString("utf8");
|
|
358
|
+
} catch (error) {
|
|
359
|
+
logger?.debug("Skipping unreadable declarative supplemental file", {
|
|
360
|
+
error,
|
|
361
|
+
filePath
|
|
362
|
+
});
|
|
363
|
+
return;
|
|
364
|
+
} finally {
|
|
365
|
+
await handle?.close();
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
function resolveFindingFilePath(cwd, targetFilePath, findingFilePath) {
|
|
369
|
+
if (findingFilePath === void 0 || findingFilePath.length === 0) return targetFilePath;
|
|
370
|
+
return isAbsolute(findingFilePath) ? findingFilePath : resolve(cwd, findingFilePath);
|
|
371
|
+
}
|
|
372
|
+
//#endregion
|
|
373
|
+
//#region src/agents/presets/basic-coding-agent.ts
|
|
374
|
+
const maxAgentSteps = 8;
|
|
375
|
+
const reportFindingsToolName = "report_findings";
|
|
376
|
+
function createBasicCodingAgentRule(rule) {
|
|
377
|
+
return {
|
|
378
|
+
cache: false,
|
|
379
|
+
create: (ctx) => ({ async onTarget(target) {
|
|
380
|
+
if (target.kind !== "file") return;
|
|
381
|
+
const model = await ctx.model();
|
|
382
|
+
const result = await createCodingAgent(model).run({
|
|
383
|
+
cwd: ctx.cwd,
|
|
384
|
+
instruction: rule.instruction,
|
|
385
|
+
outputLanguage: ctx.outputLanguage,
|
|
386
|
+
sourceText: target.file.text,
|
|
387
|
+
targetFilePath: target.file.path,
|
|
388
|
+
tools: createTools(ctx.cwd)
|
|
389
|
+
});
|
|
390
|
+
if (result.usage) ctx.metering.recordUsage({
|
|
391
|
+
filePath: target.file.path,
|
|
392
|
+
inputTokens: result.usage.inputTokens,
|
|
393
|
+
metadata: { operation: `declarative-${rule.name}-coding-agent` },
|
|
394
|
+
modelId: model.id,
|
|
395
|
+
outputTokens: result.usage.outputTokens,
|
|
396
|
+
providerId: model.provider.id,
|
|
397
|
+
ruleId: ctx.id,
|
|
398
|
+
totalTokens: result.usage.totalTokens
|
|
399
|
+
});
|
|
400
|
+
reportDeclarativeFindings({
|
|
401
|
+
ctx,
|
|
402
|
+
excludeFiles: rule.excludeFiles,
|
|
403
|
+
findings: result.findings,
|
|
404
|
+
includeFiles: rule.includeFiles,
|
|
405
|
+
targetFilePath: target.file.path
|
|
406
|
+
});
|
|
407
|
+
} })
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function createCodingAgent(model, runner = chat({
|
|
411
|
+
baseURL: model.provider.endpoint,
|
|
412
|
+
headers: model.provider.headers,
|
|
413
|
+
model: model.id,
|
|
414
|
+
parallelToolCalls: false,
|
|
415
|
+
stopWhen: or(hasToolCall(reportFindingsToolName), stepCountAtLeast(maxAgentSteps)),
|
|
416
|
+
toolChoice: "required"
|
|
417
|
+
})) {
|
|
418
|
+
return { async run(options) {
|
|
419
|
+
let report;
|
|
420
|
+
const tools = options.tools.map((agentTool) => rawTool({
|
|
421
|
+
description: agentTool.description,
|
|
422
|
+
execute: async (input) => {
|
|
423
|
+
return await agentTool.execute(input) ?? "";
|
|
424
|
+
},
|
|
425
|
+
name: agentTool.name,
|
|
426
|
+
parameters: agentTool.parameters
|
|
427
|
+
}));
|
|
428
|
+
tools.push(rawTool({
|
|
429
|
+
description: "Submit all findings and finish the review. Submit an empty findings array when there are no issues.",
|
|
430
|
+
execute: async (input) => {
|
|
431
|
+
report = parse(declarativeFindingResponseSchema, input);
|
|
432
|
+
return report;
|
|
433
|
+
},
|
|
434
|
+
name: reportFindingsToolName,
|
|
435
|
+
parameters: toolParametersFromSchema(declarativeFindingResponseSchema),
|
|
436
|
+
strict: true
|
|
437
|
+
}));
|
|
438
|
+
const result = await runner({
|
|
439
|
+
channel: {
|
|
440
|
+
emit: () => {},
|
|
441
|
+
subscribe: () => () => {}
|
|
442
|
+
},
|
|
443
|
+
input: [user([
|
|
444
|
+
formatOutputLanguageInstruction(options.outputLanguage),
|
|
445
|
+
`Project root: ${options.cwd}`,
|
|
446
|
+
`Reviewed target file path: ${options.targetFilePath}`,
|
|
447
|
+
`Reviewed target source with line numbers:\n\n${formatSourceWithLineNumbers(options.sourceText)}`
|
|
448
|
+
].filter(Boolean).join("\n\n"))],
|
|
449
|
+
instructions: [
|
|
450
|
+
options.instruction,
|
|
451
|
+
"Use the filesystem tools to inspect the project as needed before reaching a conclusion.",
|
|
452
|
+
`When the review is complete, call ${reportFindingsToolName} exactly once with all findings. Submit an empty findings array when there are no issues.`
|
|
453
|
+
].join("\n\n"),
|
|
454
|
+
tools,
|
|
455
|
+
turnId: "alint"
|
|
456
|
+
});
|
|
457
|
+
if (!report) throw new Error(`basic-coding-agent stopped without calling ${reportFindingsToolName}`);
|
|
458
|
+
return {
|
|
459
|
+
findings: report.findings,
|
|
460
|
+
usage: result.usage ? {
|
|
461
|
+
inputTokens: result.usage.inputTokens,
|
|
462
|
+
outputTokens: result.usage.outputTokens,
|
|
463
|
+
totalTokens: result.usage.totalTokens
|
|
464
|
+
} : void 0
|
|
465
|
+
};
|
|
466
|
+
} };
|
|
467
|
+
}
|
|
468
|
+
//#endregion
|
|
469
|
+
//#region src/plugins/declarative/plugin.ts
|
|
470
|
+
function createDeclarativePlugin(options) {
|
|
471
|
+
return { rules: Object.fromEntries(options.rules.map((rule) => [rule.name, createDeclarativeRule(rule)])) };
|
|
472
|
+
}
|
|
473
|
+
function createDeclarativeRule(rule) {
|
|
474
|
+
if (rule.builtInAgent === "basic-coding-agent") return createBasicCodingAgentRule(rule);
|
|
475
|
+
return createBasicStructuredRule(rule);
|
|
476
|
+
}
|
|
477
|
+
//#endregion
|
|
91
478
|
//#region src/plugins/sources/manifest.ts
|
|
92
479
|
function resolveRelativeRootEntry(packageJson) {
|
|
93
480
|
const [entry] = exports(packageJson, ".", {
|
|
@@ -114,7 +501,7 @@ async function createLockEntry$1(installed, options) {
|
|
|
114
501
|
}
|
|
115
502
|
async function install$1(options) {
|
|
116
503
|
const packageDir = await canonicalRoot(options.alias, options.specifier.directory);
|
|
117
|
-
await
|
|
504
|
+
await validateInstallRoot(options.alias, packageDir);
|
|
118
505
|
return {
|
|
119
506
|
path: packageDir,
|
|
120
507
|
type: "directory"
|
|
@@ -124,10 +511,7 @@ async function resolve$2(entry) {
|
|
|
124
511
|
const lockedPath = isAbsolute(entry.lockEntry.path) || win32.isAbsolute(entry.lockEntry.path) ? entry.lockEntry.path : resolve(entry.cwd, entry.lockEntry.path);
|
|
125
512
|
const [packageDir, lockedPackageDir] = await Promise.all([lockedRoot(entry.alias, entry.specifier.directory, "configured"), lockedRoot(entry.alias, lockedPath, "locked")]);
|
|
126
513
|
if (packageDir !== lockedPackageDir) throw new Error(`Directory plugin "${entry.alias}" has moved or its symlink target changed. Run: alint plugin install`);
|
|
127
|
-
return
|
|
128
|
-
cache: "content",
|
|
129
|
-
entry: await validateRoot(entry.alias, packageDir)
|
|
130
|
-
};
|
|
514
|
+
return validateResolvedRoot(entry.alias, packageDir);
|
|
131
515
|
}
|
|
132
516
|
async function canonicalRoot(alias, directory) {
|
|
133
517
|
let packageDir;
|
|
@@ -146,6 +530,15 @@ async function canonicalRoot(alias, directory) {
|
|
|
146
530
|
if (!directoryStat.isDirectory()) throw new Error(`Directory plugin "${alias}" path "${directory}" is not a directory.`);
|
|
147
531
|
return packageDir;
|
|
148
532
|
}
|
|
533
|
+
async function hasPackageJson(packageDir) {
|
|
534
|
+
try {
|
|
535
|
+
await access(join(packageDir, "package.json"));
|
|
536
|
+
return true;
|
|
537
|
+
} catch (error) {
|
|
538
|
+
if (isENOENTError(error)) return false;
|
|
539
|
+
throw error;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
149
542
|
async function lockedRoot(alias, directory, source) {
|
|
150
543
|
try {
|
|
151
544
|
return await realpath(directory);
|
|
@@ -154,7 +547,25 @@ async function lockedRoot(alias, directory, source) {
|
|
|
154
547
|
throw new Error(`Could not resolve ${source} directory plugin "${alias}" at "${directory}".`, { cause: error });
|
|
155
548
|
}
|
|
156
549
|
}
|
|
157
|
-
async function
|
|
550
|
+
async function validateInstallRoot(alias, packageDir) {
|
|
551
|
+
if (await hasPackageJson(packageDir)) return {
|
|
552
|
+
alias,
|
|
553
|
+
cache: "content",
|
|
554
|
+
entry: await validatePackageRoot(alias, packageDir),
|
|
555
|
+
kind: "module"
|
|
556
|
+
};
|
|
557
|
+
await loadDeclarativeRules({
|
|
558
|
+
alias,
|
|
559
|
+
root: packageDir
|
|
560
|
+
});
|
|
561
|
+
return {
|
|
562
|
+
alias,
|
|
563
|
+
cache: "content",
|
|
564
|
+
entry: packageDir,
|
|
565
|
+
kind: "declarative"
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
async function validatePackageRoot(alias, packageDir) {
|
|
158
569
|
const manifestPath = join(packageDir, "package.json");
|
|
159
570
|
let packageJson;
|
|
160
571
|
try {
|
|
@@ -183,6 +594,21 @@ async function validateRoot(alias, packageDir) {
|
|
|
183
594
|
if (!entryStat.isFile()) throw new Error(`Directory plugin "${alias}" entry "${relativeEntry}" is not a regular file.`);
|
|
184
595
|
return physicalEntry;
|
|
185
596
|
}
|
|
597
|
+
async function validateResolvedRoot(alias, packageDir) {
|
|
598
|
+
if (await hasPackageJson(packageDir)) return {
|
|
599
|
+
alias,
|
|
600
|
+
cache: "content",
|
|
601
|
+
entry: await validatePackageRoot(alias, packageDir),
|
|
602
|
+
kind: "module"
|
|
603
|
+
};
|
|
604
|
+
if (!await hasDeclarativeRuleFiles(packageDir)) throw new Error(`Directory plugin "${alias}" must contain package.json or rule.alint.* files.`);
|
|
605
|
+
return {
|
|
606
|
+
alias,
|
|
607
|
+
cache: "content",
|
|
608
|
+
entry: packageDir,
|
|
609
|
+
kind: "declarative"
|
|
610
|
+
};
|
|
611
|
+
}
|
|
186
612
|
//#endregion
|
|
187
613
|
//#region src/plugins/sources/package/index.ts
|
|
188
614
|
function createLockEntry(installed, options) {
|
|
@@ -246,8 +672,10 @@ async function resolve$1(entry) {
|
|
|
246
672
|
if (!isPathInside(resolvedEntry, packageDir)) throw new Error(`Plugin lock entry "${entry.alias}" resolves outside the locked package directory.`);
|
|
247
673
|
JSON.parse(await readFile(join(packageDir, "package.json"), "utf8"));
|
|
248
674
|
return {
|
|
675
|
+
alias: entry.alias,
|
|
249
676
|
cache: "default",
|
|
250
|
-
entry: resolvedEntry
|
|
677
|
+
entry: resolvedEntry,
|
|
678
|
+
kind: "module"
|
|
251
679
|
};
|
|
252
680
|
}
|
|
253
681
|
async function extractEntry(packageDir, header, stream) {
|
|
@@ -563,6 +991,10 @@ function resolveDirectoryLockIdentity(path, cwd) {
|
|
|
563
991
|
//#endregion
|
|
564
992
|
//#region src/plugins/sources/import.ts
|
|
565
993
|
async function importPlugin(target) {
|
|
994
|
+
if (target.kind === "declarative") return createDeclarativePlugin({ rules: await loadDeclarativeRules({
|
|
995
|
+
alias: target.alias,
|
|
996
|
+
root: target.entry
|
|
997
|
+
}) });
|
|
566
998
|
const url = pathToFileURL(target.entry);
|
|
567
999
|
if (target.cache === "content") url.searchParams.set("content", createHash("sha256").update(await readFile(target.entry)).digest("hex"));
|
|
568
1000
|
return getDefaultExport(await import(url.href));
|
|
@@ -1094,4 +1526,4 @@ async function writeSetupConfig(filePath, config) {
|
|
|
1094
1526
|
await writeFile(filePath, stringifySetupConfigToml(config), "utf8");
|
|
1095
1527
|
}
|
|
1096
1528
|
//#endregion
|
|
1097
|
-
export { createEmptyPluginLockFile, emptySetupConfig, getGlobalSetupConfigPath, getProjectPluginLockPath, getProjectPluginStorePath, getProjectSetupConfigPath, getStatsDir, ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated, installStaticPlugins, listMissing, listUnresolved, loadAlintConfig, loadPluginLockFile, loadSetupConfig, loadStaticConfig, mergeSetupConfigs, parsePluginLockFile, parsePluginSpecifier, parseSetupConfigToml, parseStaticConfig, stringifySetupConfigToml, toAlintConfig, writePluginLockFile, writeSetupConfig };
|
|
1529
|
+
export { builtInAgentNames, createDeclarativePlugin, createEmptyPluginLockFile, createReportScope, declarativeFindingResponseSchema, declarativeFindingSchema, declarativeRuleFilePattern, emptySetupConfig, getGlobalSetupConfigPath, getProjectPluginLockPath, getProjectPluginStorePath, getProjectSetupConfigPath, getStatsDir, hasDeclarativeRuleFiles, ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated, installStaticPlugins, isBuiltInAgentName, listMissing, listUnresolved, loadAlintConfig, loadDeclarativeRules, loadPluginLockFile, loadSetupConfig, loadStaticConfig, mergeSetupConfigs, parsePluginLockFile, parsePluginSpecifier, parseSetupConfigToml, parseStaticConfig, stringifySetupConfigToml, toAlintConfig, writePluginLockFile, writeSetupConfig };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alint-js/config",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.22",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"types": "./dist/index.d.mts",
|
|
@@ -15,9 +15,12 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@moeru/std": "^0.1.0-beta.18",
|
|
17
17
|
"@package-json/types": "^0.0.13",
|
|
18
|
+
"@xsai/tool": "^0.5.0-beta.6",
|
|
19
|
+
"apeira": "^0.0.6",
|
|
18
20
|
"c12": "^3.3.4",
|
|
19
21
|
"es-toolkit": "^1.49.0",
|
|
20
22
|
"jiti": "^2.7.0",
|
|
23
|
+
"minimatch": "^10.2.5",
|
|
21
24
|
"ofetch": "^1.5.1",
|
|
22
25
|
"pathe": "^2.0.3",
|
|
23
26
|
"resolve.exports": "2.0.3",
|
|
@@ -25,15 +28,12 @@
|
|
|
25
28
|
"tar-stream": "^3.2.0",
|
|
26
29
|
"valibot": "^1.4.1",
|
|
27
30
|
"xdg-basedir": "^5.1.0",
|
|
28
|
-
"@alint-js/core": "0.0.
|
|
31
|
+
"@alint-js/core": "0.0.22",
|
|
32
|
+
"@alint-js/tools-fs": "0.0.22"
|
|
29
33
|
},
|
|
30
34
|
"devDependencies": {
|
|
31
|
-
"@types/node": "^26.0.1",
|
|
32
35
|
"@types/tar-stream": "^3.1.4",
|
|
33
|
-
"h3": "2.0.1-rc.25"
|
|
34
|
-
"tsdown": "^0.22.3",
|
|
35
|
-
"typescript": "^6.0.3",
|
|
36
|
-
"vitest": "^4.1.9"
|
|
36
|
+
"h3": "2.0.1-rc.25"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "tsdown",
|