@alint-js/core 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.mjs ADDED
@@ -0,0 +1,1210 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import process, { cwd } from "node:process";
3
+ import { errorCauseFrom, errorMessageFrom } from "@moeru/std/error";
4
+ import { resolve } from "pathe";
5
+ import { createHash, randomUUID } from "node:crypto";
6
+ import { statSync } from "node:fs";
7
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
8
+ import { dirname, extname, isAbsolute, join, relative, resolve as resolve$1, sep } from "node:path";
9
+ import { array, check, description, is, literal, number, object, optional, pipe, record, string, union, unknown } from "valibot";
10
+ import { parseSync } from "oxc-parser";
11
+ import { clamp } from "es-toolkit";
12
+ //#region package.json
13
+ var version = "0.0.4";
14
+ //#endregion
15
+ //#region src/dsl/registry.ts
16
+ function buildRuleRegistry(config) {
17
+ const rules = /* @__PURE__ */ new Map();
18
+ const enabledRules = [];
19
+ for (const plugin of config.plugins ?? []) for (const [localId, rule] of Object.entries(plugin.rules)) {
20
+ const id = `${plugin.scope}/${localId}`;
21
+ if (rules.has(id)) throw new Error(`Duplicate rule id "${id}".`);
22
+ rules.set(id, rule);
23
+ const severity = normalizeSeverity(config.rules?.[id]);
24
+ if (severity !== "off") enabledRules.push({
25
+ id,
26
+ localId,
27
+ rule,
28
+ scope: plugin.scope,
29
+ severity
30
+ });
31
+ }
32
+ for (const id of Object.keys(config.rules ?? {})) if (!rules.has(id)) throw new Error(`Unknown rule "${id}".`);
33
+ return {
34
+ enabledRules,
35
+ rules
36
+ };
37
+ }
38
+ function normalizeSeverity(entry) {
39
+ if (Array.isArray(entry)) return entry[0] ?? "warn";
40
+ return entry ?? "warn";
41
+ }
42
+ //#endregion
43
+ //#region src/models/resolve.ts
44
+ function resolveModel(registry, options = {}) {
45
+ const candidates = flattenModels(registry);
46
+ const ruleId = options.ruleId ?? "<unknown>";
47
+ if (options.request !== void 0) {
48
+ const request = options.request;
49
+ const candidate = candidates.find(({ model }) => matchesRequest(model, request));
50
+ if (candidate === void 0) throw new Error(`Unknown model "${request}".`);
51
+ if (!satisfiesHardRequirements(candidate.model, options.requirement)) throw new Error(`Model "${request}" does not satisfy requirement for rule "${ruleId}".`);
52
+ return toResolvedModel(candidate, options.requirement);
53
+ }
54
+ const candidate = preferSize(candidates.filter(({ model }) => satisfiesHardRequirements(model, options.requirement)), options.requirement);
55
+ if (candidate === void 0) throw new Error(`No model satisfies requirement for rule "${ruleId}".`);
56
+ return toResolvedModel(candidate, options.requirement);
57
+ }
58
+ function flattenModels(registry) {
59
+ return registry.providers.flatMap((provider) => provider.models.map((model) => ({
60
+ model,
61
+ provider
62
+ })));
63
+ }
64
+ function matchesRequest(model, request) {
65
+ return model.id === request || model.name === request || (model.aliases ?? []).includes(request);
66
+ }
67
+ function preferSize(candidates, requirement) {
68
+ if (requirement?.size === void 0) return candidates[0];
69
+ return candidates.find(({ model }) => model.size === requirement.size) ?? candidates[0];
70
+ }
71
+ function satisfiesHardRequirements(model, requirement) {
72
+ if (requirement === void 0) return true;
73
+ if (requirement.capabilities !== void 0 && !requirement.capabilities.every((capability) => (model.capabilities ?? []).includes(capability))) return false;
74
+ if (requirement.minContextWindow !== void 0 && (model.contextWindow === void 0 || model.contextWindow < requirement.minContextWindow)) return false;
75
+ return true;
76
+ }
77
+ function toResolvedModel(candidate, requirement) {
78
+ const { model, provider } = candidate;
79
+ return {
80
+ aliases: [...model.aliases ?? []],
81
+ capabilities: [...model.capabilities ?? []],
82
+ contextWindow: model.contextWindow,
83
+ id: model.id,
84
+ name: model.name ?? model.id,
85
+ params: {
86
+ ...model.defaultParams ?? {},
87
+ ...requirement?.params ?? {}
88
+ },
89
+ provider: {
90
+ endpoint: provider.endpoint,
91
+ headers: { ...provider.headers ?? {} },
92
+ id: provider.id,
93
+ type: provider.type
94
+ },
95
+ size: model.size
96
+ };
97
+ }
98
+ //#endregion
99
+ //#region src/core/cache.ts
100
+ const CACHE_SCHEMA_VERSION = 1;
101
+ const DEFAULT_CACHE_FILE_NAME = ".alintcache";
102
+ const CacheFileSchema = pipe(object({
103
+ createdAt: pipe(string(), description("Cache file creation timestamp.")),
104
+ entries: pipe(unknown(), check((value) => typeof value === "object" && value !== null && !Array.isArray(value)), record(string(), object({
105
+ diagnostics: pipe(array(object({
106
+ filePath: pipe(string(), description("Diagnostic file path.")),
107
+ message: pipe(string(), description("Diagnostic message.")),
108
+ ruleId: pipe(string(), description("Diagnostic rule id.")),
109
+ severity: pipe(union([literal("error"), literal("warn")]), description("Diagnostic severity."))
110
+ })), description("Diagnostics produced for the cached target.")),
111
+ filePath: pipe(string(), description("Cache entry file path.")),
112
+ fingerprint: pipe(object({
113
+ alintVersion: pipe(string(), description("Alint version used to create the cache entry.")),
114
+ configHash: pipe(string(), description("Runner config hash used to create the cache entry.")),
115
+ modelHash: pipe(string(), description("Model configuration hash used to create the cache entry.")),
116
+ ruleHash: pipe(string(), description("Rule configuration hash used to create the cache entry."))
117
+ }), description("Cache entry fingerprint.")),
118
+ target: pipe(object({
119
+ hash: pipe(string(), description("Cached target hash.")),
120
+ identity: pipe(string(), description("Stable cached target identity.")),
121
+ kind: pipe(union([
122
+ literal("file"),
123
+ literal("class"),
124
+ literal("function")
125
+ ]), description("Cached target kind."))
126
+ }), description("Cached target metadata.")),
127
+ usage: pipe(array(object({
128
+ inputTokens: pipe(optional(number()), description("Optional input token count.")),
129
+ modelId: pipe(string(), description("Usage model id.")),
130
+ outputTokens: pipe(optional(number()), description("Optional output token count.")),
131
+ providerId: pipe(string(), description("Usage provider id.")),
132
+ ruleId: pipe(string(), description("Usage rule id.")),
133
+ totalTokens: pipe(optional(number()), description("Optional total token count."))
134
+ })), description("Inference usage records for the cache entry."))
135
+ })), description("Cache entries keyed by cache key.")),
136
+ files: pipe(unknown(), check((value) => typeof value === "object" && value !== null && !Array.isArray(value)), record(string(), object({
137
+ contentHash: pipe(string(), description("Cached file content hash.")),
138
+ entries: pipe(array(string()), description("Cache entry keys associated with the file.")),
139
+ path: pipe(string(), description("Normalized cached file path."))
140
+ })), description("Cached files keyed by normalized file path.")),
141
+ schemaVersion: pipe(literal(CACHE_SCHEMA_VERSION), description("Cache schema version.")),
142
+ updatedAt: pipe(string(), description("Cache file update timestamp."))
143
+ }), description("Alint cache file."));
144
+ function createCacheKey(input) {
145
+ return stableHash(input);
146
+ }
147
+ async function createCacheStore(options) {
148
+ const location = resolveCacheLocation(options.cwd, options.location);
149
+ if (!options.enabled) return createNoopCacheStore(location);
150
+ const cacheFile = await readCacheFile(location);
151
+ return {
152
+ get: (key) => cacheFile.entries[key],
153
+ location,
154
+ markFile: (filePath, contentHash, entries) => {
155
+ const path = normalizeCachePath(options.cwd, filePath);
156
+ cacheFile.files[path] = {
157
+ contentHash,
158
+ entries,
159
+ path
160
+ };
161
+ },
162
+ reconcile: async () => {
163
+ await mkdir(dirname(location), { recursive: true });
164
+ cacheFile.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
165
+ const tempPath = join(dirname(location), `.${DEFAULT_CACHE_FILE_NAME}.${process.pid}.${randomUUID()}.tmp`);
166
+ await writeFile(tempPath, `${JSON.stringify(cacheFile, null, 2)}\n`);
167
+ await rename(tempPath, location);
168
+ },
169
+ set: (key, entry) => {
170
+ cacheFile.entries[key] = entry;
171
+ }
172
+ };
173
+ }
174
+ function createTargetIdentityResolver(targets) {
175
+ const baseCounts = /* @__PURE__ */ new Map();
176
+ for (const target of targets) {
177
+ const base = createBaseTargetIdentity(target);
178
+ baseCounts.set(base, (baseCounts.get(base) ?? 0) + 1);
179
+ }
180
+ return (target) => {
181
+ const base = createBaseTargetIdentity(target);
182
+ if ((baseCounts.get(base) ?? 0) <= 1) return base;
183
+ if (target.range) return `${base}:${target.range.start}:${target.range.end}`;
184
+ return base;
185
+ };
186
+ }
187
+ function hashText(text) {
188
+ return createHash("sha256").update(text).digest("hex");
189
+ }
190
+ function normalizeCachePath(cwd, filePath) {
191
+ return relative(cwd, isAbsolute(filePath) ? resolve$1(filePath) : resolve$1(cwd, filePath)).split(sep).join("/");
192
+ }
193
+ function normalizeRunnerCacheConfig(cache, cwd) {
194
+ if (cache === false) return {
195
+ enabled: false,
196
+ location: resolveCacheLocation(cwd)
197
+ };
198
+ if (cache === true || cache === void 0) return {
199
+ enabled: true,
200
+ location: resolveCacheLocation(cwd)
201
+ };
202
+ return {
203
+ enabled: cache.enabled ?? true,
204
+ location: resolveCacheLocation(cwd, cache.location)
205
+ };
206
+ }
207
+ function resolveCacheLocation(cwd, location) {
208
+ if (!location) return join(cwd, DEFAULT_CACHE_FILE_NAME);
209
+ const resolved = isAbsolute(location) ? resolve$1(location) : resolve$1(cwd, location);
210
+ if (location.endsWith("/") || location.endsWith("\\")) return join(resolved, DEFAULT_CACHE_FILE_NAME);
211
+ try {
212
+ if (statSyncIsDirectory(resolved)) return join(resolved, DEFAULT_CACHE_FILE_NAME);
213
+ } catch {}
214
+ return resolved;
215
+ }
216
+ function stableHash(value) {
217
+ return hashText(stableStringify(value));
218
+ }
219
+ function createBaseTargetIdentity(target) {
220
+ if (target.kind === "file") return target.filePath ? `file:${target.filePath}` : "file";
221
+ if (target.name) return `${target.kind}:${target.name}`;
222
+ if (target.range) return `${target.kind}:${target.range.start}:${target.range.end}`;
223
+ return target.kind;
224
+ }
225
+ function createEmptyCacheFile() {
226
+ const now = (/* @__PURE__ */ new Date()).toISOString();
227
+ return {
228
+ createdAt: now,
229
+ entries: {},
230
+ files: {},
231
+ schemaVersion: CACHE_SCHEMA_VERSION,
232
+ updatedAt: now
233
+ };
234
+ }
235
+ function createNoopCacheStore(location) {
236
+ return {
237
+ get: () => void 0,
238
+ location,
239
+ markFile: () => {},
240
+ reconcile: async () => {},
241
+ set: () => {}
242
+ };
243
+ }
244
+ function isCacheFile(value) {
245
+ return is(CacheFileSchema, value);
246
+ }
247
+ function isRecord(value) {
248
+ return typeof value === "object" && value !== null && !Array.isArray(value);
249
+ }
250
+ async function readCacheFile(location) {
251
+ try {
252
+ const parsed = JSON.parse(await readFile(location, "utf8"));
253
+ if (isCacheFile(parsed)) return parsed;
254
+ } catch {}
255
+ return createEmptyCacheFile();
256
+ }
257
+ function stableStringify(value) {
258
+ if (Array.isArray(value)) return `[${value.map((item) => stableStringify(item)).join(",")}]`;
259
+ if (isRecord(value)) return `{${Object.keys(value).sort().map((key) => {
260
+ const property = value[key];
261
+ if (property === void 0) return;
262
+ return `${JSON.stringify(key)}:${stableStringify(property)}`;
263
+ }).filter((entry) => entry !== void 0).join(",")}}`;
264
+ return JSON.stringify(value);
265
+ }
266
+ function statSyncIsDirectory(path) {
267
+ return statSync(path).isDirectory();
268
+ }
269
+ //#endregion
270
+ //#region src/core/source/runtime.ts
271
+ function createSourceFile(path, text) {
272
+ return {
273
+ language: inferLanguage(path),
274
+ lines: text.split(/\r?\n/),
275
+ path,
276
+ text
277
+ };
278
+ }
279
+ function createSourceRuntime() {
280
+ return {
281
+ getText: (target) => target.text,
282
+ readFile: async (filePath) => createSourceFile(filePath, await readFile(filePath, "utf8")),
283
+ sliceLines,
284
+ sliceRange
285
+ };
286
+ }
287
+ function sliceLines(file, range) {
288
+ const startLine = clampLine(range.startLine, file.lines.length);
289
+ const endLine = clampLine(range.endLine, file.lines.length);
290
+ const orderedStartLine = Math.min(startLine, endLine);
291
+ const orderedEndLine = Math.max(startLine, endLine);
292
+ const text = file.lines.slice(orderedStartLine - 1, orderedEndLine).join("\n");
293
+ return {
294
+ filePath: file.path,
295
+ loc: {
296
+ end: {
297
+ column: file.lines[orderedEndLine - 1]?.length ?? 0,
298
+ line: orderedEndLine
299
+ },
300
+ start: {
301
+ column: 0,
302
+ line: orderedStartLine
303
+ }
304
+ },
305
+ text
306
+ };
307
+ }
308
+ function sliceRange(file, range) {
309
+ const start = clampOffset(range.start, file.text.length);
310
+ const end = clampOffset(range.end, file.text.length);
311
+ const orderedStart = Math.min(start, end);
312
+ const orderedEnd = Math.max(start, end);
313
+ return {
314
+ filePath: file.path,
315
+ loc: {
316
+ end: getPosition(file.text, orderedEnd),
317
+ start: getPosition(file.text, orderedStart)
318
+ },
319
+ text: file.text.slice(orderedStart, orderedEnd)
320
+ };
321
+ }
322
+ function clampLine(line, lineCount) {
323
+ if (lineCount === 0) return 1;
324
+ return clamp(Math.trunc(line), 1, lineCount);
325
+ }
326
+ function clampOffset(offset, textLength) {
327
+ return clamp(Math.trunc(offset), 0, textLength);
328
+ }
329
+ function getPosition(text, offset) {
330
+ let line = 1;
331
+ let column = 0;
332
+ let index = 0;
333
+ while (index < offset) {
334
+ const character = text[index];
335
+ if (character === "\r") {
336
+ if (text[index + 1] === "\n" && index + 1 < offset) index += 1;
337
+ line += 1;
338
+ column = 0;
339
+ } else if (character === "\n") {
340
+ line += 1;
341
+ column = 0;
342
+ } else column += 1;
343
+ index += 1;
344
+ }
345
+ return {
346
+ column,
347
+ line
348
+ };
349
+ }
350
+ function inferLanguage(path) {
351
+ switch (extname(path)) {
352
+ case ".cjs":
353
+ case ".js":
354
+ case ".jsx":
355
+ case ".mjs": return "javascript";
356
+ case ".cts":
357
+ case ".mts":
358
+ case ".ts":
359
+ case ".tsx": return "typescript";
360
+ default: return "unknown";
361
+ }
362
+ }
363
+ //#endregion
364
+ //#region src/core/source/js.ts
365
+ function extractJsSourceUnits(file) {
366
+ const result = parseSync(file.path, file.text, { sourceType: "module" });
367
+ const state = {
368
+ bindingNodes: /* @__PURE__ */ new Map(),
369
+ classes: [],
370
+ exportedNodes: /* @__PURE__ */ new Set(),
371
+ file,
372
+ functions: [],
373
+ inferredNames: /* @__PURE__ */ new Map(),
374
+ seenClasses: /* @__PURE__ */ new Set(),
375
+ seenFunctions: /* @__PURE__ */ new Set(),
376
+ visited: /* @__PURE__ */ new Set()
377
+ };
378
+ collectModuleBindings(result.program, state);
379
+ collectExportedBindings(result.program, state);
380
+ visit(result.program, state);
381
+ return {
382
+ classes: state.classes,
383
+ functions: state.functions
384
+ };
385
+ }
386
+ function addClassUnit(node, state) {
387
+ if (state.seenClasses.has(node)) return;
388
+ const unit = createClassUnit(node, state);
389
+ if (!unit) return;
390
+ state.seenClasses.add(node);
391
+ state.classes.push(unit);
392
+ }
393
+ function addFunctionUnit(node, state) {
394
+ if (state.seenFunctions.has(node)) return;
395
+ const unit = createFunctionUnit(node, state);
396
+ if (!unit) return;
397
+ state.seenFunctions.add(node);
398
+ state.functions.push(unit);
399
+ }
400
+ function asAstNode(value) {
401
+ return isAstNode(value) ? value : void 0;
402
+ }
403
+ function collectDeclarationBindings(node, state) {
404
+ const name = getNodeName(node);
405
+ if (name && (isClassNode(node) || isFunctionNode(node))) {
406
+ state.bindingNodes.set(name, node);
407
+ return;
408
+ }
409
+ if (node.type !== "VariableDeclaration" || !Array.isArray(node.declarations)) return;
410
+ for (const declarator of node.declarations) {
411
+ if (!isAstNode(declarator)) continue;
412
+ const id = asAstNode(declarator.id);
413
+ const initializer = asAstNode(declarator.init);
414
+ if (typeof id?.name === "string" && initializer && (isClassNode(initializer) || isFunctionNode(initializer))) state.bindingNodes.set(id.name, initializer);
415
+ }
416
+ }
417
+ function collectExportedBindings(node, state) {
418
+ if (!node) return;
419
+ if (Array.isArray(node)) {
420
+ for (const child of node) collectExportedBindings(child, state);
421
+ return;
422
+ }
423
+ if (node.type === "ExportNamedDeclaration" && !node.source && Array.isArray(node.specifiers)) for (const specifier of node.specifiers) {
424
+ if (!isAstNode(specifier) || specifier.type !== "ExportSpecifier") continue;
425
+ const local = asAstNode(specifier.local);
426
+ if (typeof local?.name === "string") markExportedBinding(local.name, state);
427
+ }
428
+ else if (node.type === "ExportDefaultDeclaration") {
429
+ const declaration = asAstNode(node.declaration);
430
+ if (typeof declaration?.name === "string") markExportedBinding(declaration.name, state);
431
+ }
432
+ for (const value of Object.values(node)) if (Array.isArray(value)) collectExportedBindings(value.filter(isAstNode), state);
433
+ else if (isAstNode(value)) collectExportedBindings(value, state);
434
+ }
435
+ function collectModuleBindings(program, state) {
436
+ if (!Array.isArray(program.body)) return;
437
+ for (const statement of program.body) {
438
+ if (!isAstNode(statement)) continue;
439
+ const declaration = isExportDeclaration(statement) ? asAstNode(statement.declaration) : statement;
440
+ if (declaration) collectDeclarationBindings(declaration, state);
441
+ }
442
+ }
443
+ function createClassUnit(node, state) {
444
+ const range = getRange(node);
445
+ if (!range) return;
446
+ const source = sliceRange(state.file, range);
447
+ return {
448
+ exported: isExportedUnit(node, state),
449
+ file: state.file,
450
+ kind: "class",
451
+ loc: source.loc,
452
+ name: getNodeName(node) ?? state.inferredNames.get(node),
453
+ range,
454
+ text: source.text
455
+ };
456
+ }
457
+ function createFunctionUnit(node, state) {
458
+ const range = getRange(node);
459
+ if (!range) return;
460
+ const source = sliceRange(state.file, range);
461
+ const functionNode = node.type === "MethodDefinition" ? asAstNode(node.value) : node;
462
+ return {
463
+ async: functionNode?.async === true,
464
+ exported: isExportedUnit(node, state),
465
+ file: state.file,
466
+ kind: "function",
467
+ loc: source.loc,
468
+ name: getNodeName(node) ?? getNodeName(functionNode) ?? state.inferredNames.get(node),
469
+ range,
470
+ text: source.text
471
+ };
472
+ }
473
+ function getNodeName(node) {
474
+ if (!node) return;
475
+ if (typeof node.name === "string") return node.name;
476
+ const id = asAstNode(node.id);
477
+ if (typeof id?.name === "string") return id.name;
478
+ const key = asAstNode(node.key);
479
+ if (typeof key?.name === "string") return key.name;
480
+ if (typeof key?.value === "string" || typeof key?.value === "number") return String(key.value);
481
+ }
482
+ function getRange(node) {
483
+ if (typeof node.start === "number" && typeof node.end === "number") return {
484
+ end: node.end,
485
+ start: node.start
486
+ };
487
+ if (Array.isArray(node.range) && node.range.length === 2) return {
488
+ end: node.range[1],
489
+ start: node.range[0]
490
+ };
491
+ }
492
+ function inferChildName(parent, key, child, state) {
493
+ if (state.inferredNames.has(child)) return;
494
+ if (parent.type === "VariableDeclarator" && key === "init") {
495
+ const id = asAstNode(parent.id);
496
+ if (typeof id?.name === "string") state.inferredNames.set(child, id.name);
497
+ }
498
+ if (parent.type === "Property" && key === "value") {
499
+ const name = getNodeName(parent);
500
+ if (name) state.inferredNames.set(child, name);
501
+ }
502
+ }
503
+ function isAstNode(value) {
504
+ return typeof value === "object" && value !== null && typeof value.type === "string";
505
+ }
506
+ function isClassNode(node) {
507
+ return node.type === "ClassDeclaration" || node.type === "ClassExpression";
508
+ }
509
+ function isExportDeclaration(node) {
510
+ return node.type === "ExportDefaultDeclaration" || node.type === "ExportNamedDeclaration";
511
+ }
512
+ function isExportedUnit(node, state) {
513
+ return state.exportedNodes.has(node);
514
+ }
515
+ function isFunctionNode(node) {
516
+ return node.type === "ArrowFunctionExpression" || node.type === "FunctionDeclaration" || node.type === "FunctionExpression";
517
+ }
518
+ function markExportedBinding(name, state) {
519
+ const bindingNode = state.bindingNodes.get(name);
520
+ if (bindingNode) state.exportedNodes.add(bindingNode);
521
+ }
522
+ function markExportedDeclaration(node, state) {
523
+ state.exportedNodes.add(node);
524
+ if (node.type !== "VariableDeclaration" || !Array.isArray(node.declarations)) return;
525
+ for (const declarator of node.declarations) {
526
+ if (!isAstNode(declarator)) continue;
527
+ const initializer = asAstNode(declarator.init);
528
+ if (initializer && (isClassNode(initializer) || isFunctionNode(initializer))) state.exportedNodes.add(initializer);
529
+ }
530
+ }
531
+ function visit(node, state) {
532
+ if (!node) return;
533
+ if (Array.isArray(node)) {
534
+ for (const child of node) visit(child, state);
535
+ return;
536
+ }
537
+ if (state.visited.has(node)) return;
538
+ state.visited.add(node);
539
+ if (node.type === "ExportDefaultDeclaration" || node.type === "ExportNamedDeclaration") {
540
+ if (node.declaration) {
541
+ markExportedDeclaration(node.declaration, state);
542
+ visit(node.declaration, state);
543
+ }
544
+ visitChildren(node, state, /* @__PURE__ */ new Set(["declaration"]));
545
+ return;
546
+ }
547
+ if (isClassNode(node)) addClassUnit(node, state);
548
+ else if (isFunctionNode(node) || node.type === "MethodDefinition") {
549
+ addFunctionUnit(node, state);
550
+ const methodValue = node.type === "MethodDefinition" ? asAstNode(node.value) : void 0;
551
+ if (methodValue) state.seenFunctions.add(methodValue);
552
+ }
553
+ visitChildren(node, state);
554
+ }
555
+ function visitChildren(node, state, skippedKeys = /* @__PURE__ */ new Set()) {
556
+ for (const [key, value] of Object.entries(node)) {
557
+ if (skippedKeys.has(key) || key === "type" || key === "start" || key === "end" || key === "range") continue;
558
+ if (Array.isArray(value)) {
559
+ for (const child of value) if (isAstNode(child)) {
560
+ inferChildName(node, key, child, state);
561
+ visit(child, state);
562
+ }
563
+ } else if (isAstNode(value)) {
564
+ inferChildName(node, key, value, state);
565
+ visit(value, state);
566
+ }
567
+ }
568
+ }
569
+ //#endregion
570
+ //#region src/core/run.ts
571
+ var AlintRuleExecutionError = class extends Error {
572
+ failure;
573
+ constructor(error, path) {
574
+ const message = errorMessageFrom(error) ?? String(error);
575
+ super(message, { cause: error });
576
+ this.name = "AlintRuleExecutionError";
577
+ this.failure = {
578
+ filePath: path.file.path,
579
+ message,
580
+ ruleId: path.rule.id,
581
+ target: {
582
+ kind: path.target.kind,
583
+ name: path.target.name
584
+ }
585
+ };
586
+ }
587
+ };
588
+ var AlintRunError = class extends Error {
589
+ failure;
590
+ result;
591
+ constructor(message, result, options = {}) {
592
+ super(message, { cause: options.cause });
593
+ this.name = "AlintRunError";
594
+ this.failure = options.failure;
595
+ this.result = result;
596
+ }
597
+ };
598
+ async function runAlint(options = {}) {
599
+ const cwd$1 = options.cwd ?? cwd();
600
+ const config = options.config ?? {};
601
+ const setupConfig = options.setupConfig ?? {
602
+ providers: [],
603
+ version: 1
604
+ };
605
+ const clock = options.runner?.clock ?? Date.now;
606
+ const diagnostics = [];
607
+ const usage = createUsageAccumulator();
608
+ const registry = buildRuleRegistry(config);
609
+ const src = createSourceRuntime();
610
+ const normalizedCacheConfig = normalizeRunnerCacheConfig(options.runner?.cache, cwd$1);
611
+ const cacheStore = await createCacheStore({
612
+ cwd: cwd$1,
613
+ enabled: normalizedCacheConfig.enabled,
614
+ location: normalizedCacheConfig.location
615
+ });
616
+ const cacheContext = {
617
+ configHash: stableHash({ rules: config.rules ?? {} }),
618
+ cwd: cwd$1,
619
+ enabled: normalizedCacheConfig.enabled,
620
+ fileEntryKeys: /* @__PURE__ */ new Map(),
621
+ modelHash: stableHash({
622
+ modelOverride: options.modelOverride,
623
+ setupConfig
624
+ }),
625
+ store: cacheStore
626
+ };
627
+ const files = await Promise.all((options.files ?? []).map(async (filePath) => {
628
+ const file = await src.readFile(resolve(cwd$1, filePath));
629
+ return {
630
+ file,
631
+ units: file.language === "javascript" || file.language === "typescript" ? extractJsSourceUnits(file) : void 0
632
+ };
633
+ }));
634
+ let planned = 0;
635
+ const counters = createRuleEndCounters();
636
+ const primaryError = createPrimaryErrorState();
637
+ let filePlans = [];
638
+ let runStartedAt;
639
+ let runError;
640
+ try {
641
+ filePlans = createPreparedFileExecutionPlans(registry.enabledRules.map((enabledRule) => {
642
+ const executionState = new AsyncLocalStorage();
643
+ const context = {
644
+ cwd: cwd$1,
645
+ id: enabledRule.id,
646
+ localId: enabledRule.localId,
647
+ logger: { debug: () => {} },
648
+ metering: { recordUsage: (record) => {
649
+ const usageRecord = usage.record({
650
+ ...record,
651
+ ruleId: record.ruleId ?? enabledRule.id
652
+ });
653
+ const state = executionState.getStore();
654
+ state?.cacheUsage?.push(usageRecord);
655
+ options.progress?.onUsage?.({
656
+ path: state?.progressPath,
657
+ record: usageRecord,
658
+ total: usage.toJSON()
659
+ });
660
+ } },
661
+ model: async (selector) => {
662
+ const request = options.modelOverride ?? (typeof selector === "string" ? selector : void 0);
663
+ const resolvedModel = resolveModel(setupConfig, {
664
+ request,
665
+ requirement: mergeModelRequirement(enabledRule.rule.model, typeof selector === "string" ? void 0 : selector),
666
+ ruleId: enabledRule.id
667
+ });
668
+ const state = executionState.getStore();
669
+ if (state) state.currentModel = toDiagnosticModel(resolvedModel, request);
670
+ return resolvedModel;
671
+ },
672
+ report: (descriptor) => {
673
+ const state = executionState.getStore();
674
+ const filePath = descriptor.filePath ?? state?.activeFilePath;
675
+ if (!filePath) throw new Error(`Diagnostic for rule "${enabledRule.id}" is missing filePath.`);
676
+ const diagnosticModel = state?.currentModel ? { ...state.currentModel } : void 0;
677
+ if (state) state.currentModel = void 0;
678
+ const diagnostic = {
679
+ evidence: descriptor.evidence,
680
+ filePath,
681
+ loc: descriptor.loc,
682
+ message: descriptor.message,
683
+ model: diagnosticModel,
684
+ ruleId: enabledRule.id,
685
+ severity: enabledRule.severity
686
+ };
687
+ diagnostics.push(diagnostic);
688
+ state?.cacheDiagnostics?.push(diagnostic);
689
+ options.progress?.onDiagnostic?.({
690
+ diagnostic,
691
+ diagnostics: [...diagnostics],
692
+ path: state?.progressPath
693
+ });
694
+ },
695
+ scope: enabledRule.scope,
696
+ src
697
+ };
698
+ return {
699
+ cacheable: enabledRule.rule.cache !== false,
700
+ enabledRule,
701
+ executionState,
702
+ handlers: enabledRule.rule.create(context),
703
+ ruleHash: stableHash({
704
+ cache: enabledRule.rule.cache ?? true,
705
+ create: String(enabledRule.rule.create),
706
+ id: enabledRule.id,
707
+ localId: enabledRule.localId,
708
+ model: enabledRule.rule.model,
709
+ scope: enabledRule.scope,
710
+ severity: enabledRule.severity
711
+ })
712
+ };
713
+ }), files, cwd$1);
714
+ planned = calculatePlannedExecutions(filePlans);
715
+ runStartedAt = clock();
716
+ options.progress?.onRunStart?.({
717
+ files: filePlans.map((filePlan) => toProgressFilePath(filePlan, files.length)),
718
+ filesTotal: files.length,
719
+ planned,
720
+ rulesTotal: registry.enabledRules.length,
721
+ startedAt: runStartedAt
722
+ });
723
+ await runConcurrently(filePlans.filter((filePlan) => filePlan.targets.length > 0), resolveFileConcurrency(options.runner?.fileConcurrency), (filePlan) => executeFilePlan(filePlan, files.length, clock, counters, diagnostics, usage, cacheContext, options));
724
+ } catch (error) {
725
+ primaryError.set();
726
+ runError = error;
727
+ } finally {
728
+ await reconcileCache(filePlans, cacheContext);
729
+ emitCleanupProgress(() => options.progress?.onRunEnd?.({
730
+ ...counters.snapshot(planned),
731
+ diagnostics,
732
+ endedAt: clock(),
733
+ startedAt: runStartedAt ?? clock(),
734
+ usage: usage.toJSON()
735
+ }), primaryError);
736
+ }
737
+ const result = {
738
+ diagnostics,
739
+ usage: usage.toJSON()
740
+ };
741
+ if (runError) throw createAlintRunError(runError, result);
742
+ return result;
743
+ }
744
+ function addTokenCount(base, value) {
745
+ return typeof value === "number" && Number.isFinite(value) ? base + value : base;
746
+ }
747
+ function calculateFilePlanExecutions(filePlan) {
748
+ return filePlan.targets.reduce((total, target) => total + target.executions.length, 0);
749
+ }
750
+ function calculatePlannedExecutions(filePlans) {
751
+ return filePlans.reduce((total, filePlan) => total + filePlan.planned, 0);
752
+ }
753
+ function collectExecutionTargets(ruleRuntimes, preparedFile) {
754
+ const targets = [];
755
+ const fileExecutions = ruleRuntimes.map((runtime) => {
756
+ if (!runtime.handlers.onFile) return void 0;
757
+ return {
758
+ run: () => runtime.handlers.onFile?.(preparedFile.file),
759
+ runtime
760
+ };
761
+ }).filter((execution) => execution !== void 0);
762
+ if (fileExecutions.length > 0) targets.push({
763
+ executions: fileExecutions,
764
+ identity: "",
765
+ kind: "file",
766
+ text: preparedFile.file.text
767
+ });
768
+ if (preparedFile.units) {
769
+ for (const classNode of preparedFile.units.classes) {
770
+ const executions = ruleRuntimes.map((runtime) => {
771
+ if (!runtime.handlers.onClass) return void 0;
772
+ return {
773
+ run: () => runtime.handlers.onClass?.(classNode),
774
+ runtime
775
+ };
776
+ }).filter((execution) => execution !== void 0);
777
+ if (executions.length > 0) targets.push({
778
+ executions,
779
+ identity: "",
780
+ kind: "class",
781
+ loc: classNode.loc,
782
+ name: classNode.name,
783
+ range: classNode.range,
784
+ text: classNode.text
785
+ });
786
+ }
787
+ for (const functionNode of preparedFile.units.functions) {
788
+ const executions = ruleRuntimes.map((runtime) => {
789
+ if (!runtime.handlers.onFunction) return void 0;
790
+ return {
791
+ run: () => runtime.handlers.onFunction?.(functionNode),
792
+ runtime
793
+ };
794
+ }).filter((execution) => execution !== void 0);
795
+ if (executions.length > 0) targets.push({
796
+ executions,
797
+ identity: "",
798
+ kind: "function",
799
+ loc: functionNode.loc,
800
+ name: functionNode.name,
801
+ range: functionNode.range,
802
+ text: functionNode.text
803
+ });
804
+ }
805
+ }
806
+ return targets;
807
+ }
808
+ function createAlintRunError(error, result) {
809
+ if (error instanceof AlintRunError) return error;
810
+ if (error instanceof AlintRuleExecutionError) return new AlintRunError(error.message, result, {
811
+ cause: errorCauseFrom(error),
812
+ failure: error.failure
813
+ });
814
+ return new AlintRunError(errorMessageFrom(error) ?? String(error), result, { cause: error });
815
+ }
816
+ function createExecutionCacheKey(runtime, target, path, cacheContext) {
817
+ if (!cacheContext.enabled || !runtime.cacheable) return;
818
+ return createCacheKey({
819
+ alintVersion: version,
820
+ configHash: cacheContext.configHash,
821
+ filePath: normalizeCachePath(cacheContext.cwd, path.file.path),
822
+ modelHash: cacheContext.modelHash,
823
+ ruleHash: runtime.ruleHash,
824
+ schemaVersion: 1,
825
+ targetHash: hashText(target.text),
826
+ targetIdentity: target.identity,
827
+ targetKind: target.kind
828
+ });
829
+ }
830
+ function createPreparedFileExecutionPlans(ruleRuntimes, files, cwd) {
831
+ return files.map((preparedFile, fileOffset) => {
832
+ const targets = collectExecutionTargets(ruleRuntimes, preparedFile);
833
+ const resolveTargetIdentity = createTargetIdentityResolver(targets.map((target) => toTargetIdentityInput(cwd, preparedFile.file.path, target)));
834
+ for (const target of targets) target.identity = resolveTargetIdentity(toTargetIdentityInput(cwd, preparedFile.file.path, target));
835
+ const filePlan = {
836
+ fileIndex: fileOffset + 1,
837
+ planned: 0,
838
+ preparedFile,
839
+ targets
840
+ };
841
+ filePlan.planned = calculateFilePlanExecutions(filePlan);
842
+ return filePlan;
843
+ });
844
+ }
845
+ function createPrimaryErrorState() {
846
+ let hasError = false;
847
+ return {
848
+ get hasError() {
849
+ return hasError;
850
+ },
851
+ set() {
852
+ hasError = true;
853
+ }
854
+ };
855
+ }
856
+ function createProgressPath(filePath, ruleId, entry) {
857
+ return {
858
+ file: {
859
+ index: entry.fileIndex,
860
+ path: filePath,
861
+ planned: entry.filePlanned,
862
+ total: entry.fileTotal
863
+ },
864
+ rule: {
865
+ id: ruleId,
866
+ index: entry.ruleIndex,
867
+ total: entry.ruleTotal
868
+ },
869
+ target: {
870
+ index: entry.targetIndex,
871
+ kind: entry.targetKind,
872
+ name: entry.targetName,
873
+ total: entry.targetTotal
874
+ }
875
+ };
876
+ }
877
+ function createRuleEndCounters() {
878
+ let cached = 0;
879
+ let completed = 0;
880
+ let errored = 0;
881
+ return {
882
+ cache() {
883
+ cached += 1;
884
+ },
885
+ complete() {
886
+ completed += 1;
887
+ },
888
+ error() {
889
+ errored += 1;
890
+ },
891
+ snapshot(planned) {
892
+ return {
893
+ cached,
894
+ completed,
895
+ errored,
896
+ planned
897
+ };
898
+ }
899
+ };
900
+ }
901
+ function createUsageAccumulator() {
902
+ const records = [];
903
+ let inputTokens = 0;
904
+ let outputTokens = 0;
905
+ let totalTokens = 0;
906
+ return {
907
+ record(record) {
908
+ records.push(record);
909
+ inputTokens = addTokenCount(inputTokens, record.inputTokens);
910
+ outputTokens = addTokenCount(outputTokens, record.outputTokens);
911
+ totalTokens = addTokenCount(totalTokens, record.totalTokens);
912
+ return record;
913
+ },
914
+ toJSON() {
915
+ return {
916
+ inputTokens,
917
+ outputTokens,
918
+ records,
919
+ totalTokens
920
+ };
921
+ }
922
+ };
923
+ }
924
+ function emitCleanupProgress(callback, primaryError) {
925
+ try {
926
+ callback();
927
+ } catch (error) {
928
+ if (!primaryError.hasError) throw error;
929
+ }
930
+ }
931
+ async function executeFilePlan(filePlan, filesTotal, clock, counters, diagnostics, usage, cacheContext, options) {
932
+ const preparedFile = filePlan.preparedFile;
933
+ const fileStartedAt = clock();
934
+ const fileProgress = {
935
+ index: filePlan.fileIndex,
936
+ path: preparedFile.file.path,
937
+ planned: filePlan.planned,
938
+ total: filesTotal
939
+ };
940
+ options.progress?.onFileStart?.({
941
+ file: fileProgress,
942
+ startedAt: fileStartedAt
943
+ });
944
+ const fileError = createPrimaryErrorState();
945
+ try {
946
+ for (const [targetOffset, target] of filePlan.targets.entries()) {
947
+ const targetPath = createProgressPath(preparedFile.file.path, target.executions[0]?.runtime.enabledRule.id ?? "", {
948
+ fileIndex: filePlan.fileIndex,
949
+ filePlanned: filePlan.planned,
950
+ fileTotal: filesTotal,
951
+ ruleIndex: 1,
952
+ ruleTotal: target.executions.length,
953
+ targetIndex: targetOffset + 1,
954
+ targetKind: target.kind,
955
+ targetName: target.name,
956
+ targetTotal: filePlan.targets.length
957
+ });
958
+ const targetError = createPrimaryErrorState();
959
+ const targetStartedAt = clock();
960
+ options.progress?.onTargetStart?.({
961
+ path: targetPath,
962
+ startedAt: targetStartedAt
963
+ });
964
+ try {
965
+ for (const [executionOffset, execution] of target.executions.entries()) await executeProgressTarget(execution, createProgressPath(preparedFile.file.path, execution.runtime.enabledRule.id, {
966
+ fileIndex: filePlan.fileIndex,
967
+ filePlanned: filePlan.planned,
968
+ fileTotal: filesTotal,
969
+ ruleIndex: executionOffset + 1,
970
+ ruleTotal: target.executions.length,
971
+ targetIndex: targetOffset + 1,
972
+ targetKind: target.kind,
973
+ targetName: target.name,
974
+ targetTotal: filePlan.targets.length
975
+ }), target, clock, counters, diagnostics, usage, cacheContext, options);
976
+ } catch (error) {
977
+ targetError.set();
978
+ throw error;
979
+ } finally {
980
+ emitCleanupProgress(() => options.progress?.onTargetEnd?.({
981
+ endedAt: clock(),
982
+ path: targetPath,
983
+ startedAt: targetStartedAt
984
+ }), targetError);
985
+ }
986
+ }
987
+ } catch (error) {
988
+ fileError.set();
989
+ throw error;
990
+ } finally {
991
+ emitCleanupProgress(() => options.progress?.onFileEnd?.({
992
+ endedAt: clock(),
993
+ file: fileProgress,
994
+ startedAt: fileStartedAt
995
+ }), fileError);
996
+ }
997
+ }
998
+ async function executeProgressTarget(execution, path, target, clock, counters, diagnostics, usage, cacheContext, options) {
999
+ const startedAt = clock();
1000
+ const cacheKey = createExecutionCacheKey(execution.runtime, target, path, cacheContext);
1001
+ const cachedEntry = cacheKey && cacheContext.enabled && execution.runtime.cacheable ? cacheContext.store.get(cacheKey) : void 0;
1002
+ options.progress?.onRuleStart?.({
1003
+ path,
1004
+ startedAt
1005
+ });
1006
+ if (cacheKey && cachedEntry) {
1007
+ rememberFileCacheEntry(cacheContext, path.file.path, cacheKey);
1008
+ try {
1009
+ replayCachedEntry(cachedEntry, path, diagnostics, usage, options);
1010
+ } catch (error) {
1011
+ counters.error();
1012
+ try {
1013
+ options.progress?.onRuleEnd?.({
1014
+ cache: "hit",
1015
+ endedAt: clock(),
1016
+ path,
1017
+ startedAt,
1018
+ state: "errored"
1019
+ });
1020
+ } catch {}
1021
+ throw new AlintRuleExecutionError(error, path);
1022
+ }
1023
+ counters.cache();
1024
+ options.progress?.onRuleEnd?.({
1025
+ cache: "hit",
1026
+ endedAt: clock(),
1027
+ path,
1028
+ startedAt,
1029
+ state: "completed"
1030
+ });
1031
+ return;
1032
+ }
1033
+ let handlerError;
1034
+ let handlerSucceeded = false;
1035
+ const cacheDiagnostics = cacheKey ? [] : void 0;
1036
+ const cacheUsage = cacheKey ? [] : void 0;
1037
+ try {
1038
+ await execution.runtime.executionState.run({
1039
+ activeFilePath: path.file.path,
1040
+ cacheDiagnostics,
1041
+ cacheUsage,
1042
+ progressPath: path
1043
+ }, execution.run);
1044
+ handlerSucceeded = true;
1045
+ } catch (error) {
1046
+ handlerError = error;
1047
+ }
1048
+ if (handlerSucceeded) {
1049
+ if (cacheKey && cacheContext.enabled && execution.runtime.cacheable) {
1050
+ cacheContext.store.set(cacheKey, {
1051
+ diagnostics: cacheDiagnostics ?? [],
1052
+ filePath: normalizeCachePath(cacheContext.cwd, path.file.path),
1053
+ fingerprint: {
1054
+ alintVersion: version,
1055
+ configHash: cacheContext.configHash,
1056
+ modelHash: cacheContext.modelHash,
1057
+ ruleHash: execution.runtime.ruleHash
1058
+ },
1059
+ target: {
1060
+ hash: hashText(target.text),
1061
+ identity: target.identity,
1062
+ kind: target.kind,
1063
+ loc: target.loc,
1064
+ name: target.name,
1065
+ range: target.range
1066
+ },
1067
+ usage: cacheUsage ?? []
1068
+ });
1069
+ rememberFileCacheEntry(cacheContext, path.file.path, cacheKey);
1070
+ }
1071
+ counters.complete();
1072
+ options.progress?.onRuleEnd?.({
1073
+ cache: "miss",
1074
+ endedAt: clock(),
1075
+ path,
1076
+ startedAt,
1077
+ state: "completed"
1078
+ });
1079
+ return;
1080
+ }
1081
+ counters.error();
1082
+ try {
1083
+ options.progress?.onRuleEnd?.({
1084
+ cache: "miss",
1085
+ endedAt: clock(),
1086
+ path,
1087
+ startedAt,
1088
+ state: "errored"
1089
+ });
1090
+ } catch {}
1091
+ throw new AlintRuleExecutionError(handlerError, path);
1092
+ }
1093
+ function mergeCapabilities(base, extra) {
1094
+ if (!base && !extra) return;
1095
+ return [.../* @__PURE__ */ new Set([...base ?? [], ...extra ?? []])];
1096
+ }
1097
+ function mergeMinContextWindow(base, extra) {
1098
+ if (base === void 0) return extra;
1099
+ if (extra === void 0) return base;
1100
+ return Math.max(base, extra);
1101
+ }
1102
+ function mergeModelRequirement(base, extra) {
1103
+ if (!base && !extra) return;
1104
+ const capabilities = mergeCapabilities(base?.capabilities, extra?.capabilities);
1105
+ const params = {
1106
+ ...base?.params ?? {},
1107
+ ...extra?.params ?? {}
1108
+ };
1109
+ return {
1110
+ capabilities,
1111
+ minContextWindow: mergeMinContextWindow(base?.minContextWindow, extra?.minContextWindow),
1112
+ params: Object.keys(params).length > 0 ? params : void 0,
1113
+ size: extra?.size ?? base?.size
1114
+ };
1115
+ }
1116
+ async function reconcileCache(filePlans, cacheContext) {
1117
+ try {
1118
+ for (const filePlan of filePlans) {
1119
+ const file = filePlan.preparedFile.file;
1120
+ const normalizedPath = normalizeCachePath(cacheContext.cwd, file.path);
1121
+ const entries = [...cacheContext.fileEntryKeys.get(normalizedPath) ?? []];
1122
+ cacheContext.store.markFile(file.path, hashText(file.text), entries);
1123
+ }
1124
+ await cacheContext.store.reconcile();
1125
+ } catch {}
1126
+ }
1127
+ function rememberFileCacheEntry(cacheContext, filePath, cacheKey) {
1128
+ const normalizedPath = normalizeCachePath(cacheContext.cwd, filePath);
1129
+ const entries = cacheContext.fileEntryKeys.get(normalizedPath) ?? /* @__PURE__ */ new Set();
1130
+ entries.add(cacheKey);
1131
+ cacheContext.fileEntryKeys.set(normalizedPath, entries);
1132
+ }
1133
+ function replayCachedEntry(entry, path, diagnostics, usage, options) {
1134
+ for (const cachedDiagnostic of entry.diagnostics) {
1135
+ const diagnostic = { ...cachedDiagnostic };
1136
+ diagnostics.push(diagnostic);
1137
+ options.progress?.onDiagnostic?.({
1138
+ diagnostic,
1139
+ diagnostics: [...diagnostics],
1140
+ path
1141
+ });
1142
+ }
1143
+ for (const cachedUsage of entry.usage) {
1144
+ const record = usage.record({ ...cachedUsage });
1145
+ options.progress?.onUsage?.({
1146
+ path,
1147
+ record,
1148
+ total: usage.toJSON()
1149
+ });
1150
+ }
1151
+ }
1152
+ function resolveFileConcurrency(fileConcurrency) {
1153
+ return fileConcurrency ?? 1;
1154
+ }
1155
+ async function runConcurrently(items, concurrency, runItem) {
1156
+ let firstError;
1157
+ let nextIndex = 0;
1158
+ const workerCount = Math.min(Math.max(concurrency, 1), items.length);
1159
+ async function runWorker() {
1160
+ while (firstError === void 0) {
1161
+ const currentIndex = nextIndex;
1162
+ nextIndex += 1;
1163
+ if (currentIndex >= items.length) return;
1164
+ try {
1165
+ await runItem(items[currentIndex]);
1166
+ } catch (error) {
1167
+ firstError ??= error;
1168
+ return;
1169
+ }
1170
+ }
1171
+ }
1172
+ await Promise.all(Array.from({ length: workerCount }, () => runWorker()));
1173
+ if (firstError !== void 0) throw firstError;
1174
+ }
1175
+ function toDiagnosticModel(model, request) {
1176
+ return {
1177
+ providerId: model.provider.id,
1178
+ requested: request,
1179
+ resolvedId: model.id
1180
+ };
1181
+ }
1182
+ function toProgressFilePath(filePlan, filesTotal) {
1183
+ return {
1184
+ index: filePlan.fileIndex,
1185
+ path: filePlan.preparedFile.file.path,
1186
+ planned: filePlan.planned,
1187
+ total: filesTotal
1188
+ };
1189
+ }
1190
+ function toTargetIdentityInput(cwd, filePath, target) {
1191
+ return {
1192
+ filePath: target.kind === "file" ? normalizeCachePath(cwd, filePath) : void 0,
1193
+ kind: target.kind,
1194
+ name: target.name,
1195
+ range: target.range
1196
+ };
1197
+ }
1198
+ //#endregion
1199
+ //#region src/dsl/define.ts
1200
+ function defineConfig(config) {
1201
+ return config;
1202
+ }
1203
+ function definePlugin(plugin) {
1204
+ return plugin;
1205
+ }
1206
+ function defineRule(rule) {
1207
+ return rule;
1208
+ }
1209
+ //#endregion
1210
+ export { AlintRunError, buildRuleRegistry, createSourceFile, createSourceRuntime, defineConfig, definePlugin, defineRule, extractJsSourceUnits, resolveModel, runAlint, sliceLines, sliceRange };