@alint-js/core 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/dist/agent.d.mts +1 -1
- package/dist/{define-CNfm3wZW.d.mts → define-DO36VJ4W.d.mts} +1 -1
- package/dist/dsl.d.mts +3 -3
- package/dist/index.d.mts +20 -3
- package/dist/index.mjs +413 -98
- package/dist/languages-js.mjs +1 -1
- package/dist/structured-output.d.mts +1 -1
- package/dist/{types-BK5CADYK.d.mts → types-Cm5Z7Fll.d.mts} +11 -1
- package/package.json +20 -2
- package/dist/{extract-CLXRRiJW.mjs → extract-DhLFgRej.mjs} +1 -1
package/README.md
CHANGED
|
@@ -182,3 +182,10 @@ source files retained by plugin code after `ctx.src.readFile()` returns.
|
|
|
182
182
|
|
|
183
183
|
A valid cache written by the same `alint` version is still read from one monolithic JSON document.
|
|
184
184
|
An extremely large cache may therefore exhaust available memory.
|
|
185
|
+
|
|
186
|
+
After each cacheable rule job completes, `alint` writes a cache checkpoint before releasing its
|
|
187
|
+
scheduler slot. Each checkpoint atomically replaces the monolithic cache file. An interrupted run
|
|
188
|
+
can therefore reuse every result that had already become durable, but large caches can cause
|
|
189
|
+
substantial disk writes during runs with many cache misses. Cache hits, skipped jobs, failed jobs,
|
|
190
|
+
and rules that opt out of caching do not add checkpoint writes. A checkpoint or final cache write
|
|
191
|
+
error is fatal and causes the run to fail.
|
package/dist/agent.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as
|
|
1
|
+
import { F as AgentAdapter, I as AgentRequest, L as AgentResult, R as AgentTool, x as RuleContext, z as AgentUsage } from "./types-Cm5Z7Fll.mjs";
|
|
2
2
|
//#region src/agent/retry.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Declares that the complete adapter invocation can be safely replayed.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { D as RuleOptionsSchema, S as RuleDefinition, T as RuleOptionsInput, b as RuleConfigEntry, i as AlintConfigItem, m as PluginDefinition, t as AlintConfig } from "./types-Cm5Z7Fll.mjs";
|
|
2
2
|
//#region src/dsl/define.d.ts
|
|
3
3
|
type AnyRuleDefinition = RuleDefinition<any>;
|
|
4
4
|
type ConfigItemWithPluginRules<Plugins, Rules> = Omit<AlintConfigItem, 'plugins' | 'rules'> & {
|
package/dist/dsl.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import { n as definePlugin, r as defineRule, t as defineConfig } from "./define-
|
|
3
|
-
export { type AlintConfig, type AlintConfigExtends, type AlintConfigInput, type AlintConfigItem, type AlintLinterOptions, type Awaitable, type DiagnosticDescriptor, type DiagnosticLocation, type DirectoryTarget, type EnabledRule, type IgnoreConfig, type LanguageDefinition, type PluginDefinition, type ProcessorDefinition, type ProjectFileEntry, type ProjectTarget, type ProjectTargetEntry, type RuleCacheConfig, type RuleConfigEntry, type RuleContext, type RuleDefinition, type RuleHandlers, type RuleInferenceUsageRecord, type RuleOptionsInput, type RuleOptionsOutput, type RuleOptionsSchema, type RuleRegistry, type RuleSeverity, type RuleSpecializedHandlers, type RuleWithHandler, type Target, defineConfig, definePlugin, defineRule };
|
|
1
|
+
import { A as RuleSpecializedHandlers, C as RuleHandlers, D as RuleOptionsSchema, E as RuleOptionsOutput, M as StopGateConfig, N as StopGateTarget, O as RuleRegistry, P as Target, S as RuleDefinition, T as RuleOptionsInput, _ as ProjectTarget, a as AlintLinterOptions, b as RuleConfigEntry, c as DiagnosticLocation, d as IgnoreConfig, f as IntegrationsConfig, g as ProjectFileEntry, h as ProcessorDefinition, i as AlintConfigItem, j as RuleWithHandler, k as RuleSeverity, l as DirectoryTarget, m as PluginDefinition, n as AlintConfigExtends, o as Awaitable, p as LanguageDefinition, r as AlintConfigInput, s as DiagnosticDescriptor, t as AlintConfig, u as EnabledRule, v as ProjectTargetEntry, w as RuleInferenceUsageRecord, x as RuleContext, y as RuleCacheConfig } from "./types-Cm5Z7Fll.mjs";
|
|
2
|
+
import { n as definePlugin, r as defineRule, t as defineConfig } from "./define-DO36VJ4W.mjs";
|
|
3
|
+
export { type AlintConfig, type AlintConfigExtends, type AlintConfigInput, type AlintConfigItem, type AlintLinterOptions, type Awaitable, type DiagnosticDescriptor, type DiagnosticLocation, type DirectoryTarget, type EnabledRule, type IgnoreConfig, type IntegrationsConfig, type LanguageDefinition, type PluginDefinition, type ProcessorDefinition, type ProjectFileEntry, type ProjectTarget, type ProjectTargetEntry, type RuleCacheConfig, type RuleConfigEntry, type RuleContext, type RuleDefinition, type RuleHandlers, type RuleInferenceUsageRecord, type RuleOptionsInput, type RuleOptionsOutput, type RuleOptionsSchema, type RuleRegistry, type RuleSeverity, type RuleSpecializedHandlers, type RuleWithHandler, type StopGateConfig, type StopGateTarget, type Target, defineConfig, definePlugin, defineRule };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as RuleSpecializedHandlers, B as ModelRequirement, C as RuleHandlers, G as ProviderDefinition, H as ResolvedModel, J as RunnerStatsConfig, K as ProviderType, M as StopGateConfig, N as StopGateTarget, O as RuleRegistry, P as Target, S as RuleDefinition, U as ResolvedProvider, V as ResolveModelOptions, W as ModelSize, X as SetupModelDefinition, Y as SetupConfig, _ as ProjectTarget, a as AlintLinterOptions, b as RuleConfigEntry, c as DiagnosticLocation, d as IgnoreConfig, f as IntegrationsConfig, g as ProjectFileEntry, h as ProcessorDefinition, i as AlintConfigItem, j as RuleWithHandler, k as RuleSeverity, l as DirectoryTarget, m as PluginDefinition, n as AlintConfigExtends, o as Awaitable, p as LanguageDefinition, q as RunnerConfig, r as AlintConfigInput, s as DiagnosticDescriptor, t as AlintConfig, u as EnabledRule, v as ProjectTargetEntry, w as RuleInferenceUsageRecord, x as RuleContext, y as RuleCacheConfig } from "./types-Cm5Z7Fll.mjs";
|
|
2
2
|
import { C as SourceTarget, D as SourceTargetOrigin, E as SourceTargetOfKind, O as SourceText, S as SourceRuntime, T as SourceTargetMetadata, _ as SourceLocation, a as FunctionInfo, b as SourcePosition, c as LineRange, d as ProcessedSource, f as ProcessedSourceOrigin, g as SourceFile, h as SourceExtractOptions, i as FileTarget, l as PlannedSourceTarget, m as ProcessorPostprocessContext, n as CallSite, o as FunctionTarget, p as ProcessorContext, r as ClassTarget, s as LanguageContext, t as BaseSourceFile, u as PlannedSourceTargetOfKind, v as SourceMetadataObject, w as SourceTargetKind, x as SourceRange, y as SourceMetadataValue } from "./types-DM4idR4w.mjs";
|
|
3
|
-
import { n as definePlugin, r as defineRule, t as defineConfig } from "./define-
|
|
3
|
+
import { n as definePlugin, r as defineRule, t as defineConfig } from "./define-DO36VJ4W.mjs";
|
|
4
4
|
//#region src/config/config-array.d.ts
|
|
5
5
|
interface EffectiveAlintConfig {
|
|
6
6
|
agent?: AlintConfigItem['agent'];
|
|
@@ -32,6 +32,19 @@ declare function resolveConfigForDirectory(directoryPath: string, input: AlintCo
|
|
|
32
32
|
declare function resolveConfigForFile(filePath: string, input: AlintConfig, options: ResolveConfigOptions): ResolveConfigResult;
|
|
33
33
|
declare function resolveConfigForProject(projectPath: string, input: AlintConfig, options: ResolveConfigOptions): ResolveConfigResult;
|
|
34
34
|
//#endregion
|
|
35
|
+
//#region src/config/stop-gate.d.ts
|
|
36
|
+
interface ResolvedStopGateConfig {
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
target: StopGateTarget;
|
|
39
|
+
timeoutMs: number;
|
|
40
|
+
}
|
|
41
|
+
declare const maximumStopGateTimeoutMs: number;
|
|
42
|
+
declare const defaultStopGateConfig: Readonly<ResolvedStopGateConfig>;
|
|
43
|
+
declare function resolveStopGateConfig(config: AlintConfig, cwd: string): ResolvedStopGateConfig;
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/core/hash.d.ts
|
|
46
|
+
declare function stableHash(value: unknown): string;
|
|
47
|
+
//#endregion
|
|
35
48
|
//#region src/core/languages/registry.d.ts
|
|
36
49
|
interface LanguageRegistry {
|
|
37
50
|
byExtension: Map<string, string>;
|
|
@@ -270,6 +283,10 @@ declare class AlintAbortError extends AlintRunCancelledError {
|
|
|
270
283
|
cause?: unknown;
|
|
271
284
|
});
|
|
272
285
|
}
|
|
286
|
+
declare class AlintCachePersistenceError extends Error {
|
|
287
|
+
readonly result: RunResult;
|
|
288
|
+
constructor(result: RunResult, cause: unknown);
|
|
289
|
+
}
|
|
273
290
|
declare class AlintRunError extends Error {
|
|
274
291
|
readonly failures: AlintRunFailure[];
|
|
275
292
|
readonly result: RunResult;
|
|
@@ -380,4 +397,4 @@ declare function benchmarkModels(config: SetupConfig, options?: BenchmarkModelsO
|
|
|
380
397
|
//#region src/models/resolve.d.ts
|
|
381
398
|
declare function resolveModel(registry: SetupConfig, options?: ResolveModelOptions): ResolvedModel;
|
|
382
399
|
//#endregion
|
|
383
|
-
export { AlintAbortError, type AlintConfig, type AlintConfigExtends, type AlintConfigInput, type AlintConfigItem, type AlintFailureCause, type AlintFileFailure, type AlintLinterOptions, type AlintRuleFailure, AlintRunCancelledError, AlintRunError, type AlintRunFailure, type Awaitable, type BaseSourceFile, type BenchmarkModelsOptions, type CallSite, type ClassTarget, type Diagnostic, type DiagnosticDescriptor, type DiagnosticLocation, type DiagnosticProgressPayload, type DirectoryTarget, type EffectiveAlintConfig, type EnabledRule, type ExecutionCounts, type ExecutionProgressPayload, type FileReadyPayload, type FileTarget, type FunctionInfo, type FunctionTarget, type IgnoreConfig, type InferenceUsageRecord, type JobEndPayload, type JobQueuedPayload, type JobRetryPayload, type JobStartPayload, type LanguageContext, type LanguageDefinition, type LanguageRegistry, type LineRange, type ModelBenchmarkActive, type ModelBenchmarkMeasurement, type ModelBenchmarkProgress, type ModelBenchmarkRequest, type ModelBenchmarkResult, type ModelRequirement, type ModelSize, type PlannedSourceTarget, type PlannedSourceTargetOfKind, type PluginDefinition, type PrepareEndPayload, type PrepareStartPayload, type ProcessedSource, type ProcessedSourceOrigin, type ProcessorContext, type ProcessorDefinition, type ProcessorPostprocessContext, type ProgressJobRef, type ProgressReporter, type ProgressSnapshot, type ProgressTargetKind, type ProjectFileEntry, type ProjectTarget, type ProjectTargetEntry, type ProviderDefinition, type ProviderType, type ResolveConfigResult, type ResolveLanguageOptions, type ResolveModelOptions, type ResolvedModel, type ResolvedProvider, type RuleCacheConfig, type RuleConfigEntry, type RuleContext, type RuleDefinition, type RuleHandlers, type RuleInferenceUsageRecord, type RuleRegistry, type RuleSeverity, type RuleSpecializedHandlers, type RuleWithHandler, type RunEndPayload, type RunExecution, type RunOptions, type RunResult, type RunUsage, type RunUsageTotals, type RunnerConfig, type RunnerStatsConfig, type SetupConfig, type SetupModelDefinition, type SourceExtractOptions, type SourceFile, type SourceLocation, type SourceMetadataObject, type SourceMetadataValue, type SourcePosition, type SourceRange, type SourceRuntime, type SourceRuntimeOptions, type SourceTarget, type SourceTargetKind, type SourceTargetMetadata, type SourceTargetOfKind, type SourceTargetOrigin, type SourceText, type Target, type UsageProgressPayload, benchmarkModels, buildRuleRegistry, createBuiltInLanguageRegistry, createSourceExtractor, createSourceFile, createSourceRuntime, defineConfig, definePlugin, defineRule, hasDiscoveryFilePatterns, matchesDiscoveryFile, normalizeConfig, registerLanguage, resolveConfigForDirectory, resolveConfigForFile, resolveConfigForProject, resolveLanguage, resolveModel, runAlint, sliceLines, sliceRange, targetIdentity, withLanguage, withStableIdentities };
|
|
400
|
+
export { AlintAbortError, AlintCachePersistenceError, type AlintConfig, type AlintConfigExtends, type AlintConfigInput, type AlintConfigItem, type AlintFailureCause, type AlintFileFailure, type AlintLinterOptions, type AlintRuleFailure, AlintRunCancelledError, AlintRunError, type AlintRunFailure, type Awaitable, type BaseSourceFile, type BenchmarkModelsOptions, type CallSite, type ClassTarget, type Diagnostic, type DiagnosticDescriptor, type DiagnosticLocation, type DiagnosticProgressPayload, type DirectoryTarget, type EffectiveAlintConfig, type EnabledRule, type ExecutionCounts, type ExecutionProgressPayload, type FileReadyPayload, type FileTarget, type FunctionInfo, type FunctionTarget, type IgnoreConfig, type InferenceUsageRecord, type IntegrationsConfig, type JobEndPayload, type JobQueuedPayload, type JobRetryPayload, type JobStartPayload, type LanguageContext, type LanguageDefinition, type LanguageRegistry, type LineRange, type ModelBenchmarkActive, type ModelBenchmarkMeasurement, type ModelBenchmarkProgress, type ModelBenchmarkRequest, type ModelBenchmarkResult, type ModelRequirement, type ModelSize, type PlannedSourceTarget, type PlannedSourceTargetOfKind, type PluginDefinition, type PrepareEndPayload, type PrepareStartPayload, type ProcessedSource, type ProcessedSourceOrigin, type ProcessorContext, type ProcessorDefinition, type ProcessorPostprocessContext, type ProgressJobRef, type ProgressReporter, type ProgressSnapshot, type ProgressTargetKind, type ProjectFileEntry, type ProjectTarget, type ProjectTargetEntry, type ProviderDefinition, type ProviderType, type ResolveConfigResult, type ResolveLanguageOptions, type ResolveModelOptions, type ResolvedModel, type ResolvedProvider, type ResolvedStopGateConfig, type RuleCacheConfig, type RuleConfigEntry, type RuleContext, type RuleDefinition, type RuleHandlers, type RuleInferenceUsageRecord, type RuleRegistry, type RuleSeverity, type RuleSpecializedHandlers, type RuleWithHandler, type RunEndPayload, type RunExecution, type RunOptions, type RunResult, type RunUsage, type RunUsageTotals, type RunnerConfig, type RunnerStatsConfig, type SetupConfig, type SetupModelDefinition, type SourceExtractOptions, type SourceFile, type SourceLocation, type SourceMetadataObject, type SourceMetadataValue, type SourcePosition, type SourceRange, type SourceRuntime, type SourceRuntimeOptions, type SourceTarget, type SourceTargetKind, type SourceTargetMetadata, type SourceTargetOfKind, type SourceTargetOrigin, type SourceText, type StopGateConfig, type StopGateTarget, type Target, type UsageProgressPayload, benchmarkModels, buildRuleRegistry, createBuiltInLanguageRegistry, createSourceExtractor, createSourceFile, createSourceRuntime, defaultStopGateConfig, defineConfig, definePlugin, defineRule, hasDiscoveryFilePatterns, matchesDiscoveryFile, maximumStopGateTimeoutMs, normalizeConfig, registerLanguage, resolveConfigForDirectory, resolveConfigForFile, resolveConfigForProject, resolveLanguage, resolveModel, resolveStopGateConfig, runAlint, sliceLines, sliceRange, stableHash, targetIdentity, withLanguage, withStableIdentities };
|
package/dist/index.mjs
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { o as withAgentRetry, t as combineAbortSignals } from "./agent-B6GG68NX.mjs";
|
|
2
2
|
import { n as definePlugin, r as defineRule, t as defineConfig } from "./define-Da342Fcx.mjs";
|
|
3
|
-
import { a as createSourceFile, c as sliceRange, d as stableHash, i as SourceChangedError, l as withLanguage, n as targetIdentity, o as createSourceRuntime, r as withStableIdentities, s as sliceLines, t as extractJsSourceTargets, u as createStableHasher } from "./extract-
|
|
3
|
+
import { a as createSourceFile, c as sliceRange, d as stableHash, i as SourceChangedError, l as withLanguage, n as targetIdentity, o as createSourceRuntime, r as withStableIdentities, s as sliceLines, t as extractJsSourceTargets, u as createStableHasher } from "./extract-DhLFgRej.mjs";
|
|
4
4
|
import { minimatch } from "minimatch";
|
|
5
5
|
import { basename, extname, isAbsolute, relative, resolve } from "pathe";
|
|
6
|
-
import {
|
|
7
|
-
import { basename as basename$1, dirname, extname as extname$1, isAbsolute as isAbsolute$1, join, relative as relative$1, resolve as resolve$1, sep } from "node:path";
|
|
6
|
+
import { array, boolean, check, custom, finite, getDotPath, integer, literal, looseObject, maxValue, minValue, number, object, optional, parse, picklist, pipe, record, safeParse, string, union, unknown } from "valibot";
|
|
8
7
|
import { randomUUID } from "node:crypto";
|
|
8
|
+
import { access, appendFile, mkdir, open, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
9
|
+
import { basename as basename$1, dirname, extname as extname$1, isAbsolute as isAbsolute$1, join, relative as relative$1, resolve as resolve$1, sep } from "node:path";
|
|
9
10
|
import process, { cwd } from "node:process";
|
|
10
|
-
import { array, boolean, check, custom, finite, getDotPath, literal, number, object, optional, parse, pipe, record, safeParse, string, union, unknown } from "valibot";
|
|
11
11
|
import { Buffer } from "node:buffer";
|
|
12
12
|
import { statSync } from "node:fs";
|
|
13
|
+
import lockfile from "proper-lockfile";
|
|
14
|
+
import { isNodeErrorCode } from "@alint-js/utils/node";
|
|
13
15
|
import { errorMessageFrom } from "@moeru/std/error";
|
|
14
16
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
15
17
|
import { streamText } from "@xsai/stream-text";
|
|
@@ -236,6 +238,37 @@ function withExpansionMetadata(item, inheritedMatcherScopes, effectiveBasePath)
|
|
|
236
238
|
return item;
|
|
237
239
|
}
|
|
238
240
|
//#endregion
|
|
241
|
+
//#region src/config/stop-gate.ts
|
|
242
|
+
const maximumStopGateTimeoutMs = 861e5;
|
|
243
|
+
const defaultStopGateConfig = {
|
|
244
|
+
enabled: false,
|
|
245
|
+
target: "dirty-files",
|
|
246
|
+
timeoutMs: 9e5
|
|
247
|
+
};
|
|
248
|
+
const stopGateConfigSchema = looseObject({
|
|
249
|
+
enabled: optional(boolean("integrations.stopGate.enabled must be a boolean.")),
|
|
250
|
+
target: optional(picklist(["all", "dirty-files"], "integrations.stopGate.target must be \"all\" or \"dirty-files\".")),
|
|
251
|
+
timeoutMs: optional(pipe(number(`integrations.stopGate.timeoutMs must be an integer from 1 to ${maximumStopGateTimeoutMs}.`), integer(`integrations.stopGate.timeoutMs must be an integer from 1 to ${maximumStopGateTimeoutMs}.`), minValue(1, `integrations.stopGate.timeoutMs must be an integer from 1 to ${maximumStopGateTimeoutMs}.`), maxValue(maximumStopGateTimeoutMs, `integrations.stopGate.timeoutMs must be an integer from 1 to ${maximumStopGateTimeoutMs}.`)))
|
|
252
|
+
}, "integrations.stopGate must be an object.");
|
|
253
|
+
function resolveStopGateConfig(config, cwd) {
|
|
254
|
+
const project = resolveConfigForProject(cwd, config, { cwd });
|
|
255
|
+
for (const skipped of project.skipped) if (skipped.item.integrations?.stopGate !== void 0) throw new Error("integrations.stopGate must be declared in a global config item without files, directories, or ignores.");
|
|
256
|
+
return project.matched.reduce((resolved, item) => mergeStopGateConfig(resolved, item), { ...defaultStopGateConfig });
|
|
257
|
+
}
|
|
258
|
+
function assertStopGateConfig(value) {
|
|
259
|
+
parse(stopGateConfigSchema, value);
|
|
260
|
+
}
|
|
261
|
+
function mergeStopGateConfig(resolved, item) {
|
|
262
|
+
const stopGate = item.integrations?.stopGate;
|
|
263
|
+
if (stopGate === void 0) return resolved;
|
|
264
|
+
assertStopGateConfig(stopGate);
|
|
265
|
+
return {
|
|
266
|
+
enabled: stopGate.enabled ?? resolved.enabled,
|
|
267
|
+
target: stopGate.target ?? resolved.target,
|
|
268
|
+
timeoutMs: stopGate.timeoutMs ?? resolved.timeoutMs
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
//#endregion
|
|
239
272
|
//#region src/core/languages/js/index.ts
|
|
240
273
|
const javascriptLanguage = {
|
|
241
274
|
extensions: [
|
|
@@ -605,13 +638,16 @@ function recordMissingLanguages(into, rules, registry, path) {
|
|
|
605
638
|
}
|
|
606
639
|
//#endregion
|
|
607
640
|
//#region package.json
|
|
608
|
-
var version = "0.
|
|
641
|
+
var version = "0.6.0";
|
|
609
642
|
//#endregion
|
|
610
643
|
//#region src/core/cache/types.ts
|
|
611
644
|
const CACHE_MAGIC = "ALINT_CACHE";
|
|
612
645
|
//#endregion
|
|
613
646
|
//#region src/core/cache/store.ts
|
|
614
647
|
const DEFAULT_CACHE_FILE_NAME = ".alintcache";
|
|
648
|
+
const LOCK_STALE_MS = 2e4;
|
|
649
|
+
const LOCK_UPDATE_MS = 3e3;
|
|
650
|
+
const METADATA_READ_LIMIT = 4096;
|
|
615
651
|
const FiniteNumberSchema = pipe(number(), finite());
|
|
616
652
|
const PositionSchema = object({
|
|
617
653
|
column: FiniteNumberSchema,
|
|
@@ -671,18 +707,19 @@ const CacheEntrySchema = object({
|
|
|
671
707
|
}),
|
|
672
708
|
usage: array(UsageSchema)
|
|
673
709
|
});
|
|
710
|
+
const CachedOwnerSchema = object({
|
|
711
|
+
contentHash: optional(string()),
|
|
712
|
+
kind: union([literal("file"), literal("project")]),
|
|
713
|
+
path: string(),
|
|
714
|
+
slots: array(string())
|
|
715
|
+
});
|
|
674
716
|
function objectRecord(value) {
|
|
675
717
|
return pipe(unknown(), check((input) => typeof input === "object" && input !== null && !Array.isArray(input)), record(string(), value));
|
|
676
718
|
}
|
|
677
719
|
const CacheFileBodySchema = pipe(object({
|
|
678
720
|
createdAt: string(),
|
|
679
721
|
entries: objectRecord(CacheEntrySchema),
|
|
680
|
-
owners: objectRecord(
|
|
681
|
-
contentHash: optional(string()),
|
|
682
|
-
kind: union([literal("file"), literal("project")]),
|
|
683
|
-
path: string(),
|
|
684
|
-
slots: array(string())
|
|
685
|
-
})),
|
|
722
|
+
owners: objectRecord(CachedOwnerSchema),
|
|
686
723
|
updatedAt: string()
|
|
687
724
|
}), check((body) => {
|
|
688
725
|
const entryKeys = Object.keys(body.entries);
|
|
@@ -690,22 +727,68 @@ const CacheFileBodySchema = pipe(object({
|
|
|
690
727
|
const uniqueSlots = new Set(referencedSlots);
|
|
691
728
|
return uniqueSlots.size === referencedSlots.length && uniqueSlots.size === entryKeys.length && entryKeys.every((key) => uniqueSlots.has(key));
|
|
692
729
|
}));
|
|
730
|
+
const CacheMetadataSchema = object({
|
|
731
|
+
alintVersion: string(),
|
|
732
|
+
createdAt: string(),
|
|
733
|
+
magic: literal(CACHE_MAGIC),
|
|
734
|
+
schemaVersion: literal(2),
|
|
735
|
+
type: literal("metadata")
|
|
736
|
+
});
|
|
737
|
+
const PutEventSchema = object({
|
|
738
|
+
at: string(),
|
|
739
|
+
entry: CacheEntrySchema,
|
|
740
|
+
owner: CachedOwnerSchema,
|
|
741
|
+
ownerKey: string(),
|
|
742
|
+
slotKey: string(),
|
|
743
|
+
type: literal("put")
|
|
744
|
+
});
|
|
745
|
+
const ReplaceOwnerEventSchema = object({
|
|
746
|
+
at: string(),
|
|
747
|
+
entries: array(object({
|
|
748
|
+
entry: CacheEntrySchema,
|
|
749
|
+
slotKey: string()
|
|
750
|
+
})),
|
|
751
|
+
mode: union([literal("merge"), literal("replace")]),
|
|
752
|
+
owner: CachedOwnerSchema,
|
|
753
|
+
ownerKey: string(),
|
|
754
|
+
type: literal("replace-owner")
|
|
755
|
+
});
|
|
756
|
+
const RemoveOwnerEventSchema = object({
|
|
757
|
+
at: string(),
|
|
758
|
+
ownerKey: string(),
|
|
759
|
+
type: literal("remove-owner")
|
|
760
|
+
});
|
|
761
|
+
const CacheEventSchema = union([
|
|
762
|
+
PutEventSchema,
|
|
763
|
+
ReplaceOwnerEventSchema,
|
|
764
|
+
RemoveOwnerEventSchema
|
|
765
|
+
]);
|
|
693
766
|
async function createCacheStore(options) {
|
|
694
767
|
const location = resolveCacheLocation(options.cwd, options.location);
|
|
695
768
|
if (!options.enabled) return createNoopCacheStore(location);
|
|
696
769
|
const alintVersion = options.alintVersion ?? version;
|
|
697
|
-
const
|
|
698
|
-
|
|
699
|
-
|
|
770
|
+
const initial = await loadCacheLog(location, alintVersion);
|
|
771
|
+
if (options.readOnly) return createReadOnlyCacheStore(location, initial.body, options.cwd);
|
|
772
|
+
const dependencies = {
|
|
773
|
+
append: options.appendFile ?? appendFile,
|
|
774
|
+
lock: options.lock ?? { acquire: lockfile.lock },
|
|
775
|
+
write: options.writeFile ?? writeFile
|
|
776
|
+
};
|
|
777
|
+
const trimmed = await trimOnce(location, alintVersion, initial, dependencies);
|
|
778
|
+
const writer = createEventWriter(location, trimmed.metadata, dependencies);
|
|
700
779
|
const fileExists = options.fileExists ?? defaultFileExists;
|
|
701
|
-
const writeFile$1 = options.writeFile ?? writeFile;
|
|
702
780
|
return {
|
|
703
|
-
beginOwner: (owner) => beginOwner(body, owner, options.cwd),
|
|
781
|
+
beginOwner: (owner, metadata) => beginOwner(trimmed.body, owner, options.cwd, writer, metadata),
|
|
782
|
+
flush: () => writer.flush(false),
|
|
704
783
|
location,
|
|
705
784
|
reconcile: async () => {
|
|
706
|
-
await collectMissingFileOwners(body, options.cwd, fileExists);
|
|
707
|
-
|
|
708
|
-
|
|
785
|
+
const removedOwners = await collectMissingFileOwners(trimmed.body, options.cwd, fileExists);
|
|
786
|
+
for (const ownerKey of removedOwners) writer.enqueue({
|
|
787
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
788
|
+
ownerKey,
|
|
789
|
+
type: "remove-owner"
|
|
790
|
+
});
|
|
791
|
+
await writer.flush(true);
|
|
709
792
|
}
|
|
710
793
|
};
|
|
711
794
|
}
|
|
@@ -757,7 +840,50 @@ function resolveCacheLocation(cwd, location) {
|
|
|
757
840
|
} catch {}
|
|
758
841
|
return resolved;
|
|
759
842
|
}
|
|
760
|
-
function
|
|
843
|
+
async function appendBatch(location, payload, append) {
|
|
844
|
+
const handle = await open(location, "a+");
|
|
845
|
+
try {
|
|
846
|
+
const { size } = await handle.stat();
|
|
847
|
+
let separator = "";
|
|
848
|
+
if (size > 0) {
|
|
849
|
+
const finalByte = Buffer.alloc(1);
|
|
850
|
+
await handle.read(finalByte, 0, 1, size - 1);
|
|
851
|
+
separator = finalByte[0] === 10 ? "" : "\n";
|
|
852
|
+
}
|
|
853
|
+
await append(location, `${separator}${payload}`, "utf8");
|
|
854
|
+
} finally {
|
|
855
|
+
await handle.close();
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
function applyEvent(body, event) {
|
|
859
|
+
if (event.type === "remove-owner") {
|
|
860
|
+
removeOwner(body, event.ownerKey);
|
|
861
|
+
body.updatedAt = event.at;
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
if (event.type === "put") {
|
|
865
|
+
const current = body.owners[event.ownerKey];
|
|
866
|
+
const slots = new Set(current?.slots ?? []);
|
|
867
|
+
slots.add(event.slotKey);
|
|
868
|
+
body.entries[event.slotKey] = event.entry;
|
|
869
|
+
body.owners[event.ownerKey] = {
|
|
870
|
+
...event.owner,
|
|
871
|
+
slots: [...slots].sort()
|
|
872
|
+
};
|
|
873
|
+
body.updatedAt = event.at;
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
const entries = event.mode === "merge" ? ownerEntries(body, body.owners[event.ownerKey]) : /* @__PURE__ */ new Map();
|
|
877
|
+
for (const item of event.entries) entries.set(item.slotKey, item.entry);
|
|
878
|
+
for (const slot of body.owners[event.ownerKey]?.slots ?? []) delete body.entries[slot];
|
|
879
|
+
for (const [slotKey, entry] of entries) body.entries[slotKey] = entry;
|
|
880
|
+
body.owners[event.ownerKey] = {
|
|
881
|
+
...event.owner,
|
|
882
|
+
slots: [...entries.keys()].sort()
|
|
883
|
+
};
|
|
884
|
+
body.updatedAt = event.at;
|
|
885
|
+
}
|
|
886
|
+
function beginOwner(body, owner, cwd, writer, ownerMetadata = {}) {
|
|
761
887
|
const normalizedOwner = {
|
|
762
888
|
kind: owner.kind,
|
|
763
889
|
path: normalizeCachePath(cwd, owner.path)
|
|
@@ -765,22 +891,64 @@ function beginOwner(body, owner, cwd) {
|
|
|
765
891
|
const key = ownerKey(normalizedOwner, cwd);
|
|
766
892
|
const previousOwner = body.owners[key];
|
|
767
893
|
const nextEntries = /* @__PURE__ */ new Map();
|
|
894
|
+
const checkpointEntries = /* @__PURE__ */ new Map();
|
|
895
|
+
const commit = (metadata = {}) => {
|
|
896
|
+
const contentHash = metadata.contentHash ?? ownerMetadata.contentHash;
|
|
897
|
+
const committedEntries = metadata.mode === "merge" ? ownerEntries(body, body.owners[key]) : /* @__PURE__ */ new Map();
|
|
898
|
+
for (const [entryKey, cacheEntry] of nextEntries) committedEntries.set(entryKey, cacheEntry);
|
|
899
|
+
const replacedSlots = /* @__PURE__ */ new Set([...previousOwner?.slots ?? [], ...body.owners[key]?.slots ?? []]);
|
|
900
|
+
for (const replacedSlot of replacedSlots) delete body.entries[replacedSlot];
|
|
901
|
+
for (const [entryKey, cacheEntry] of committedEntries) body.entries[entryKey] = cacheEntry;
|
|
902
|
+
const cachedOwner = {
|
|
903
|
+
contentHash,
|
|
904
|
+
kind: normalizedOwner.kind,
|
|
905
|
+
path: normalizedOwner.path,
|
|
906
|
+
slots: [...committedEntries.keys()].sort()
|
|
907
|
+
};
|
|
908
|
+
body.owners[key] = cachedOwner;
|
|
909
|
+
body.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
910
|
+
writer.enqueue({
|
|
911
|
+
at: body.updatedAt,
|
|
912
|
+
entries: [...committedEntries].map(([slotKey, entry]) => ({
|
|
913
|
+
entry,
|
|
914
|
+
slotKey
|
|
915
|
+
})),
|
|
916
|
+
mode: metadata.mode ?? "replace",
|
|
917
|
+
owner: cachedOwner,
|
|
918
|
+
ownerKey: key,
|
|
919
|
+
type: "replace-owner"
|
|
920
|
+
});
|
|
921
|
+
checkpointEntries.clear();
|
|
922
|
+
};
|
|
768
923
|
return {
|
|
769
|
-
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
const replacedSlots = /* @__PURE__ */ new Set([...previousOwner?.slots ?? [], ...body.owners[key]?.slots ?? []]);
|
|
773
|
-
for (const replacedSlot of replacedSlots) delete body.entries[replacedSlot];
|
|
774
|
-
for (const [entryKey, cacheEntry] of committedEntries) body.entries[entryKey] = cacheEntry;
|
|
775
|
-
body.owners[key] = {
|
|
776
|
-
contentHash: metadata.contentHash,
|
|
924
|
+
checkpoint: async () => {
|
|
925
|
+
const cachedOwner = {
|
|
926
|
+
contentHash: ownerMetadata.contentHash,
|
|
777
927
|
kind: normalizedOwner.kind,
|
|
778
928
|
path: normalizedOwner.path,
|
|
779
|
-
slots: [
|
|
929
|
+
slots: []
|
|
780
930
|
};
|
|
781
|
-
|
|
931
|
+
for (const [entryKey, cacheEntry] of checkpointEntries) {
|
|
932
|
+
const event = {
|
|
933
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
934
|
+
entry: cacheEntry,
|
|
935
|
+
owner: cachedOwner,
|
|
936
|
+
ownerKey: key,
|
|
937
|
+
slotKey: entryKey,
|
|
938
|
+
type: "put"
|
|
939
|
+
};
|
|
940
|
+
applyEvent(body, event);
|
|
941
|
+
writer.enqueue(event);
|
|
942
|
+
}
|
|
943
|
+
checkpointEntries.clear();
|
|
944
|
+
await writer.flush(false);
|
|
945
|
+
},
|
|
946
|
+
commit,
|
|
947
|
+
discard: (cacheSlot) => {
|
|
948
|
+
const entryKey = slotKey(normalizedOwner, cacheSlot, cwd);
|
|
949
|
+
nextEntries.delete(entryKey);
|
|
950
|
+
checkpointEntries.delete(entryKey);
|
|
782
951
|
},
|
|
783
|
-
discard: (cacheSlot) => nextEntries.delete(slotKey(normalizedOwner, cacheSlot, cwd)),
|
|
784
952
|
lookup: (cacheSlot, fingerprint) => {
|
|
785
953
|
const entryKey = slotKey(normalizedOwner, cacheSlot, cwd);
|
|
786
954
|
const cached = body.entries[entryKey];
|
|
@@ -788,15 +956,21 @@ function beginOwner(body, owner, cwd) {
|
|
|
788
956
|
nextEntries.set(entryKey, cached);
|
|
789
957
|
return cached;
|
|
790
958
|
},
|
|
791
|
-
put: (cacheSlot, cacheEntry) =>
|
|
959
|
+
put: (cacheSlot, cacheEntry) => {
|
|
960
|
+
const entryKey = slotKey(normalizedOwner, cacheSlot, cwd);
|
|
961
|
+
nextEntries.set(entryKey, cacheEntry);
|
|
962
|
+
checkpointEntries.set(entryKey, cacheEntry);
|
|
963
|
+
}
|
|
792
964
|
};
|
|
793
965
|
}
|
|
794
966
|
async function collectMissingFileOwners(body, cwd, fileExists) {
|
|
967
|
+
const removed = [];
|
|
795
968
|
for (const [key, owner] of Object.entries(body.owners)) {
|
|
796
969
|
if (owner.kind !== "file" || await fileExists(resolve$1(cwd, owner.path))) continue;
|
|
797
|
-
|
|
798
|
-
|
|
970
|
+
removeOwner(body, key);
|
|
971
|
+
removed.push(key);
|
|
799
972
|
}
|
|
973
|
+
return removed;
|
|
800
974
|
}
|
|
801
975
|
function createBaseTargetIdentity(target) {
|
|
802
976
|
if (target.identity && (target.kind !== "file" || target.identity !== "file")) return target.filePath ? `${target.kind}:${target.filePath}:${target.identity}` : `${target.kind}:${target.identity}`;
|
|
@@ -805,17 +979,53 @@ function createBaseTargetIdentity(target) {
|
|
|
805
979
|
if (target.range) return `${target.kind}:${target.range.start}:${target.range.end}`;
|
|
806
980
|
return target.kind;
|
|
807
981
|
}
|
|
808
|
-
function createEmptyCacheBody() {
|
|
809
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
982
|
+
function createEmptyCacheBody(createdAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
810
983
|
return {
|
|
811
|
-
createdAt
|
|
984
|
+
createdAt,
|
|
812
985
|
entries: {},
|
|
813
986
|
owners: {},
|
|
814
|
-
updatedAt:
|
|
987
|
+
updatedAt: createdAt
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
function createEventWriter(location, metadata, dependencies) {
|
|
991
|
+
const backlog = [];
|
|
992
|
+
let validationError;
|
|
993
|
+
let previousWrite = Promise.resolve();
|
|
994
|
+
const persistBacklog = async () => {
|
|
995
|
+
if (validationError) throw validationError;
|
|
996
|
+
if (backlog.length === 0) return;
|
|
997
|
+
await mkdir(dirname(location), { recursive: true });
|
|
998
|
+
await withCacheLock(location, dependencies.lock, getCacheLockOptions({
|
|
999
|
+
factor: 2,
|
|
1000
|
+
maxTimeout: 16e3,
|
|
1001
|
+
minTimeout: 2e3,
|
|
1002
|
+
retries: 4
|
|
1003
|
+
}), async () => {
|
|
1004
|
+
await ensureCompatibleMetadata(location, metadata, dependencies.write);
|
|
1005
|
+
const batchSize = backlog.length;
|
|
1006
|
+
await appendBatch(location, backlog.slice(0, batchSize).map((event) => `${JSON.stringify(event)}\n`).join(""), dependencies.append);
|
|
1007
|
+
backlog.splice(0, batchSize);
|
|
1008
|
+
});
|
|
1009
|
+
validationError = void 0;
|
|
1010
|
+
};
|
|
1011
|
+
return {
|
|
1012
|
+
enqueue: (event) => {
|
|
1013
|
+
try {
|
|
1014
|
+
backlog.push(parse(CacheEventSchema, event));
|
|
1015
|
+
} catch (error) {
|
|
1016
|
+
validationError ??= error;
|
|
1017
|
+
}
|
|
1018
|
+
},
|
|
1019
|
+
flush: (finalAttempt) => {
|
|
1020
|
+
const write = previousWrite.then(persistBacklog);
|
|
1021
|
+
previousWrite = write.catch(() => {});
|
|
1022
|
+
return finalAttempt ? write : write.catch(() => {});
|
|
1023
|
+
}
|
|
815
1024
|
};
|
|
816
1025
|
}
|
|
817
1026
|
function createNoopCacheStore(location) {
|
|
818
1027
|
const transaction = {
|
|
1028
|
+
checkpoint: async () => {},
|
|
819
1029
|
commit: () => {},
|
|
820
1030
|
discard: () => {},
|
|
821
1031
|
lookup: () => void 0,
|
|
@@ -823,22 +1033,24 @@ function createNoopCacheStore(location) {
|
|
|
823
1033
|
};
|
|
824
1034
|
return {
|
|
825
1035
|
beginOwner: () => transaction,
|
|
1036
|
+
flush: async () => {},
|
|
826
1037
|
location,
|
|
827
1038
|
reconcile: async () => {}
|
|
828
1039
|
};
|
|
829
1040
|
}
|
|
830
1041
|
function createReadOnlyCacheStore(location, body, cwd) {
|
|
831
|
-
const transaction = (owner) => ({
|
|
832
|
-
commit: () => {},
|
|
833
|
-
discard: () => {},
|
|
834
|
-
lookup: (slot, fingerprint) => {
|
|
835
|
-
const cached = body.entries[slotKey(owner, slot, cwd)];
|
|
836
|
-
return cached && fingerprintsEqual(cached.fingerprint, fingerprint) ? cached : void 0;
|
|
837
|
-
},
|
|
838
|
-
put: () => {}
|
|
839
|
-
});
|
|
840
1042
|
return {
|
|
841
|
-
beginOwner:
|
|
1043
|
+
beginOwner: (owner) => ({
|
|
1044
|
+
checkpoint: async () => {},
|
|
1045
|
+
commit: () => {},
|
|
1046
|
+
discard: () => {},
|
|
1047
|
+
lookup: (slot, fingerprint) => {
|
|
1048
|
+
const cached = body.entries[slotKey(owner, slot, cwd)];
|
|
1049
|
+
return cached && fingerprintsEqual(cached.fingerprint, fingerprint) ? cached : void 0;
|
|
1050
|
+
},
|
|
1051
|
+
put: () => {}
|
|
1052
|
+
}),
|
|
1053
|
+
flush: async () => {},
|
|
842
1054
|
location,
|
|
843
1055
|
reconcile: async () => {}
|
|
844
1056
|
};
|
|
@@ -852,9 +1064,38 @@ async function defaultFileExists(path) {
|
|
|
852
1064
|
throw error;
|
|
853
1065
|
}
|
|
854
1066
|
}
|
|
1067
|
+
function emptyLoadedLog(alintVersion) {
|
|
1068
|
+
const metadata = {
|
|
1069
|
+
alintVersion,
|
|
1070
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1071
|
+
magic: CACHE_MAGIC,
|
|
1072
|
+
schemaVersion: 2,
|
|
1073
|
+
type: "metadata"
|
|
1074
|
+
};
|
|
1075
|
+
return {
|
|
1076
|
+
body: createEmptyCacheBody(metadata.createdAt),
|
|
1077
|
+
metadata
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
async function ensureCompatibleMetadata(location, metadata, write) {
|
|
1081
|
+
try {
|
|
1082
|
+
if (parseMetadataLine(await readMetadataLine(location), metadata.alintVersion)) return;
|
|
1083
|
+
} catch (error) {
|
|
1084
|
+
if (!isMissingFileError(error)) throw error;
|
|
1085
|
+
}
|
|
1086
|
+
await write(location, `${JSON.stringify(metadata)}\n`);
|
|
1087
|
+
}
|
|
855
1088
|
function fingerprintsEqual(left, right) {
|
|
856
1089
|
return left.configHash === right.configHash && left.modelHash === right.modelHash && left.ruleHash === right.ruleHash && left.targetHash === right.targetHash;
|
|
857
1090
|
}
|
|
1091
|
+
function getCacheLockOptions(retries) {
|
|
1092
|
+
return {
|
|
1093
|
+
realpath: false,
|
|
1094
|
+
retries,
|
|
1095
|
+
stale: LOCK_STALE_MS,
|
|
1096
|
+
update: LOCK_UPDATE_MS
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
858
1099
|
function hasJsonArrayProperties(input, ancestors) {
|
|
859
1100
|
let arrayLength;
|
|
860
1101
|
let indexCount = 0;
|
|
@@ -866,7 +1107,8 @@ function hasJsonArrayProperties(input, ancestors) {
|
|
|
866
1107
|
arrayLength = descriptor.value;
|
|
867
1108
|
continue;
|
|
868
1109
|
}
|
|
869
|
-
|
|
1110
|
+
const index = typeof key === "string" ? Number(key) : NaN;
|
|
1111
|
+
if (typeof key !== "string" || !Number.isInteger(index) || index < 0 || index >= 2 ** 32 - 1 || String(index) !== key || !descriptor.enumerable) return false;
|
|
870
1112
|
if (!isJsonValueAt(descriptor.value, ancestors)) return false;
|
|
871
1113
|
indexCount += 1;
|
|
872
1114
|
}
|
|
@@ -881,10 +1123,6 @@ function hasJsonObjectProperties(input, ancestors) {
|
|
|
881
1123
|
}
|
|
882
1124
|
return true;
|
|
883
1125
|
}
|
|
884
|
-
function isCanonicalArrayIndex(key) {
|
|
885
|
-
const index = Number(key);
|
|
886
|
-
return Number.isInteger(index) && index >= 0 && index < 2 ** 32 - 1 && String(index) === key;
|
|
887
|
-
}
|
|
888
1126
|
function isJsonValue(input) {
|
|
889
1127
|
return isJsonValueAt(input, /* @__PURE__ */ new WeakSet());
|
|
890
1128
|
}
|
|
@@ -910,27 +1148,14 @@ function isJsonValueAt(input, ancestors) {
|
|
|
910
1148
|
function isMissingFileError(error) {
|
|
911
1149
|
return isNodeErrorCode(error, "ENOENT") || isNodeErrorCode(error, "ENOTDIR");
|
|
912
1150
|
}
|
|
913
|
-
function
|
|
914
|
-
return error instanceof Error && "code" in error && error.code === code;
|
|
915
|
-
}
|
|
916
|
-
async function loadCacheBody(location, expectedHeader, readOnly) {
|
|
917
|
-
let handle;
|
|
1151
|
+
async function loadCacheLog(location, alintVersion) {
|
|
918
1152
|
try {
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
await
|
|
922
|
-
handle = void 0;
|
|
923
|
-
if (header !== expectedHeader) {
|
|
924
|
-
if (!readOnly) await rm(location, { force: true });
|
|
925
|
-
return createEmptyCacheBody();
|
|
926
|
-
}
|
|
927
|
-
const text = await readFile(location, "utf8");
|
|
928
|
-
return parseCacheBody(text.slice(text.indexOf("\n") + 1));
|
|
1153
|
+
const metadata = parseMetadataLine(await readMetadataLine(location), alintVersion);
|
|
1154
|
+
if (!metadata) return emptyLoadedLog(alintVersion);
|
|
1155
|
+
return replayLog(await readFile(location, "utf8"), metadata);
|
|
929
1156
|
} catch (error) {
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
if (!readOnly) await rm(location, { force: true }).catch(() => {});
|
|
933
|
-
return createEmptyCacheBody();
|
|
1157
|
+
if (isMissingFileError(error)) return emptyLoadedLog(alintVersion);
|
|
1158
|
+
throw error;
|
|
934
1159
|
}
|
|
935
1160
|
}
|
|
936
1161
|
function ownerEntries(body, owner) {
|
|
@@ -947,27 +1172,72 @@ function ownerKey(owner, cwd) {
|
|
|
947
1172
|
path: normalizeCachePath(cwd, owner.path)
|
|
948
1173
|
});
|
|
949
1174
|
}
|
|
950
|
-
function
|
|
951
|
-
|
|
952
|
-
|
|
1175
|
+
function parseMetadataLine(line, expectedAlintVersion) {
|
|
1176
|
+
if (!line) return void 0;
|
|
1177
|
+
try {
|
|
1178
|
+
const value = JSON.parse(line);
|
|
1179
|
+
const metadata = parse(CacheMetadataSchema, value);
|
|
1180
|
+
if (expectedAlintVersion !== void 0 && metadata.alintVersion !== expectedAlintVersion) return void 0;
|
|
1181
|
+
return metadata;
|
|
1182
|
+
} catch {
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
953
1185
|
}
|
|
954
|
-
async function
|
|
955
|
-
const
|
|
956
|
-
|
|
1186
|
+
async function persistCompactedLog(location, loaded, write) {
|
|
1187
|
+
const metadata = parse(CacheMetadataSchema, loaded.metadata);
|
|
1188
|
+
const events = Object.entries(loaded.body.owners).map(([ownerKey, owner]) => ({
|
|
1189
|
+
at: loaded.body.updatedAt,
|
|
1190
|
+
entries: [...ownerEntries(loaded.body, owner)].map(([slotKey, entry]) => ({
|
|
1191
|
+
entry,
|
|
1192
|
+
slotKey
|
|
1193
|
+
})),
|
|
1194
|
+
mode: "replace",
|
|
1195
|
+
owner,
|
|
1196
|
+
ownerKey,
|
|
1197
|
+
type: "replace-owner"
|
|
1198
|
+
})).map((event) => parse(CacheEventSchema, event));
|
|
1199
|
+
const contents = [`${JSON.stringify(metadata)}\n`, ...events.map((event) => `${JSON.stringify(event)}\n`)].join("");
|
|
957
1200
|
const tempPath = join(dirname(location), `.${basename$1(location)}.${process.pid}.${randomUUID()}.tmp`);
|
|
958
1201
|
try {
|
|
959
|
-
await
|
|
1202
|
+
await write(tempPath, contents);
|
|
960
1203
|
await rename(tempPath, location);
|
|
961
1204
|
} catch (error) {
|
|
962
1205
|
await rm(tempPath, { force: true }).catch(() => {});
|
|
963
1206
|
throw error;
|
|
964
1207
|
}
|
|
965
1208
|
}
|
|
966
|
-
async function
|
|
967
|
-
const
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1209
|
+
async function readMetadataLine(location) {
|
|
1210
|
+
const handle = await open(location, "r");
|
|
1211
|
+
try {
|
|
1212
|
+
const buffer = Buffer.alloc(METADATA_READ_LIMIT);
|
|
1213
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
1214
|
+
const newlineIndex = buffer.subarray(0, bytesRead).indexOf(10);
|
|
1215
|
+
if (newlineIndex < 0) return void 0;
|
|
1216
|
+
const lineEnd = newlineIndex > 0 && buffer[newlineIndex - 1] === 13 ? newlineIndex - 1 : newlineIndex;
|
|
1217
|
+
return buffer.toString("utf8", 0, lineEnd);
|
|
1218
|
+
} finally {
|
|
1219
|
+
await handle.close();
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
function removeOwner(body, key) {
|
|
1223
|
+
for (const slot of body.owners[key]?.slots ?? []) delete body.entries[slot];
|
|
1224
|
+
delete body.owners[key];
|
|
1225
|
+
}
|
|
1226
|
+
function replayLog(text, metadata) {
|
|
1227
|
+
const body = createEmptyCacheBody(metadata.createdAt);
|
|
1228
|
+
const lines = text.split(/\r?\n/);
|
|
1229
|
+
for (let index = 1; index < lines.length; index += 1) {
|
|
1230
|
+
const line = lines[index];
|
|
1231
|
+
if (!line) continue;
|
|
1232
|
+
try {
|
|
1233
|
+
const value = JSON.parse(line);
|
|
1234
|
+
applyEvent(body, parse(CacheEventSchema, value));
|
|
1235
|
+
} catch {}
|
|
1236
|
+
}
|
|
1237
|
+
return {
|
|
1238
|
+
body: parse(CacheFileBodySchema, body),
|
|
1239
|
+
metadata
|
|
1240
|
+
};
|
|
971
1241
|
}
|
|
972
1242
|
function slotKey(owner, slot, cwd) {
|
|
973
1243
|
return stableHash({
|
|
@@ -975,6 +1245,43 @@ function slotKey(owner, slot, cwd) {
|
|
|
975
1245
|
...slot
|
|
976
1246
|
});
|
|
977
1247
|
}
|
|
1248
|
+
async function trimOnce(location, alintVersion, fallback, dependencies) {
|
|
1249
|
+
await mkdir(dirname(location), { recursive: true });
|
|
1250
|
+
try {
|
|
1251
|
+
return await withCacheLock(location, dependencies.lock, getCacheLockOptions(0), async () => {
|
|
1252
|
+
const latest = await loadCacheLog(location, alintVersion);
|
|
1253
|
+
await persistCompactedLog(location, latest, dependencies.write);
|
|
1254
|
+
return latest;
|
|
1255
|
+
});
|
|
1256
|
+
} catch (error) {
|
|
1257
|
+
if (isNodeErrorCode(error, "ELOCKED")) return fallback;
|
|
1258
|
+
throw error;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
async function withCacheLock(location, lock, options, operation) {
|
|
1262
|
+
let compromised;
|
|
1263
|
+
const release = await lock.acquire(location, {
|
|
1264
|
+
...options,
|
|
1265
|
+
onCompromised: (error) => {
|
|
1266
|
+
compromised = error;
|
|
1267
|
+
}
|
|
1268
|
+
});
|
|
1269
|
+
let operationError;
|
|
1270
|
+
try {
|
|
1271
|
+
const result = await operation();
|
|
1272
|
+
if (compromised) throw compromised;
|
|
1273
|
+
return result;
|
|
1274
|
+
} catch (error) {
|
|
1275
|
+
operationError = error;
|
|
1276
|
+
throw error;
|
|
1277
|
+
} finally {
|
|
1278
|
+
try {
|
|
1279
|
+
await release();
|
|
1280
|
+
} catch (error) {
|
|
1281
|
+
if (operationError === void 0) throw error;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
978
1285
|
//#endregion
|
|
979
1286
|
//#region src/core/execution/records.ts
|
|
980
1287
|
function snapshotDiagnostic(diagnostic) {
|
|
@@ -1112,9 +1419,10 @@ async function executeRuleJob(job, options) {
|
|
|
1112
1419
|
failure: failure(handlerCause, job, handlerCause instanceof SourceChangedError ? "source-changed" : "handler"),
|
|
1113
1420
|
state: "failed"
|
|
1114
1421
|
});
|
|
1115
|
-
if (slot && fingerprint)
|
|
1422
|
+
if (slot && fingerprint) {
|
|
1116
1423
|
job.target.cacheOwner?.put(slot, createCacheEntry(job, fingerprint, bucket));
|
|
1117
|
-
|
|
1424
|
+
await job.target.cacheOwner?.checkpoint();
|
|
1425
|
+
}
|
|
1118
1426
|
return finish(job, bucket, {
|
|
1119
1427
|
cache: "miss",
|
|
1120
1428
|
state: "completed"
|
|
@@ -2097,7 +2405,7 @@ async function planSource(input, options) {
|
|
|
2097
2405
|
cacheOwner = options.cacheStore.beginOwner({
|
|
2098
2406
|
kind: "file",
|
|
2099
2407
|
path: file.path
|
|
2100
|
-
});
|
|
2408
|
+
}, { contentHash });
|
|
2101
2409
|
const baseFile = {
|
|
2102
2410
|
contentHash,
|
|
2103
2411
|
language: file.language,
|
|
@@ -2112,10 +2420,7 @@ async function planSource(input, options) {
|
|
|
2112
2420
|
};
|
|
2113
2421
|
}
|
|
2114
2422
|
if (options.signal?.aborted) {
|
|
2115
|
-
cacheOwner.commit({
|
|
2116
|
-
contentHash,
|
|
2117
|
-
mode: "merge"
|
|
2118
|
-
});
|
|
2423
|
+
cacheOwner.commit({ mode: "merge" });
|
|
2119
2424
|
return {
|
|
2120
2425
|
outcomes: Promise.resolve([]),
|
|
2121
2426
|
project
|
|
@@ -2124,11 +2429,9 @@ async function planSource(input, options) {
|
|
|
2124
2429
|
const batch = options.scheduler.schedule(jobs);
|
|
2125
2430
|
reportFilePlanningComplete(input, batch.jobsAdded, options);
|
|
2126
2431
|
return {
|
|
2127
|
-
outcomes: batch.outcomes.then((settled) => {
|
|
2128
|
-
cacheOwner.commit(options.signal?.aborted ? {
|
|
2129
|
-
|
|
2130
|
-
mode: "merge"
|
|
2131
|
-
} : { contentHash });
|
|
2432
|
+
outcomes: batch.outcomes.then(async (settled) => {
|
|
2433
|
+
cacheOwner.commit(options.signal?.aborted ? { mode: "merge" } : void 0);
|
|
2434
|
+
await options.cacheStore.flush();
|
|
2132
2435
|
return settled;
|
|
2133
2436
|
}),
|
|
2134
2437
|
project
|
|
@@ -2405,6 +2708,14 @@ var AlintAbortError = class extends AlintRunCancelledError {
|
|
|
2405
2708
|
this.name = "AlintAbortError";
|
|
2406
2709
|
}
|
|
2407
2710
|
};
|
|
2711
|
+
var AlintCachePersistenceError = class extends Error {
|
|
2712
|
+
result;
|
|
2713
|
+
constructor(result, cause) {
|
|
2714
|
+
super("Failed to persist the alint cache.", { cause });
|
|
2715
|
+
this.name = "AlintCachePersistenceError";
|
|
2716
|
+
this.result = result;
|
|
2717
|
+
}
|
|
2718
|
+
};
|
|
2408
2719
|
var AlintRunError = class extends Error {
|
|
2409
2720
|
failures;
|
|
2410
2721
|
result;
|
|
@@ -2482,6 +2793,7 @@ async function runAlint(options = {}) {
|
|
|
2482
2793
|
const outcomes = [];
|
|
2483
2794
|
const fileFailures = [];
|
|
2484
2795
|
let infrastructureError;
|
|
2796
|
+
let cachePersistenceError;
|
|
2485
2797
|
let infrastructureFailed = false;
|
|
2486
2798
|
let admissionFailed = false;
|
|
2487
2799
|
try {
|
|
@@ -2545,7 +2857,9 @@ async function runAlint(options = {}) {
|
|
|
2545
2857
|
}
|
|
2546
2858
|
if (!admissionFailed && !options.cacheOnly) try {
|
|
2547
2859
|
await cacheStore.reconcile();
|
|
2548
|
-
} catch {
|
|
2860
|
+
} catch (error) {
|
|
2861
|
+
cachePersistenceError = error;
|
|
2862
|
+
}
|
|
2549
2863
|
}
|
|
2550
2864
|
runProgress.finalize();
|
|
2551
2865
|
if (admissionFailed) throw infrastructureError;
|
|
@@ -2574,6 +2888,7 @@ async function runAlint(options = {}) {
|
|
|
2574
2888
|
usage: snapshotRunUsage(result.usage)
|
|
2575
2889
|
});
|
|
2576
2890
|
if (infrastructureFailed) throw infrastructureError;
|
|
2891
|
+
if (cachePersistenceError !== void 0) throw new AlintCachePersistenceError(result, cachePersistenceError);
|
|
2577
2892
|
if (options.signal?.aborted) throw new AlintAbortError(result, { cause: options.signal.reason });
|
|
2578
2893
|
if (failures.length > 0) throw new AlintRunError(fileFailures.length > 0 ? `${failures.length} alint execution${failures.length === 1 ? "" : "s"} failed.` : `${failures.length} rule execution${failures.length === 1 ? "" : "s"} failed.`, result, {
|
|
2579
2894
|
cause: new AggregateError(failures.map((failure) => new Error(failure.message)), "Alint execution failures."),
|
|
@@ -2930,4 +3245,4 @@ function sampleCorpus(seed) {
|
|
|
2930
3245
|
].map((section) => `--- corpus section ${section} ---\n${rotated}`).join("\n\n");
|
|
2931
3246
|
}
|
|
2932
3247
|
//#endregion
|
|
2933
|
-
export { AlintAbortError, AlintRunCancelledError, AlintRunError, benchmarkModels, buildRuleRegistry, createBuiltInLanguageRegistry, createSourceExtractor, createSourceFile, createSourceRuntime, defineConfig, definePlugin, defineRule, hasDiscoveryFilePatterns, matchesDiscoveryFile, normalizeConfig, registerLanguage, resolveConfigForDirectory, resolveConfigForFile, resolveConfigForProject, resolveLanguage, resolveModel, runAlint, sliceLines, sliceRange, targetIdentity, withLanguage, withStableIdentities };
|
|
3248
|
+
export { AlintAbortError, AlintCachePersistenceError, AlintRunCancelledError, AlintRunError, benchmarkModels, buildRuleRegistry, createBuiltInLanguageRegistry, createSourceExtractor, createSourceFile, createSourceRuntime, defaultStopGateConfig, defineConfig, definePlugin, defineRule, hasDiscoveryFilePatterns, matchesDiscoveryFile, maximumStopGateTimeoutMs, normalizeConfig, registerLanguage, resolveConfigForDirectory, resolveConfigForFile, resolveConfigForProject, resolveLanguage, resolveModel, resolveStopGateConfig, runAlint, sliceLines, sliceRange, stableHash, targetIdentity, withLanguage, withStableIdentities };
|
package/dist/languages-js.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as extractJsSourceTargets } from "./extract-
|
|
1
|
+
import { t as extractJsSourceTargets } from "./extract-DhLFgRej.mjs";
|
|
2
2
|
export { extractJsSourceTargets };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { H as ResolvedModel, x as RuleContext } from "./types-Cm5Z7Fll.mjs";
|
|
2
2
|
import { GenericSchema, InferOutput } from "valibot";
|
|
3
3
|
import { JsonSchema } from "@valibot/to-json-schema";
|
|
4
4
|
import { Message } from "@xsai/shared-chat";
|
|
@@ -109,6 +109,7 @@ interface AlintConfigItem {
|
|
|
109
109
|
files?: readonly (readonly string[] | string)[];
|
|
110
110
|
ignore?: IgnoreConfig;
|
|
111
111
|
ignores?: readonly string[];
|
|
112
|
+
integrations?: IntegrationsConfig;
|
|
112
113
|
language?: string;
|
|
113
114
|
languageOptions?: Record<string, unknown>;
|
|
114
115
|
linterOptions?: AlintLinterOptions;
|
|
@@ -160,6 +161,9 @@ interface EnabledRule {
|
|
|
160
161
|
interface IgnoreConfig {
|
|
161
162
|
gitignore?: boolean;
|
|
162
163
|
}
|
|
164
|
+
interface IntegrationsConfig {
|
|
165
|
+
stopGate?: StopGateConfig;
|
|
166
|
+
}
|
|
163
167
|
interface LanguageDefinition {
|
|
164
168
|
extensions?: readonly string[];
|
|
165
169
|
extract: (file: SourceFile, context: LanguageContext) => Awaitable<SourceTarget[]>;
|
|
@@ -305,6 +309,12 @@ interface RuleWithHandler {
|
|
|
305
309
|
onTargetProject?: never;
|
|
306
310
|
onTargetWith: (target: Target) => Awaitable<void>;
|
|
307
311
|
}
|
|
312
|
+
interface StopGateConfig {
|
|
313
|
+
enabled?: boolean;
|
|
314
|
+
target?: StopGateTarget;
|
|
315
|
+
timeoutMs?: number;
|
|
316
|
+
}
|
|
317
|
+
type StopGateTarget = 'all' | 'dirty-files';
|
|
308
318
|
type Target = DirectoryTarget | PlannedSourceTarget | ProjectTarget;
|
|
309
319
|
//#endregion
|
|
310
|
-
export {
|
|
320
|
+
export { RuleSpecializedHandlers as A, ModelRequirement as B, RuleHandlers as C, RuleOptionsSchema as D, RuleOptionsOutput as E, AgentAdapter as F, ProviderDefinition as G, ResolvedModel as H, AgentRequest as I, RunnerStatsConfig as J, ProviderType as K, AgentResult as L, StopGateConfig as M, StopGateTarget as N, RuleRegistry as O, Target as P, AgentTool as R, RuleDefinition as S, RuleOptionsInput as T, ResolvedProvider as U, ResolveModelOptions as V, ModelSize as W, SetupModelDefinition as X, SetupConfig as Y, ProjectTarget as _, AlintLinterOptions as a, RuleConfigEntry as b, DiagnosticLocation as c, IgnoreConfig as d, IntegrationsConfig as f, ProjectFileEntry as g, ProcessorDefinition as h, AlintConfigItem as i, RuleWithHandler as j, RuleSeverity as k, DirectoryTarget as l, PluginDefinition as m, AlintConfigExtends as n, Awaitable as o, LanguageDefinition as p, RunnerConfig as q, AlintConfigInput as r, DiagnosticDescriptor as s, AlintConfig as t, EnabledRule as u, ProjectTargetEntry as v, RuleInferenceUsageRecord as w, RuleContext as x, RuleCacheConfig as y, AgentUsage as z };
|
package/package.json
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alint-js/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.0",
|
|
5
|
+
"description": "Public rule and plugin SDK and run engine for alint",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/moeru-ai/alint.git",
|
|
10
|
+
"directory": "packages/core"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"alint",
|
|
14
|
+
"lint",
|
|
15
|
+
"linter",
|
|
16
|
+
"ai",
|
|
17
|
+
"code-analysis",
|
|
18
|
+
"sdk"
|
|
19
|
+
],
|
|
5
20
|
"exports": {
|
|
6
21
|
".": {
|
|
7
22
|
"types": "./dist/index.d.mts",
|
|
@@ -39,9 +54,12 @@
|
|
|
39
54
|
"minimatch": "^10.2.6",
|
|
40
55
|
"oxc-parser": "^0.142.0",
|
|
41
56
|
"pathe": "^2.0.3",
|
|
42
|
-
"
|
|
57
|
+
"proper-lockfile": "4.1.2",
|
|
58
|
+
"valibot": "^1.4.2",
|
|
59
|
+
"@alint-js/utils": "0.6.0"
|
|
43
60
|
},
|
|
44
61
|
"devDependencies": {
|
|
62
|
+
"@types/proper-lockfile": "4.1.4",
|
|
45
63
|
"effect": "^3.22.1",
|
|
46
64
|
"sury": "^11.0.0-rc.0"
|
|
47
65
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { readFile } from "node:fs/promises";
|
|
2
3
|
import { extname } from "node:path";
|
|
3
4
|
import { clamp } from "es-toolkit";
|
|
4
|
-
import { createHash } from "node:crypto";
|
|
5
5
|
import { parseSync } from "oxc-parser";
|
|
6
6
|
//#region src/core/hash.ts
|
|
7
7
|
function createStableHasher() {
|