@adhisang/minecraft-modding-mcp 7.1.0 → 7.1.1
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/CHANGELOG.md +42 -0
- package/README.md +1 -1
- package/dist/access-transformer-parser.d.ts +8 -0
- package/dist/access-transformer-parser.js +8 -1
- package/dist/access-widener-parser.d.ts +17 -0
- package/dist/access-widener-parser.js +12 -1
- package/dist/cache-registry.js +19 -6
- package/dist/entry-tools/inspect-minecraft/handlers/versions.js +15 -9
- package/dist/entry-tools/manage-cache-service.js +10 -14
- package/dist/entry-tools/validate-project/cases/access-transformer.js +22 -3
- package/dist/entry-tools/validate-project/cases/access-widener.js +31 -4
- package/dist/entry-tools/validate-project/cases/mixin.js +11 -3
- package/dist/entry-tools/validate-project/cases/project-summary.js +24 -5
- package/dist/entry-tools/verify-mixin-target-service.js +19 -8
- package/dist/index.js +1 -0
- package/dist/java-process.d.ts +1 -0
- package/dist/java-process.js +14 -0
- package/dist/mapping/lookup.js +16 -1
- package/dist/mapping-service.d.ts +14 -0
- package/dist/mapping-service.js +35 -15
- package/dist/minecraft-explorer-service.js +70 -8
- package/dist/mixin/access-validators.js +38 -2
- package/dist/mixin/annotation-validators.js +137 -43
- package/dist/mixin/parsed-validator.js +21 -7
- package/dist/mixin-parser.d.ts +52 -0
- package/dist/mixin-parser.js +709 -130
- package/dist/mod-decompile-service.js +11 -1
- package/dist/mod-remap-service.js +6 -6
- package/dist/nbt/java-nbt-codec.js +7 -1
- package/dist/source/access-validate.js +10 -0
- package/dist/source/artifact-resolver.d.ts +13 -3
- package/dist/source/artifact-resolver.js +129 -18
- package/dist/source/class-source.js +13 -2
- package/dist/source/nested-jars.d.ts +15 -1
- package/dist/source/nested-jars.js +14 -5
- package/dist/source/search.d.ts +10 -2
- package/dist/source/search.js +60 -13
- package/dist/source/validate-mixin/pipeline/mapping-health.js +20 -1
- package/dist/source/validate-mixin/pipeline/target-lookup.js +16 -7
- package/dist/source/validate-mixin.d.ts +5 -0
- package/dist/source/validate-mixin.js +136 -21
- package/dist/source/workspace-target.js +75 -7
- package/dist/source-jar-reader.d.ts +15 -1
- package/dist/source-jar-reader.js +35 -3
- package/dist/source-resolver.js +2 -9
- package/dist/stdio-supervisor.d.ts +35 -1
- package/dist/stdio-supervisor.js +77 -2
- package/dist/storage/files-repo.d.ts +7 -0
- package/dist/storage/files-repo.js +17 -4
- package/dist/tool-contract-manifest.js +2 -2
- package/dist/tool-execution-gate.js +2 -1
- package/dist/version-service.js +7 -0
- package/dist/workspace-context-cache.d.ts +25 -0
- package/dist/workspace-context-cache.js +52 -2
- package/dist/workspace-mapping-service.js +116 -14
- package/docs/README-ja.md +1 -1
- package/docs/tool-reference.md +14 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,48 @@ and this project aims to follow [Semantic Versioning](https://semver.org/spec/v2
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [7.1.1] - 2026-09-25
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- `validate-mixin` checks each member against the targets of its own `@Mixin` class. In a file with more than one `@Mixin` class, nested or top-level, every `@Shadow`, `@Accessor`, `@Invoker` and injection was checked against every target in the file, so members were reported missing from the other classes' targets. When a target cannot be mapped or loaded, only the members of the class that names it are skipped. For files with one `@Mixin` class, this per-class checking gives the same results as before. A `@Mixin` that follows other code on the same line, such as `@Pseudo @Mixin(A.class)`, and a second `@Mixin` class on a line that starts with `@Mixin` are now recognized. `@Mixin(value = Foo.class, targets = "pkg.Bar")` checks `pkg.Bar` as well as `Foo`, the only target checked before, and a string target containing `.class`, such as `targets = "net.minecraft.class_1234"`, no longer adds a nonexistent `net.minecraft` target that was reported missing.
|
|
15
|
+
- `validate-mixin` no longer drops members because of how their annotations are written. It reads `@Shadow`, `@Inject` and the other injection annotations, `@Accessor` and `@Invoker` when another annotation precedes them on the same line (`@Final @Shadow private int foo;`, `@Environment(EnvType.CLIENT) @Inject(...)`), when their arguments wrap onto later lines, and, wherever they appear, when an argument string contains a parenthesis, such as `args = "stringValue=)"`. It also keeps a member whose declaration shares the closing line of a wrapped annotation. Such members were skipped, so a mixin with a misspelled `@Shadow` after another annotation was reported clean, their `prefix` and `aliases` were ignored, or their `method` was reported missing. `@Shadow`-like text inside a Java text block (`"""…"""`) no longer creates members, a `'"'` character literal no longer makes the comments after it misread, and annotation-like text inside a preceding annotation's string argument is still ignored.
|
|
16
|
+
- `validate-mixin` reads target-selector quantifiers the way Mixin does: `*`, `+`, `{n}`, `{n,}` and `{n,m}` count the methods of exactly that name. They matched any method whose name started with the text, so `render*` against a class with only `renderItem` was reported valid; it is now reported as a missing method. A descriptor in the selector is still checked, also in name-less selectors such as `*(D)V`. When `+` or `{n}` finds fewer methods than required, the result is an uncertain warning. Whitespace inside a quantifier (`tick{1, 3}`) is accepted, a quantifier that allows no match (`{}`, `{0}`) is flagged as malformed, one whose maximum is below its minimum (`{3,1}`) requires the minimum as in Mixin, and regex selectors (`/pattern/`) are reported as skipped rather than missing. A `method = {"tick{2}", "jump"}` array whose selectors contain braces is read instead of being reported as empty, and a `method` value that is a constant or an expression such as `"a" + "b"` gets a warning that it could not be parsed instead of a check of a fragment of it.
|
|
17
|
+
- `validate-mixin` no longer reports these target members as missing: a `@Shadow` method written with Mixin's `shadow$` prefix or an explicit `prefix = "..."`, a name listed in `aliases`, a synthetic injector target such as the lambda body `lambda$tick$0`, and a synthetic `@Shadow`, `@Accessor` or `@Invoker` member such as an enum's `$VALUES` or an inner class's `this$0`. An explicit `prefix = ""` keeps the declared name, and a `@Shadow` field keeps its declared name because Mixin allows the prefix only on methods. Synthetic and bridge methods also count toward quantified selectors, as in Mixin.
|
|
18
|
+
- `validate-mixin` no longer reports a mixin as valid because `warningCategoryFilter` hid its only error. The filter now narrows `issues`, `structuredWarnings` and the `summary` counts only: `valid`, `validationStatus` and `quickSummary` are what the same call reports without the filter, `unfilteredSummary` keeps the true counts, and the automatic retry with Maven-first mapping sources is decided as if no category filter were set. `toolHealth.overallHealthy` now reflects only the mappings the request needs: Mojang mappings for `mojang`, Tiny mappings for `yarn` and `intermediary`, and only the game jar for `obfuscated`. It reported unhealthy whenever Mojang mappings were missing, which lowered the confidence score and turned descriptor mismatches into warnings even for requests that never use them.
|
|
19
|
+
- `validate-mixin` and `validate-project` validate a nested mixin listed in a mixins config as `Outer$Inner` (or `Outer$Mid$Inner`): the class is read from `Outer.java`, and only its own members, `@Mixin` targets and priority are checked. Such an entry was reported as a missing `Outer$Inner.java`. A nested name that `Outer.java` does not declare is reported as a processing error naming the class and the file; a class declared inside a method cannot be addressed this way. Every config entry now validates only its own class, so an entry `Outer` no longer checks the nested mixins declared inside `Outer.java`, whether or not they are listed as their own entries.
|
|
20
|
+
- `validate-mixin` in config mode rejects a config that is not a JSON object, whose `mixins`, `client` or `server` is not an array of strings, or whose `package` is not a string, with `ERR_INVALID_INPUT` naming the config path and the offending key. Such configs crashed with `ERR_INTERNAL` or were silently misread.
|
|
21
|
+
- `validate-project` with `task: "mixin"` or `task: "project-summary"` reports `status: "partial"` when mixin inputs could not be loaded, counts them under `counts.processingErrors`, and says how many could not be processed (in the headline for `project-summary`). Both reported `status: "ok"`, and `task: "mixin"` said `Validated N mixin input(s).`
|
|
22
|
+
- `validate-project` with `task: "access-widener"` returns the invalid entries in its `issues` block under `detail: "standard"` or `"full"`, or with `include: ["issues"]`. The block was always omitted, so the counts said `invalid: 1` with no way to learn which line failed.
|
|
23
|
+
- `validate-access-widener` and `validate-project` with `task: "access-widener"` report a file invalid when it breaks the Fabric access-widener grammar: `mutable` on a class or method, `extendable` on a field, a `transitive-` entry under a `v1` header, an unsupported header version, a missing header, or a line before the header that is neither blank nor a comment. Such files were reported valid whenever their symbols existed. `validate-access-widener`, `validate-access-transformer` and their `validate-project` tasks also report a file invalid when it contains a line they cannot understand, naming the line; a file whose only line was malformed was reported valid with zero entries. A file with a `classTweaker` header is reported as an unsupported format rather than an invalid access widener: `ERR_INVALID_INPUT` from `validate-access-widener` and from `validate-project` with `task: "access-widener"`, and a processing error with a warning in `validate-project` `project-summary`.
|
|
24
|
+
- `verify-mixin-target` suggests `@Shadow` for a public or protected field, since mixin code needs `@Shadow` to reference any target field; it suggested `@Inject-only`, which applies only to methods. For a visible method, the reasoning says no `@Invoker` is needed and that `@Inject` hooks it while `@Shadow` calls it. The `@Shadow` example for a static field keeps `static`. A package-private target is named `package-private` in the reasoning text instead of private; `suggestedAnnotation` is unchanged. The rule matrix in `docs/tool-reference.md` lists fields and methods separately and lists package-private alongside private.
|
|
25
|
+
- Mapping lookups return only symbols of the kind that was asked for. A field whose name equals a same-class method's name, such as a record component and its accessor or a reused obfuscated name, was remapped to the method's mapped name with no warning, which reached `get-class-members`, `validate-mixin`, `validate-access-widener` and `diff-class-signatures`. A `find-mapping` field query whose name equals a class's simple name resolved to the class.
|
|
26
|
+
- A mapped method's `symbol` string and `descriptor` are now in the same namespace. After the descriptor was translated, `symbol` still embedded the source-namespace descriptor.
|
|
27
|
+
- `find-mapping` with `signatureMode: "exact"` and `resolve-method-mapping-exact` resolve a Mojang-to-Yarn method whose mappings are split across separate per-namespace files (a Mojang ProGuard file plus `official → intermediary` and `intermediary → named` Tiny files). They answered `not_found`.
|
|
28
|
+
- A pre-release Minecraft jar is no longer served as an exact match for the release it precedes: `1.21` no longer matches a `1.21-rc1` artifact and `26.1` no longer matches `26.1-pre-1`, so `strictVersion: true` refuses the substitution and the served version is flagged as approximate instead of being accepted silently.
|
|
29
|
+
- Runtime jars for Minecraft 26.1 and later are recognized by version, so `validate-access-widener`, `validate-access-transformer` and `resolve-artifact` provenance report the version actually served, with `requestedVersion` and the approximation flag, instead of echoing the requested one. A jar named `minecraft-merged-26.2-pre-6.jar` is read as its full version, `26.2-pre-6`. Neither a loader's own version number, such as Forge `47.3.0`, nor a version in the name of a directory above the build tool's own folders, such as a project checked out as `mymod-1.21`, is taken for the jar's Minecraft version; that directory name made the access-widener and access-transformer checks look up another version's mappings and report a false approximation.
|
|
30
|
+
- Workspace detection ignores commented-out Gradle lines. `resolve-workspace-symbol` and the other workspace-aware tools skip commented-out `mappings` declarations in `build.gradle` and `build.gradle.kts`, so a Yarn project that keeps a `// mappings loom.officialMojangMappings()` line is detected as Yarn instead of refused with conflicting mappings. Kotlin nested block comments, Kotlin raw strings and Groovy `$/…/$` strings are read by their own rules; text inside ordinary string literals, such as a repository URL, is still treated as code. Loader detection for workspace targets and `validate-project` no longer counts a `// id 'fabric-loom'` line or an `accessTransformer` mention inside a block comment as evidence of a loader.
|
|
31
|
+
- A workspace's detected Minecraft version, mapping and loader are refreshed as soon as `gradle.properties`, the project's root `build.gradle` or `build.gradle.kts`, or a subproject build script or mod descriptor that declared a mapping or loader changes, instead of being served from the 5-minute workspace cache. Other subproject build scripts are still picked up when the cache expires; the tool reference lists the exact files.
|
|
32
|
+
- Windows-style and WSL paths are translated on WSL in three more places. `projectPath` on `target.kind: "workspace"` and `"dependency"` targets and with `preferProjectVersion` is accepted as on the other routes; `C:\dev\mymod` on a WSL host failed to find `gradle.properties`. A `GRADLE_USER_HOME` written as `C:\...` or `\\wsl$\...` is used wherever the Gradle cache is searched; resolving a Maven coordinate from the Gradle cache and inferring a `target.kind: "dependency"` version found nothing. The recovery advice after a mapping could not be applied suggests retrying against the workspace for a Windows or UNC `projectPath`, as it already did for a Linux path, instead of generic advice.
|
|
33
|
+
- Text search (`search-class-source`, `inspect-minecraft` with `task: "search"`) finds a query whose words are full-text search operators, such as `BooleanOp.AND`. Such a query silently returned no hits, and a query like `a NOT b` quietly searched for something else.
|
|
34
|
+
- Text and path search (`search-class-source`, `inspect-minecraft` with `task: "search"`) with `scope.packagePrefix`, or with a `scope.fileGlob` that starts with a literal directory such as `net/minecraft/world/**`, finds in-scope matches even when many out-of-scope files also match; those matches could come back as no hits at all. A glob without a literal directory prefix still filters only the bounded set of candidates a search checks; the tool reference describes that limit and how to search past it. A `search-class-source` text or path search that returns fewer hits than `limit` while the index matched more files than the search checks in one pass (500 for `match: "exact"`/`"prefix"`, 1,000 by default for `"contains"`) now says so in `warnings`, suggesting `queryMode: "literal"` for text, `match: "regex"` for paths, or a narrower `packagePrefix` or literal-prefixed `fileGlob`. It returned the short page silently; the hits and their order are unchanged.
|
|
35
|
+
- `get-signature` and every tool built on it report a truncated or corrupted class file, including a bad magic number, an unsupported constant-pool tag, a class cut short inside its last attribute, or a malformed member descriptor in the bytecode, as `ERR_CLASS_NOT_FOUND` naming the class and jar. These cases answered `ERR_INTERNAL`, `ERR_INVALID_INPUT` (blaming the caller for a third-party jar's content), or parsed the truncated class as if it were complete.
|
|
36
|
+
- `get-class-members` honors `includeAnnotations: true`. The option was accepted but had no effect when called through the MCP tool, so member annotations such as `@Deprecated` were never returned.
|
|
37
|
+
- `find-class` reports `nested: true` and `enclosingClass` for an inner class looked up by a qualified name such as `a.b.Outer.Inner`, and for one found through a Jar-in-Jar shell jar. It already did so for an unqualified lookup and for a class found in the artifact's own index.
|
|
38
|
+
- An unusable file path reports `ERR_INVALID_INPUT`, naming the path and the OS error code, instead of `ERR_INTERNAL`: in `get-class-source` and `get-mod-class-source` when the `outputFile` path cannot be written because its parent directory does not exist, it is a directory, or it is not writable, and in `validate-project` with a `task` of `access-widener` or `access-transformer` and a `subject.input.mode` of `path` when the file cannot be read.
|
|
39
|
+
- Recovery suggestions now propose calls that can succeed. When a `mapping` request on a `target.kind: "jar"` source jar is refused because the jar's Minecraft version is unknown, the advice is a fillable `resolve-artifact` example with a version placeholder; it fed the jar path back as a Minecraft version. `ERR_WORKSPACE_VERSION_UNRESOLVED` carries `exampleCalls` with a `target.kind: "version"` retry to fill in; it carried no retry suggestion at all, although the tool reference said it did, and the tool reference now also names the `exampleCalls` field for `ERR_DEPENDENCY_VERSION_UNRESOLVED`. `inspect-minecraft` with `task: "versions"` omits its follow-up suggestion when the version manifest lists no release, instead of suggesting a call with no version.
|
|
40
|
+
- `manage-cache` with `action: "rebuild"` reports that it rebuilt nothing and points at `action: "delete"` instead, since deleted entries are rebuilt from source the next time they are needed. It reported `status: "changed"` and a count of rebuilt entries without rebuilding anything, and the preview offered an apply follow-up that would change nothing. The README tool table no longer lists rebuild among the cache operations.
|
|
41
|
+
- `manage-cache` with `action: "verify"` no longer reports every artifact-index entry as `in_use` while the server has its cache database open, so artifact-index entries show their real health (`healthy`, `stale`, `partial` or `orphaned`) and a `selector.status` other than `in_use` matches them, including for `delete` and `prune`. Deleting or pruning file caches no longer opens and integrity-checks the whole cache database when no artifact-index entry was selected.
|
|
42
|
+
- `remap-mod-jar` without `outputJar` returns the same output path, next to the input jar, whether or not the remapped jar was already cached. A cached result was returned from the server's internal cache directory instead.
|
|
43
|
+
- Detecting whether a mod jar uses intermediary or Mojang names no longer falls back to a low-confidence result for a jar that repeats an entry name.
|
|
44
|
+
- When the server's worker process restarts and the client re-sends `initialize`, tool calls that queued in the meantime are no longer delivered to the replacement worker ahead of the handshake. The re-sent `initialize` now goes first, and the queued calls are released once it is answered.
|
|
45
|
+
- When the server's worker process crashes or is killed from outside on Linux or macOS, the processes it started, such as a Java decompiler, are stopped with it instead of being left running. On Windows, processes started by a worker that has already exited are still not cleaned up. A Java subprocess (the decompiler and the other Java helpers) that is stopped for exceeding its time limit but ignores the stop request is now force-killed after 3 seconds; it was left running.
|
|
46
|
+
- A failed Minecraft version-manifest request releases its response body instead of holding the connection until garbage collection, and a nested (Jar-in-Jar) jar that cannot be moved into the cache after extraction no longer leaves its temporary file there.
|
|
47
|
+
|
|
48
|
+
### Documentation
|
|
49
|
+
|
|
50
|
+
- The tool reference documents a known issue: `get-class-source` with `mode: "metadata"` returns a generated outline of the class's symbols, yet reports `returnedRange` as the whole source file and, unless `maxChars` cut the outline, `truncated: false`. Do not use those values to address lines of the outline; use `mode: "snippet"` or `"full"` for line numbers. A future major release will replace them with fields specific to the metadata mode.
|
|
51
|
+
|
|
10
52
|
## [7.1.0] - 2026-09-19
|
|
11
53
|
|
|
12
54
|
### Added
|
package/README.md
CHANGED
|
@@ -279,7 +279,7 @@ Start with these top-level workflow tools unless you already know the exact spec
|
|
|
279
279
|
| `compare-minecraft` | Compare version pairs, class diffs, registry diffs, and migration-oriented summaries |
|
|
280
280
|
| `analyze-mod` | Summarize mod metadata, decompile and search mod code, inspect class source, read class members from bytecode, and preview or apply remaps |
|
|
281
281
|
| `validate-project` | Summarize workspaces and run direct Mixin, Access Widener, or Access Transformer validation |
|
|
282
|
-
| `manage-cache` | List, verify, and preview or apply cache cleanup
|
|
282
|
+
| `manage-cache` | List, verify, and preview or apply cache cleanup operations |
|
|
283
283
|
<!-- END GENERATED TOOL TABLE: top-level-workflow-tools -->
|
|
284
284
|
|
|
285
285
|
### Source Exploration
|
|
@@ -14,5 +14,13 @@ export type AccessTransformerEntry = {
|
|
|
14
14
|
export type ParsedAccessTransformer = {
|
|
15
15
|
entries: AccessTransformerEntry[];
|
|
16
16
|
parseWarnings: string[];
|
|
17
|
+
/**
|
|
18
|
+
* Count of lines the parser had to drop for malformed syntax (unsupported
|
|
19
|
+
* access declaration, incomplete entry, missing member name/descriptor). A
|
|
20
|
+
* caller-visible validator uses this to force its verdict invalid even when
|
|
21
|
+
* `entries` is empty. Optional (defaults to 0) so hand-built
|
|
22
|
+
* `ParsedAccessTransformer` fixtures that predate this field keep working.
|
|
23
|
+
*/
|
|
24
|
+
rejectedEntryCount?: number;
|
|
17
25
|
};
|
|
18
26
|
export declare function parseAccessTransformer(content: string): ParsedAccessTransformer;
|
|
@@ -50,6 +50,7 @@ export function parseAccessTransformer(content) {
|
|
|
50
50
|
const entries = [];
|
|
51
51
|
const parseWarnings = [];
|
|
52
52
|
const lines = content.split(/\r?\n/);
|
|
53
|
+
let rejectedEntryCount = 0;
|
|
53
54
|
for (let index = 0; index < lines.length; index++) {
|
|
54
55
|
const lineNumber = index + 1;
|
|
55
56
|
const rawLine = (lines[index] ?? "").trim();
|
|
@@ -63,25 +64,30 @@ export function parseAccessTransformer(content) {
|
|
|
63
64
|
const parts = withoutComment.split(/\s+/);
|
|
64
65
|
if (parts.length < 2) {
|
|
65
66
|
parseWarnings.push(`Line ${lineNumber}: Incomplete access transformer entry "${withoutComment}".`);
|
|
67
|
+
rejectedEntryCount++;
|
|
66
68
|
continue;
|
|
67
69
|
}
|
|
68
70
|
const declaration = parseAccessDeclaration(parts[0] ?? "");
|
|
69
71
|
if (!declaration) {
|
|
70
72
|
parseWarnings.push(`Line ${lineNumber}: Unsupported access declaration "${parts[0]}".`);
|
|
73
|
+
rejectedEntryCount++;
|
|
71
74
|
continue;
|
|
72
75
|
}
|
|
73
76
|
const owner = parts[1] ?? "";
|
|
74
77
|
if (!owner) {
|
|
75
78
|
parseWarnings.push(`Line ${lineNumber}: Incomplete access transformer entry "${withoutComment}".`);
|
|
79
|
+
rejectedEntryCount++;
|
|
76
80
|
continue;
|
|
77
81
|
}
|
|
78
82
|
const member = splitMemberToken(parts.slice(2));
|
|
79
83
|
if (member.targetKind === "method" && (!member.name || !member.descriptor)) {
|
|
80
84
|
parseWarnings.push(`Line ${lineNumber}: Method entry requires a method name and JVM descriptor.`);
|
|
85
|
+
rejectedEntryCount++;
|
|
81
86
|
continue;
|
|
82
87
|
}
|
|
83
88
|
if ((member.targetKind === "field" || member.targetKind === "method") && !member.name) {
|
|
84
89
|
parseWarnings.push(`Line ${lineNumber}: Member entry requires a target name.`);
|
|
90
|
+
rejectedEntryCount++;
|
|
85
91
|
continue;
|
|
86
92
|
}
|
|
87
93
|
const target = member.targetKind === "class"
|
|
@@ -100,7 +106,8 @@ export function parseAccessTransformer(content) {
|
|
|
100
106
|
}
|
|
101
107
|
return {
|
|
102
108
|
entries,
|
|
103
|
-
parseWarnings
|
|
109
|
+
parseWarnings,
|
|
110
|
+
rejectedEntryCount
|
|
104
111
|
};
|
|
105
112
|
}
|
|
106
113
|
//# sourceMappingURL=access-transformer-parser.js.map
|
|
@@ -21,5 +21,22 @@ export type ParsedAccessWidener = {
|
|
|
21
21
|
namespace: string;
|
|
22
22
|
entries: AccessWidenerEntry[];
|
|
23
23
|
parseWarnings: string[];
|
|
24
|
+
/**
|
|
25
|
+
* Count of entry lines the parser had to drop for malformed syntax (unknown
|
|
26
|
+
* kind/target kind, incomplete entry, missing descriptor), plus any
|
|
27
|
+
* non-comment line preceding the header. Excludes the header-line warnings
|
|
28
|
+
* of a file that has no header at all. A caller-visible validator
|
|
29
|
+
* uses this to force its verdict invalid even when `entries` is empty.
|
|
30
|
+
* Optional (defaults to 0) so hand-built `ParsedAccessWidener` fixtures
|
|
31
|
+
* that predate this field keep working.
|
|
32
|
+
*/
|
|
33
|
+
rejectedEntryCount?: number;
|
|
34
|
+
/**
|
|
35
|
+
* The line number the `accessWidener <version> <namespace>` header was
|
|
36
|
+
* found on, when a syntactically well-formed header line was found (set
|
|
37
|
+
* even when its version is unsupported). Undefined when no header line
|
|
38
|
+
* matched the expected format at all; callers fall back to line 1.
|
|
39
|
+
*/
|
|
40
|
+
headerLine?: number;
|
|
24
41
|
};
|
|
25
42
|
export declare function parseAccessWidener(content: string): ParsedAccessWidener;
|
|
@@ -14,6 +14,10 @@ export function parseAccessWidener(content) {
|
|
|
14
14
|
const entries = [];
|
|
15
15
|
let headerVersion = "";
|
|
16
16
|
let namespace = "";
|
|
17
|
+
let rejectedEntryCount = 0;
|
|
18
|
+
let headerLine;
|
|
19
|
+
// Fabric requires the header first, so lines before a later header are rejected once it appears.
|
|
20
|
+
let preHeaderLineCount = 0;
|
|
17
21
|
for (let i = 0; i < lines.length; i++) {
|
|
18
22
|
const raw = lines[i].trim();
|
|
19
23
|
const lineNum = i + 1;
|
|
@@ -26,9 +30,12 @@ export function parseAccessWidener(content) {
|
|
|
26
30
|
if (parts[0] === "accessWidener" && parts.length >= 3) {
|
|
27
31
|
headerVersion = parts[1];
|
|
28
32
|
namespace = parts[2];
|
|
33
|
+
headerLine = lineNum;
|
|
34
|
+
rejectedEntryCount += preHeaderLineCount;
|
|
29
35
|
}
|
|
30
36
|
else {
|
|
31
37
|
parseWarnings.push(`Line ${lineNum}: Expected accessWidener header, got: "${raw}"`);
|
|
38
|
+
preHeaderLineCount++;
|
|
32
39
|
}
|
|
33
40
|
continue;
|
|
34
41
|
}
|
|
@@ -36,6 +43,7 @@ export function parseAccessWidener(content) {
|
|
|
36
43
|
const parts = raw.split(/\s+/);
|
|
37
44
|
if (parts.length < 3) {
|
|
38
45
|
parseWarnings.push(`Line ${lineNum}: Incomplete entry: "${raw}"`);
|
|
46
|
+
rejectedEntryCount++;
|
|
39
47
|
continue;
|
|
40
48
|
}
|
|
41
49
|
const kind = parts[0];
|
|
@@ -44,10 +52,12 @@ export function parseAccessWidener(content) {
|
|
|
44
52
|
const baseKind = transitive ? kind.slice("transitive-".length) : kind;
|
|
45
53
|
if (!VALID_KINDS.has(baseKind)) {
|
|
46
54
|
parseWarnings.push(`Line ${lineNum}: Unknown access kind "${kind}".`);
|
|
55
|
+
rejectedEntryCount++;
|
|
47
56
|
continue;
|
|
48
57
|
}
|
|
49
58
|
if (!VALID_TARGET_KINDS.has(targetKind)) {
|
|
50
59
|
parseWarnings.push(`Line ${lineNum}: Unknown target kind "${targetKind}".`);
|
|
60
|
+
rejectedEntryCount++;
|
|
51
61
|
continue;
|
|
52
62
|
}
|
|
53
63
|
const validKind = baseKind;
|
|
@@ -70,11 +80,12 @@ export function parseAccessWidener(content) {
|
|
|
70
80
|
}
|
|
71
81
|
else {
|
|
72
82
|
parseWarnings.push(`Line ${lineNum}: ${validTargetKind} entry requires owner, name, and descriptor.`);
|
|
83
|
+
rejectedEntryCount++;
|
|
73
84
|
}
|
|
74
85
|
}
|
|
75
86
|
if (!headerVersion) {
|
|
76
87
|
parseWarnings.push("Missing accessWidener header.");
|
|
77
88
|
}
|
|
78
|
-
return { headerVersion, namespace, entries, parseWarnings };
|
|
89
|
+
return { headerVersion, namespace, entries, parseWarnings, rejectedEntryCount, headerLine };
|
|
79
90
|
}
|
|
80
91
|
//# sourceMappingURL=access-widener-parser.js.map
|
package/dist/cache-registry.js
CHANGED
|
@@ -414,7 +414,6 @@ async function artifactIndexEntries(config) {
|
|
|
414
414
|
ON artifact_content_bytes.artifact_id = artifacts.artifact_id
|
|
415
415
|
ORDER BY artifacts.updated_at DESC
|
|
416
416
|
`).all();
|
|
417
|
-
const dbInUse = existsSync(`${config.sqlitePath}-wal`) || existsSync(`${config.sqlitePath}-journal`);
|
|
418
417
|
return rows.map((row) => {
|
|
419
418
|
const qualityFlags = parseStringArray(row.quality_flags_json);
|
|
420
419
|
const binaryJarPath = row.binary_jar_path ?? undefined;
|
|
@@ -437,7 +436,9 @@ async function artifactIndexEntries(config) {
|
|
|
437
436
|
projectPath: inferProjectPath(binaryJarPath ?? sourceJarPath, config.pathRuntimeInfo),
|
|
438
437
|
scope: inferScope(binaryJarPath, sourceJarPath, ...qualityFlags) ?? "vanilla",
|
|
439
438
|
partial: qualityFlags.some((flag) => flag.includes("partial")),
|
|
440
|
-
|
|
439
|
+
// A WAL file only shows that a connection (usually ours) has the database open.
|
|
440
|
+
// SQLite transactions already serialize row deletes; it says nothing about this entry.
|
|
441
|
+
inUse: false
|
|
441
442
|
}
|
|
442
443
|
};
|
|
443
444
|
});
|
|
@@ -722,7 +723,11 @@ export function createCacheRegistry(config) {
|
|
|
722
723
|
downloadIdentities.set(entry.path, await readDownloadEntryIdentity(entry.path));
|
|
723
724
|
}
|
|
724
725
|
}
|
|
725
|
-
|
|
726
|
+
// Opening the index runs a PRAGMA quick_check over the whole file, so
|
|
727
|
+
// only pay for it when a row of it is actually going to be deleted.
|
|
728
|
+
const db = entries.some((entry) => entry.cacheKind === "artifact-index")
|
|
729
|
+
? openDb(config)
|
|
730
|
+
: undefined;
|
|
726
731
|
try {
|
|
727
732
|
for (const entry of entries) {
|
|
728
733
|
if (entry.cacheKind === "artifact-index") {
|
|
@@ -791,10 +796,18 @@ export function createCacheRegistry(config) {
|
|
|
791
796
|
return this.deleteEntries(input);
|
|
792
797
|
},
|
|
793
798
|
async rebuildEntries(input) {
|
|
794
|
-
|
|
799
|
+
// No cache kind knows how to rebuild itself from here: the previous
|
|
800
|
+
// implementation only counted the entries a selector matched and reported
|
|
801
|
+
// that count as "rebuilt", so apply mode claimed a change it never made.
|
|
802
|
+
// Validate the selector the way every other action does - the inventory
|
|
803
|
+
// itself would be thrown away - then say what actually happened.
|
|
804
|
+
prepareSelector(input.selector, config.pathRuntimeInfo);
|
|
805
|
+
const selectedKinds = input.cacheKinds?.length ? input.cacheKinds : [...PUBLIC_CACHE_KINDS];
|
|
795
806
|
return {
|
|
796
|
-
rebuiltEntries:
|
|
797
|
-
warnings: [
|
|
807
|
+
rebuiltEntries: 0,
|
|
808
|
+
warnings: [
|
|
809
|
+
`rebuild is not implemented for cache kind(s): ${selectedKinds.join(", ")}. Nothing was rebuilt. Delete the matching entries instead (manage-cache action="delete"); each one is rebuilt from source the next time it is needed.`
|
|
810
|
+
]
|
|
798
811
|
};
|
|
799
812
|
}
|
|
800
813
|
};
|
|
@@ -5,6 +5,10 @@ export async function handleVersions(deps, input, detail, include) {
|
|
|
5
5
|
includeSnapshots: input.includeSnapshots,
|
|
6
6
|
limit: input.limit
|
|
7
7
|
});
|
|
8
|
+
// versions.latest.release and releases[0] can both be absent for an effectively
|
|
9
|
+
// empty version manifest; omit the nextAction rather than suggest an unusable
|
|
10
|
+
// follow-up call with no version.
|
|
11
|
+
const resolvedNextVersion = versions.latest.release ?? versions.releases[0]?.id;
|
|
8
12
|
const summary = {
|
|
9
13
|
status: "ok",
|
|
10
14
|
headline: `Found ${versions.totalAvailable} Minecraft versions.`,
|
|
@@ -18,15 +22,17 @@ export async function handleVersions(deps, input, detail, include) {
|
|
|
18
22
|
releases: versions.releases.length,
|
|
19
23
|
snapshots: versions.snapshots?.length ?? 0
|
|
20
24
|
},
|
|
21
|
-
nextActions: nextActionsOrUndefined(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
nextActions: nextActionsOrUndefined(resolvedNextVersion !== undefined
|
|
26
|
+
? [
|
|
27
|
+
createNextAction("inspect-minecraft", {
|
|
28
|
+
task: "artifact",
|
|
29
|
+
subject: {
|
|
30
|
+
kind: "version",
|
|
31
|
+
version: resolvedNextVersion
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
]
|
|
35
|
+
: [])
|
|
30
36
|
};
|
|
31
37
|
return {
|
|
32
38
|
...buildEntryToolResult({
|
|
@@ -225,23 +225,19 @@ export class ManageCacheService {
|
|
|
225
225
|
include,
|
|
226
226
|
summary: {
|
|
227
227
|
status: output.rebuiltEntries > 0 && executionMode === "apply" ? "changed" : "unchanged",
|
|
228
|
-
headline
|
|
228
|
+
// Neither headline may read as work done: rebuild is not
|
|
229
|
+
// implemented for any cache kind, so apply changes nothing and
|
|
230
|
+
// preview has nothing to promise.
|
|
231
|
+
headline: executionMode === "apply"
|
|
232
|
+
? `Rebuild is not implemented for ${cacheKinds.length} cache kind(s); nothing changed.`
|
|
233
|
+
: `Previewed rebuild for ${cacheKinds.length} cache kind(s): nothing would change.`,
|
|
229
234
|
subject: summarySubject,
|
|
230
235
|
counts: {
|
|
231
236
|
rebuiltEntries: output.rebuiltEntries
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
createNextAction("manage-cache", {
|
|
237
|
-
action: "rebuild",
|
|
238
|
-
cacheKinds,
|
|
239
|
-
executionMode: "apply",
|
|
240
|
-
selector: input.selector
|
|
241
|
-
})
|
|
242
|
-
]
|
|
243
|
-
}
|
|
244
|
-
: {})
|
|
237
|
+
}
|
|
238
|
+
// No apply follow-up: rebuild is not implemented for any cache
|
|
239
|
+
// kind, so advertising one would promise a call that changes
|
|
240
|
+
// nothing. The registry's warning names what to do instead.
|
|
245
241
|
},
|
|
246
242
|
blocks: {
|
|
247
243
|
operation: {
|
|
@@ -34,9 +34,28 @@ export async function handleAccessTransformer(deps, input, detail, include) {
|
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
let content;
|
|
38
|
+
if (input.subject.input.mode === "inline") {
|
|
39
|
+
content = input.subject.input.content;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const atPath = input.subject.input.path;
|
|
43
|
+
try {
|
|
44
|
+
content = await readFile(atPath, "utf8");
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
const osCode = err?.code ?? "UNKNOWN";
|
|
48
|
+
throw createError({
|
|
49
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
50
|
+
message: `Could not read access transformer file "${atPath}": ${osCode} - ${err instanceof Error ? err.message : String(err)}`,
|
|
51
|
+
details: {
|
|
52
|
+
task: "access-transformer",
|
|
53
|
+
failedStage: "input-validation",
|
|
54
|
+
nextAction: `Verify the file exists and is readable at "${atPath}".`
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
40
59
|
if (!deps.validateAccessTransformer) {
|
|
41
60
|
throw createError({
|
|
42
61
|
code: ERROR_CODES.CONTEXT_UNRESOLVED,
|
|
@@ -34,9 +34,28 @@ export async function handleAccessWidener(deps, input, detail, include) {
|
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
let content;
|
|
38
|
+
if (input.subject.input.mode === "inline") {
|
|
39
|
+
content = input.subject.input.content;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const awPath = input.subject.input.path;
|
|
43
|
+
try {
|
|
44
|
+
content = await readFile(awPath, "utf8");
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
const osCode = err?.code ?? "UNKNOWN";
|
|
48
|
+
throw createError({
|
|
49
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
50
|
+
message: `Could not read access widener file "${awPath}": ${osCode} - ${err instanceof Error ? err.message : String(err)}`,
|
|
51
|
+
details: {
|
|
52
|
+
task: "access-widener",
|
|
53
|
+
failedStage: "input-validation",
|
|
54
|
+
nextAction: `Verify the file exists and is readable at "${awPath}".`
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
40
59
|
const output = await deps.validateAccessWidener({
|
|
41
60
|
content,
|
|
42
61
|
version: input.version,
|
|
@@ -45,6 +64,14 @@ export async function handleAccessWidener(deps, input, detail, include) {
|
|
|
45
64
|
scope: input.scope,
|
|
46
65
|
preferProjectVersion: input.preferProjectVersion
|
|
47
66
|
});
|
|
67
|
+
const issueEntries = Array.isArray(output.entries)
|
|
68
|
+
? output.entries.filter((entry) => {
|
|
69
|
+
if (!entry || typeof entry !== "object" || !("valid" in entry)) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
return entry.valid !== true;
|
|
73
|
+
})
|
|
74
|
+
: undefined;
|
|
48
75
|
return {
|
|
49
76
|
...buildEntryToolResult({
|
|
50
77
|
task: "access-widener",
|
|
@@ -76,7 +103,7 @@ export async function handleAccessWidener(deps, input, detail, include) {
|
|
|
76
103
|
invalid: output.valid ? 0 : 1
|
|
77
104
|
}
|
|
78
105
|
},
|
|
79
|
-
issues: include.includes("issues") || detail !== "summary" ?
|
|
106
|
+
issues: include.includes("issues") || detail !== "summary" ? issueEntries : undefined
|
|
80
107
|
},
|
|
81
108
|
alwaysBlocks: ["project"]
|
|
82
109
|
}),
|
|
@@ -74,14 +74,21 @@ export async function handleMixin(deps, input, detail, include) {
|
|
|
74
74
|
const summary = output.summary;
|
|
75
75
|
const invalidCount = summary?.invalid ?? 0;
|
|
76
76
|
const partialCount = summary?.partial ?? 0;
|
|
77
|
+
const processingErrorsCount = summary?.processingErrors ?? 0;
|
|
77
78
|
return {
|
|
78
79
|
...buildEntryToolResult({
|
|
79
80
|
task: "mixin",
|
|
80
81
|
detail,
|
|
81
82
|
include,
|
|
82
83
|
summary: {
|
|
83
|
-
status: invalidCount > 0
|
|
84
|
-
|
|
84
|
+
status: invalidCount > 0
|
|
85
|
+
? "invalid"
|
|
86
|
+
: partialCount > 0 || processingErrorsCount > 0
|
|
87
|
+
? "partial"
|
|
88
|
+
: "ok",
|
|
89
|
+
headline: processingErrorsCount > 0
|
|
90
|
+
? `Validated ${summary?.total ?? 0} mixin input(s); ${processingErrorsCount} could not be processed.`
|
|
91
|
+
: `Validated ${summary?.total ?? 0} mixin input(s).`,
|
|
85
92
|
subject: createSummarySubject({
|
|
86
93
|
task: "mixin",
|
|
87
94
|
kind: input.subject.kind,
|
|
@@ -94,7 +101,8 @@ export async function handleMixin(deps, input, detail, include) {
|
|
|
94
101
|
counts: {
|
|
95
102
|
valid: summary?.valid ?? 0,
|
|
96
103
|
partial: partialCount,
|
|
97
|
-
invalid: invalidCount
|
|
104
|
+
invalid: invalidCount,
|
|
105
|
+
processingErrors: processingErrorsCount
|
|
98
106
|
}
|
|
99
107
|
},
|
|
100
108
|
blocks: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { mapWithConcurrencyLimit } from "../../../concurrency.js";
|
|
3
3
|
import { buildEntryToolResult, createSummarySubject } from "../../response-contract.js";
|
|
4
|
-
import { ERROR_CODES, createError } from "../../../errors.js";
|
|
4
|
+
import { ERROR_CODES, createError, isAppError } from "../../../errors.js";
|
|
5
5
|
import { buildEarlyTasksForBlocked, buildFullTaskStatusReport } from "../internal.js";
|
|
6
6
|
// Telemetry failures must not change validation outcomes; swallow rejections
|
|
7
7
|
// so a broken emitter does not abort the summary or count as a validation error.
|
|
@@ -248,6 +248,7 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
248
248
|
let validMixins = 0;
|
|
249
249
|
let partialMixins = 0;
|
|
250
250
|
let invalidMixins = 0;
|
|
251
|
+
let mixinProcessingErrors = 0;
|
|
251
252
|
let mixinCaughtErrors = 0;
|
|
252
253
|
await safeEmit(options.stageEmitter, "validate-project:mixin-validation", {
|
|
253
254
|
targetTotal: mixinConfigs.length
|
|
@@ -296,6 +297,8 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
296
297
|
valid: summary?.valid ?? 0,
|
|
297
298
|
partial: summary?.partial ?? 0,
|
|
298
299
|
invalid: summary?.invalid ?? 0,
|
|
300
|
+
// Mixin sources that failed to load count in none of valid/partial/invalid.
|
|
301
|
+
processingErrors: summary?.processingErrors ?? 0,
|
|
299
302
|
caught: 0,
|
|
300
303
|
warnings: Array.isArray(mixinResult.warnings) ? [...mixinResult.warnings] : []
|
|
301
304
|
};
|
|
@@ -305,6 +308,7 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
305
308
|
valid: 0,
|
|
306
309
|
partial: 0,
|
|
307
310
|
invalid: 1,
|
|
311
|
+
processingErrors: 0,
|
|
308
312
|
caught: 1,
|
|
309
313
|
warnings: error instanceof Error ? [`${configPath}: ${error.message}`] : []
|
|
310
314
|
};
|
|
@@ -314,6 +318,7 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
314
318
|
validMixins += result.valid;
|
|
315
319
|
partialMixins += result.partial;
|
|
316
320
|
invalidMixins += result.invalid;
|
|
321
|
+
mixinProcessingErrors += result.processingErrors;
|
|
317
322
|
mixinCaughtErrors += result.caught;
|
|
318
323
|
warnings.push(...result.warnings);
|
|
319
324
|
}
|
|
@@ -322,6 +327,7 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
322
327
|
let validAw = 0;
|
|
323
328
|
let invalidAw = 0;
|
|
324
329
|
let awCaughtErrors = 0;
|
|
330
|
+
let awProcessingErrors = 0;
|
|
325
331
|
await safeEmit(options.stageEmitter, "validate-project:access-widener-validation", {
|
|
326
332
|
targetTotal: accessWideners.length
|
|
327
333
|
});
|
|
@@ -353,6 +359,12 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
353
359
|
}
|
|
354
360
|
}
|
|
355
361
|
catch (error) {
|
|
362
|
+
if (isAppError(error) && error.details?.unsupportedFormat === "classTweaker") {
|
|
363
|
+
// A format this server does not read is not a verdict on the file.
|
|
364
|
+
awProcessingErrors += 1;
|
|
365
|
+
warnings.push(`${awPath}: ${error.message}`);
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
356
368
|
invalidAw += 1;
|
|
357
369
|
awCaughtErrors += 1;
|
|
358
370
|
if (error instanceof Error) {
|
|
@@ -412,7 +424,12 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
412
424
|
const atDurationMs = Date.now() - atDurationStart;
|
|
413
425
|
const invalidCount = invalidMixins + invalidAw + invalidAt;
|
|
414
426
|
const partialCount = partialMixins;
|
|
415
|
-
const
|
|
427
|
+
const processingErrors = mixinProcessingErrors + awProcessingErrors;
|
|
428
|
+
const status = invalidCount > 0
|
|
429
|
+
? "invalid"
|
|
430
|
+
: partialCount > 0 || processingErrors > 0
|
|
431
|
+
? "partial"
|
|
432
|
+
: "ok";
|
|
416
433
|
const nothingToValidate = mixinConfigs.length === 0 && accessWideners.length === 0 && accessTransformers.length === 0
|
|
417
434
|
? describeNothingToValidate(discover, projectPath)
|
|
418
435
|
: undefined;
|
|
@@ -426,7 +443,7 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
426
443
|
summary: {
|
|
427
444
|
status,
|
|
428
445
|
headline: nothingToValidate?.headline ??
|
|
429
|
-
`Validated ${mixinConfigs.length} mixin config(s), ${accessWideners.length} access widener(s), and ${accessTransformers.length} access transformer(s).`,
|
|
446
|
+
`Validated ${mixinConfigs.length} mixin config(s), ${accessWideners.length} access widener(s), and ${accessTransformers.length} access transformer(s)${mixinProcessingErrors > 0 ? `; ${mixinProcessingErrors} mixin input(s) could not be processed` : ""}${awProcessingErrors > 0 ? `; ${awProcessingErrors} access widener(s) could not be processed` : ""}.`,
|
|
430
447
|
subject: createSummarySubject({
|
|
431
448
|
task: "project-summary",
|
|
432
449
|
kind: input.subject.kind,
|
|
@@ -440,7 +457,8 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
440
457
|
counts: {
|
|
441
458
|
valid: validMixins + validAw + validAt,
|
|
442
459
|
partial: partialCount,
|
|
443
|
-
invalid: invalidCount
|
|
460
|
+
invalid: invalidCount,
|
|
461
|
+
processingErrors
|
|
444
462
|
}
|
|
445
463
|
},
|
|
446
464
|
blocks: {
|
|
@@ -448,7 +466,8 @@ export async function handleProjectSummary(deps, input, detail, include, options
|
|
|
448
466
|
summary: {
|
|
449
467
|
valid: validMixins + validAw + validAt,
|
|
450
468
|
partial: partialCount,
|
|
451
|
-
invalid: invalidCount
|
|
469
|
+
invalid: invalidCount,
|
|
470
|
+
processingErrors
|
|
452
471
|
}
|
|
453
472
|
},
|
|
454
473
|
workspace: {
|