@alint-js/core 0.3.2 → 0.5.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 CHANGED
@@ -10,7 +10,7 @@ This package provides the core SDK and run engine APIs used by plugins, rules, l
10
10
  - `runAlint`
11
11
  - rule registry and flat config normalization
12
12
  - source runtime helpers
13
- - built-in JavaScript source extraction
13
+ - built-in JavaScript source extraction, with additional language support for others
14
14
  - model resolution by size and capability
15
15
  - diagnostics and progress payload types
16
16
  - framework-neutral agent contracts under `@alint-js/core/agent`
@@ -105,6 +105,60 @@ description defaults to the schema's valibot `description(...)`. `toolParameters
105
105
  that build their own tools or prompts. Use `ctx.agent` instead when the model needs to
106
106
  explore with tools before answering, because a forced tool call is a single shot, not a loop.
107
107
 
108
+ ### Languages
109
+
110
+ Core parses JavaScript and TypeScript. Everything else is registered by a plugin, so a rule says
111
+ which languages it can read and core decides what to hand it:
112
+
113
+ ```ts
114
+ defineRule({
115
+ create: () => ({ onTargetFunction: (target) => { /* ... */ } }),
116
+ languages: 'any',
117
+ })
118
+ ```
119
+
120
+ | `languages` | the rule receives | a named language nothing registered |
121
+ | --- | --- | --- |
122
+ | omitted | file targets only, never functions or classes | not applicable |
123
+ | `'any'` | every language except `plaintext` | never fails |
124
+ | `['go', 'rust']` | those languages only | run fails, `alint/missing-language` |
125
+ | `{ ids: ['go'], skipMissing: true }` | those languages only | skipped quietly |
126
+
127
+ `'any'` excludes `plaintext` on purpose. Plain text is what a file falls back to when no language
128
+ claims its extension, so a rule that asked for a language would otherwise be handed unparsed text.
129
+ When that happens the run reports `alint/unregistered-language` once per extension — a warning by
130
+ default, configurable through `linterOptions.reportUnregisteredLanguages`.
131
+
132
+ Declaring a list is the stricter choice. It fails the run when the user has not installed a pack
133
+ that provides one of them, rather than letting the rule match nothing and look like a pass.
134
+
135
+ A plugin registers a language by describing how to turn a file into targets:
136
+
137
+ ```ts
138
+ definePlugin({
139
+ languages: {
140
+ zig: {
141
+ extensions: ['.zig'],
142
+ extract: file => [/* SourceTarget[] */],
143
+ name: 'zig',
144
+ },
145
+ },
146
+ })
147
+ ```
148
+
149
+ Ids are the identifiers editors use — `go`, `python`, `typescript`, `plaintext`. Registering fails
150
+ on a duplicate name or extension, so two plugins can never claim the same language.
151
+
152
+ Put a `FunctionInfo` under `metadata.function` on each function target, and the file's call sites
153
+ under `metadata.calls` on the file target. Those two keys are what let a rule read any language
154
+ without a parser of its own.
155
+
156
+ `@alint-js/languages` provides Go, Python and Rust this way.
157
+
158
+ To parse a file the run was not asked to lint, such as when building a workspace-wide index, use
159
+ `ctx.src.extract(path)`. It resolves that file's own config and language, and returns nothing for a
160
+ file the config ignores rather than throwing.
161
+
108
162
  ## When to use
109
163
 
110
164
  - You are writing an `alint` plugin or rule package.
package/dist/agent.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { F as AgentTool, I as AgentUsage, M as AgentAdapter, N as AgentRequest, P as AgentResult, b as RuleContext } from "./types-deBCDA4_.mjs";
1
+ import { F as AgentTool, I as AgentUsage, M as AgentAdapter, N as AgentRequest, P as AgentResult, b as RuleContext } from "./types-BK5CADYK.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 { E as RuleOptionsSchema, i as AlintConfigItem, p as PluginDefinition, t as AlintConfig, w as RuleOptionsInput, x as RuleDefinition, y as RuleConfigEntry } from "./types-deBCDA4_.mjs";
1
+ import { E as RuleOptionsSchema, i as AlintConfigItem, p as PluginDefinition, t as AlintConfig, w as RuleOptionsInput, x as RuleDefinition, y as RuleConfigEntry } from "./types-BK5CADYK.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 RuleWithHandler, C as RuleInferenceUsageRecord, D as RuleRegistry, E as RuleOptionsSchema, O as RuleSeverity, S as RuleHandlers, T as RuleOptionsOutput, _ as ProjectTargetEntry, a as AlintLinterOptions, b as RuleContext, c as DiagnosticLocation, d as IgnoreConfig, f as LanguageDefinition, g as ProjectTarget, h as ProjectFileEntry, i as AlintConfigItem, j as Target, k as RuleSpecializedHandlers, l as DirectoryTarget, m as ProcessorDefinition, n as AlintConfigExtends, o as Awaitable, p as PluginDefinition, r as AlintConfigInput, s as DiagnosticDescriptor, t as AlintConfig, u as EnabledRule, v as RuleCacheConfig, w as RuleOptionsInput, x as RuleDefinition, y as RuleConfigEntry } from "./types-deBCDA4_.mjs";
2
- import { n as definePlugin, r as defineRule, t as defineConfig } from "./define-CJ2QJPZ8.mjs";
1
+ import { A as RuleWithHandler, C as RuleInferenceUsageRecord, D as RuleRegistry, E as RuleOptionsSchema, O as RuleSeverity, S as RuleHandlers, T as RuleOptionsOutput, _ as ProjectTargetEntry, a as AlintLinterOptions, b as RuleContext, c as DiagnosticLocation, d as IgnoreConfig, f as LanguageDefinition, g as ProjectTarget, h as ProjectFileEntry, i as AlintConfigItem, j as Target, k as RuleSpecializedHandlers, l as DirectoryTarget, m as ProcessorDefinition, n as AlintConfigExtends, o as Awaitable, p as PluginDefinition, r as AlintConfigInput, s as DiagnosticDescriptor, t as AlintConfig, u as EnabledRule, v as RuleCacheConfig, w as RuleOptionsInput, x as RuleDefinition, y as RuleConfigEntry } from "./types-BK5CADYK.mjs";
2
+ import { n as definePlugin, r as defineRule, t as defineConfig } from "./define-CNfm3wZW.mjs";
3
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 };
@@ -173,11 +173,11 @@ function targetIdentity(kind, name, range) {
173
173
  /**
174
174
  * Swaps a target's positional identity for its name, where that name is unique among these targets.
175
175
  *
176
- * Identities key caches, so a function that only moved should keep the identity it had. Two targets
177
- * sharing a name cannot be told apart that way, so both keep the positional form.
176
+ * Caches are keyed by identity, so a function that only moved should keep the identity it had. Two
177
+ * targets sharing a name cannot be told apart by name, so those keep the positional form.
178
178
  *
179
- * Run every function and class target through this. Skip it in one producer and the same function
180
- * ends up with a different identity depending on which language read it.
179
+ * Run every function and class target through this. Miss it in one language and the same function
180
+ * gets a different identity depending on which language read it.
181
181
  */
182
182
  function withStableIdentities(targets) {
183
183
  const nameCounts = /* @__PURE__ */ new Map();
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { A as RuleWithHandler, B as ResolvedProvider, C as RuleInferenceUsageRecord, D as RuleRegistry, G as RunnerStatsConfig, H as ProviderDefinition, K as SetupConfig, L as ModelRequirement, O as RuleSeverity, R as ResolveModelOptions, S as RuleHandlers, U as ProviderType, V as ModelSize, W as RunnerConfig, _ as ProjectTargetEntry, a as AlintLinterOptions, b as RuleContext, c as DiagnosticLocation, d as IgnoreConfig, f as LanguageDefinition, g as ProjectTarget, h as ProjectFileEntry, i as AlintConfigItem, j as Target, k as RuleSpecializedHandlers, l as DirectoryTarget, m as ProcessorDefinition, n as AlintConfigExtends, o as Awaitable, p as PluginDefinition, q as SetupModelDefinition, r as AlintConfigInput, s as DiagnosticDescriptor, t as AlintConfig, u as EnabledRule, v as RuleCacheConfig, x as RuleDefinition, y as RuleConfigEntry, z as ResolvedModel } from "./types-deBCDA4_.mjs";
1
+ import { A as RuleWithHandler, B as ResolvedProvider, C as RuleInferenceUsageRecord, D as RuleRegistry, G as RunnerStatsConfig, H as ProviderDefinition, K as SetupConfig, L as ModelRequirement, O as RuleSeverity, R as ResolveModelOptions, S as RuleHandlers, U as ProviderType, V as ModelSize, W as RunnerConfig, _ as ProjectTargetEntry, a as AlintLinterOptions, b as RuleContext, c as DiagnosticLocation, d as IgnoreConfig, f as LanguageDefinition, g as ProjectTarget, h as ProjectFileEntry, i as AlintConfigItem, j as Target, k as RuleSpecializedHandlers, l as DirectoryTarget, m as ProcessorDefinition, n as AlintConfigExtends, o as Awaitable, p as PluginDefinition, q as SetupModelDefinition, r as AlintConfigInput, s as DiagnosticDescriptor, t as AlintConfig, u as EnabledRule, v as RuleCacheConfig, x as RuleDefinition, y as RuleConfigEntry, z as ResolvedModel } from "./types-BK5CADYK.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-CJ2QJPZ8.mjs";
3
+ import { n as definePlugin, r as defineRule, t as defineConfig } from "./define-CNfm3wZW.mjs";
4
4
  //#region src/config/config-array.d.ts
5
5
  interface EffectiveAlintConfig {
6
6
  agent?: AlintConfigItem['agent'];
@@ -286,11 +286,11 @@ declare function targetIdentity(kind: SourceTargetKind, name: string | undefined
286
286
  /**
287
287
  * Swaps a target's positional identity for its name, where that name is unique among these targets.
288
288
  *
289
- * Identities key caches, so a function that only moved should keep the identity it had. Two targets
290
- * sharing a name cannot be told apart that way, so both keep the positional form.
289
+ * Caches are keyed by identity, so a function that only moved should keep the identity it had. Two
290
+ * targets sharing a name cannot be told apart by name, so those keep the positional form.
291
291
  *
292
- * Run every function and class target through this. Skip it in one producer and the same function
293
- * ends up with a different identity depending on which language read it.
292
+ * Run every function and class target through this. Miss it in one language and the same function
293
+ * gets a different identity depending on which language read it.
294
294
  */
295
295
  declare function withStableIdentities(targets: readonly SourceTarget[]): SourceTarget[];
296
296
  //#endregion
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
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-DoHeLdG1.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-CLXRRiJW.mjs";
4
4
  import { minimatch } from "minimatch";
5
5
  import { basename, extname, isAbsolute, relative, resolve } from "pathe";
6
6
  import { access, mkdir, open, readFile, rename, rm, writeFile } from "node:fs/promises";
@@ -299,7 +299,10 @@ function resolveLanguage(file, registry, options) {
299
299
  function resolveLanguageForPath(filePath, registry, options) {
300
300
  const languageName = options.language ?? options.processedLanguage ?? registry.byExtension.get(extname$1(filePath)) ?? "plaintext";
301
301
  const language = registry.languages.get(languageName);
302
- if (!language) throw new Error(`Unknown language "${languageName}".`);
302
+ if (!language) {
303
+ const registered = [...registry.languages.keys()].sort().join(", ");
304
+ throw new Error(`Unknown language "${languageName}". Languages come from plugins, and these are registered: ${registered}.`);
305
+ }
303
306
  return language;
304
307
  }
305
308
  //#endregion
@@ -602,7 +605,7 @@ function recordMissingLanguages(into, rules, registry, path) {
602
605
  }
603
606
  //#endregion
604
607
  //#region package.json
605
- var version = "0.3.2";
608
+ var version = "0.5.0";
606
609
  //#endregion
607
610
  //#region src/core/cache/types.ts
608
611
  const CACHE_MAGIC = "ALINT_CACHE";
@@ -1,2 +1,2 @@
1
- import { t as extractJsSourceTargets } from "./extract-DoHeLdG1.mjs";
1
+ import { t as extractJsSourceTargets } from "./extract-CLXRRiJW.mjs";
2
2
  export { extractJsSourceTargets };
@@ -1,4 +1,4 @@
1
- import { b as RuleContext, z as ResolvedModel } from "./types-deBCDA4_.mjs";
1
+ import { b as RuleContext, z as ResolvedModel } from "./types-BK5CADYK.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";
@@ -152,20 +152,32 @@ function parseStructuredResponse(schema, toolName, response) {
152
152
  retriable: true
153
153
  };
154
154
  const toolResults = response.toolResults.filter((result) => result.toolName === toolName);
155
- if (toolResults.length === 0) return {
155
+ const toolCalls = response.toolCalls.filter((call) => call.toolName === toolName);
156
+ const matches = toolResults.length + toolCalls.length;
157
+ if (matches === 0) return {
156
158
  error: `Missing ${toolName} tool result; finishReason=${response.finishReason}`,
157
159
  ok: false,
158
160
  retriable: true
159
161
  };
160
- if (toolResults.length > 1) return {
161
- error: `Expected one ${toolName} tool result, received ${toolResults.length}`,
162
+ if (matches > 1) return {
163
+ error: `Expected one ${toolName} tool result, received ${matches}`,
162
164
  ok: false,
163
165
  retriable: true
164
166
  };
167
+ let value;
168
+ try {
169
+ value = toolResults[0] !== void 0 ? toolResults[0].result : JSON.parse(toolCalls[0].args);
170
+ } catch (error) {
171
+ return {
172
+ error: `Tool call failed before validation: ${errorMessageFrom(error) ?? String(error)}`,
173
+ ok: false,
174
+ retriable: true
175
+ };
176
+ }
165
177
  try {
166
178
  return {
167
179
  ok: true,
168
- value: parse(schema, toolResults[0].result)
180
+ value: parse(schema, value)
169
181
  };
170
182
  } catch (error) {
171
183
  return {
@@ -263,8 +263,10 @@ interface RuleInferenceUsageRecord {
263
263
  totalTokens?: number;
264
264
  }
265
265
  /**
266
- * - `'any'` — every registered language, and never a failure. A rule that works from `FunctionInfo`
267
- * alone wants this: a language pack the user installs later is covered without a new release.
266
+ * - `'any'` — every language except `plaintext`, and never a failure. Plain text is what a file
267
+ * falls back to when no language claims it, so a rule asking for any language is asking for a
268
+ * real one. Rules that work from `FunctionInfo` alone want this: install another language pack
269
+ * and they cover it too, unchanged.
268
270
  * - A list of language ids — `LanguageDefinition.name` values such as `go` or `typescript`, never
269
271
  * file extensions. The rule handles exactly these. Files of other languages are skipped
270
272
  * rather than failed, so one plugin can carry rules for several languages behind one `files:`
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@alint-js/core",
3
3
  "type": "module",
4
- "version": "0.3.2",
4
+ "version": "0.5.0",
5
5
  "exports": {
6
6
  ".": {
7
7
  "types": "./dist/index.d.mts",
@@ -31,10 +31,10 @@
31
31
  "dependencies": {
32
32
  "@moeru/std": "^0.1.0-beta.20",
33
33
  "@valibot/to-json-schema": "^1.7.1",
34
- "@xsai/generate-text": "^0.5.0-beta.7",
35
- "@xsai/shared-chat": "^0.5.0-beta.7",
36
- "@xsai/stream-text": "^0.5.0-beta.7",
37
- "@xsai/tool": "^0.5.0-beta.7",
34
+ "@xsai/generate-text": "^0.5.0-beta.8",
35
+ "@xsai/shared-chat": "^0.5.0-beta.8",
36
+ "@xsai/stream-text": "^0.5.0-beta.8",
37
+ "@xsai/tool": "^0.5.0-beta.8",
38
38
  "es-toolkit": "^1.50.0",
39
39
  "minimatch": "^10.2.6",
40
40
  "oxc-parser": "^0.142.0",