@adhisang/minecraft-modding-mcp 4.0.0 → 4.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 +61 -0
- package/README.md +40 -23
- package/dist/build-suggested-call.d.ts +29 -0
- package/dist/build-suggested-call.js +58 -0
- package/dist/cache-registry.d.ts +3 -1
- package/dist/cache-registry.js +50 -6
- package/dist/entry-tools/analyze-symbol-service.d.ts +16 -16
- package/dist/entry-tools/batch-class-members-service.d.ts +34 -0
- package/dist/entry-tools/batch-class-members-service.js +97 -0
- package/dist/entry-tools/batch-class-source-service.d.ts +37 -0
- package/dist/entry-tools/batch-class-source-service.js +100 -0
- package/dist/entry-tools/batch-mappings-service.d.ts +36 -0
- package/dist/entry-tools/batch-mappings-service.js +66 -0
- package/dist/entry-tools/batch-runner.d.ts +72 -0
- package/dist/entry-tools/batch-runner.js +90 -0
- package/dist/entry-tools/batch-symbol-exists-service.d.ts +46 -0
- package/dist/entry-tools/batch-symbol-exists-service.js +113 -0
- package/dist/entry-tools/compare-minecraft-service.d.ts +6 -6
- package/dist/entry-tools/inspect-minecraft/handlers/artifact.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/artifact.js +83 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-members.d.ts +6 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +80 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-overview.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-overview.js +248 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-source.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-source.js +60 -0
- package/dist/entry-tools/inspect-minecraft/handlers/file.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/file.js +54 -0
- package/dist/entry-tools/inspect-minecraft/handlers/list-files.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/list-files.js +100 -0
- package/dist/entry-tools/inspect-minecraft/handlers/search.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/search.js +155 -0
- package/dist/entry-tools/inspect-minecraft/handlers/versions.d.ts +6 -0
- package/dist/entry-tools/inspect-minecraft/handlers/versions.js +49 -0
- package/dist/entry-tools/inspect-minecraft/internal.d.ts +1042 -0
- package/dist/entry-tools/inspect-minecraft/internal.js +448 -0
- package/dist/entry-tools/inspect-minecraft-service.d.ts +193 -308
- package/dist/entry-tools/inspect-minecraft-service.js +20 -1244
- package/dist/entry-tools/manage-cache-service.d.ts +16 -16
- package/dist/entry-tools/validate-project/cases/access-transformer.d.ts +6 -0
- package/dist/entry-tools/validate-project/cases/access-transformer.js +106 -0
- package/dist/entry-tools/validate-project/cases/access-widener.d.ts +6 -0
- package/dist/entry-tools/validate-project/cases/access-widener.js +86 -0
- package/dist/entry-tools/validate-project/cases/mixin.d.ts +6 -0
- package/dist/entry-tools/validate-project/cases/mixin.js +90 -0
- package/dist/entry-tools/validate-project/cases/project-summary.d.ts +102 -0
- package/dist/entry-tools/validate-project/cases/project-summary.js +415 -0
- package/dist/entry-tools/validate-project/internal.d.ts +142 -0
- package/dist/entry-tools/validate-project/internal.js +303 -0
- package/dist/entry-tools/validate-project-service.d.ts +67 -47
- package/dist/entry-tools/validate-project-service.js +13 -563
- package/dist/entry-tools/verify-mixin-target-service.d.ts +133 -0
- package/dist/entry-tools/verify-mixin-target-service.js +323 -0
- package/dist/error-mapping.d.ts +40 -0
- package/dist/error-mapping.js +139 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +6 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +147 -1354
- package/dist/mapping/internal-types.d.ts +54 -0
- package/dist/mapping/internal-types.js +14 -0
- package/dist/mapping/loaders/mojang.d.ts +2 -0
- package/dist/mapping/loaders/mojang.js +64 -0
- package/dist/mapping/loaders/tiny-loom.d.ts +2 -0
- package/dist/mapping/loaders/tiny-loom.js +73 -0
- package/dist/mapping/loaders/tiny-maven.d.ts +2 -0
- package/dist/mapping/loaders/tiny-maven.js +104 -0
- package/dist/mapping/loaders/types.d.ts +14 -0
- package/dist/mapping/loaders/types.js +2 -0
- package/dist/mapping/lookup.d.ts +52 -0
- package/dist/mapping/lookup.js +496 -0
- package/dist/mapping/parsers/normalize.d.ts +10 -0
- package/dist/mapping/parsers/normalize.js +52 -0
- package/dist/mapping/parsers/proguard.d.ts +20 -0
- package/dist/mapping/parsers/proguard.js +138 -0
- package/dist/mapping/parsers/symbol-records.d.ts +27 -0
- package/dist/mapping/parsers/symbol-records.js +216 -0
- package/dist/mapping/parsers/tiny.d.ts +9 -0
- package/dist/mapping/parsers/tiny.js +96 -0
- package/dist/mapping/types.d.ts +147 -0
- package/dist/mapping/types.js +2 -0
- package/dist/mapping-pipeline-service.js +3 -2
- package/dist/mapping-service.d.ts +8 -145
- package/dist/mapping-service.js +30 -1207
- package/dist/mixin/access-validators.d.ts +9 -0
- package/dist/mixin/access-validators.js +257 -0
- package/dist/mixin/annotation-validators.d.ts +5 -0
- package/dist/mixin/annotation-validators.js +162 -0
- package/dist/mixin/helpers.d.ts +28 -0
- package/dist/mixin/helpers.js +315 -0
- package/dist/mixin/parsed-validator.d.ts +8 -0
- package/dist/mixin/parsed-validator.js +337 -0
- package/dist/mixin/types.d.ts +208 -0
- package/dist/mixin/types.js +28 -0
- package/dist/mixin-validator.d.ts +9 -201
- package/dist/mixin-validator.js +8 -1020
- package/dist/source/access-validate.d.ts +4 -0
- package/dist/source/access-validate.js +254 -0
- package/dist/source/artifact-resolver.d.ts +111 -0
- package/dist/source/artifact-resolver.js +1271 -0
- package/dist/source/cache-metrics.d.ts +26 -0
- package/dist/source/cache-metrics.js +172 -0
- package/dist/source/class-source/members-builder.d.ts +34 -0
- package/dist/source/class-source/members-builder.js +46 -0
- package/dist/source/class-source/snippet-builder.d.ts +19 -0
- package/dist/source/class-source/snippet-builder.js +46 -0
- package/dist/source/class-source-helpers.d.ts +34 -0
- package/dist/source/class-source-helpers.js +140 -0
- package/dist/source/class-source.d.ts +42 -0
- package/dist/source/class-source.js +883 -0
- package/dist/source/descriptor-utils.d.ts +6 -0
- package/dist/source/descriptor-utils.js +37 -0
- package/dist/source/file-access.d.ts +4 -0
- package/dist/source/file-access.js +102 -0
- package/dist/source/indexer.d.ts +82 -0
- package/dist/source/indexer.js +522 -0
- package/dist/source/lifecycle/diff-utils.d.ts +9 -0
- package/dist/source/lifecycle/diff-utils.js +107 -0
- package/dist/source/lifecycle/diff.d.ts +2 -0
- package/dist/source/lifecycle/diff.js +265 -0
- package/dist/source/lifecycle/mapping-helpers.d.ts +22 -0
- package/dist/source/lifecycle/mapping-helpers.js +327 -0
- package/dist/source/lifecycle/runtime-check.d.ts +2 -0
- package/dist/source/lifecycle/runtime-check.js +142 -0
- package/dist/source/lifecycle/trace.d.ts +2 -0
- package/dist/source/lifecycle/trace.js +231 -0
- package/dist/source/lifecycle.d.ts +4 -0
- package/dist/source/lifecycle.js +5 -0
- package/dist/source/search.d.ts +51 -0
- package/dist/source/search.js +676 -0
- package/dist/source/shared-utils.d.ts +6 -0
- package/dist/source/shared-utils.js +55 -0
- package/dist/source/state.d.ts +26 -0
- package/dist/source/state.js +24 -0
- package/dist/source/symbol-resolver.d.ts +3 -0
- package/dist/source/symbol-resolver.js +212 -0
- package/dist/source/validate-mixin/pipeline/mapping-health.d.ts +3 -0
- package/dist/source/validate-mixin/pipeline/mapping-health.js +41 -0
- package/dist/source/validate-mixin/pipeline/parse.d.ts +2 -0
- package/dist/source/validate-mixin/pipeline/parse.js +10 -0
- package/dist/source/validate-mixin/pipeline/resolve.d.ts +3 -0
- package/dist/source/validate-mixin/pipeline/resolve.js +78 -0
- package/dist/source/validate-mixin/pipeline/target-lookup.d.ts +6 -0
- package/dist/source/validate-mixin/pipeline/target-lookup.js +260 -0
- package/dist/source/validate-mixin/pipeline-context.d.ts +72 -0
- package/dist/source/validate-mixin/pipeline-context.js +93 -0
- package/dist/source/validate-mixin.d.ts +22 -0
- package/dist/source/validate-mixin.js +799 -0
- package/dist/source/workspace-target.d.ts +18 -0
- package/dist/source/workspace-target.js +305 -0
- package/dist/source-resolver.d.ts +1 -0
- package/dist/source-resolver.js +1 -1
- package/dist/source-service.d.ts +164 -170
- package/dist/source-service.js +70 -6116
- package/dist/stage-emitter.d.ts +13 -0
- package/dist/stage-emitter.js +30 -0
- package/dist/stdio-supervisor.d.ts +61 -0
- package/dist/stdio-supervisor.js +326 -9
- package/dist/tool-contract-manifest.d.ts +1 -1
- package/dist/tool-contract-manifest.js +23 -6
- package/dist/tool-guidance.d.ts +82 -0
- package/dist/tool-guidance.js +734 -0
- package/dist/tool-schema-registry.d.ts +16 -0
- package/dist/tool-schema-registry.js +37 -0
- package/dist/tool-schemas.d.ts +3518 -0
- package/dist/tool-schemas.js +813 -0
- package/dist/types.d.ts +36 -0
- package/dist/version-service.js +7 -6
- package/dist/workspace-context-cache.d.ts +32 -0
- package/dist/workspace-context-cache.js +66 -0
- package/dist/workspace-mapping-service.d.ts +16 -0
- package/dist/workspace-mapping-service.js +173 -1
- package/docs/README-ja.md +416 -0
- package/docs/examples.md +483 -0
- package/docs/tool-reference.md +462 -0
- package/package.json +17 -4
package/dist/types.d.ts
CHANGED
|
@@ -10,6 +10,19 @@ export interface SourceTargetInput {
|
|
|
10
10
|
kind: ArtifactTargetKind;
|
|
11
11
|
value: string;
|
|
12
12
|
}
|
|
13
|
+
export type WorkspaceTargetInput = {
|
|
14
|
+
kind: "workspace";
|
|
15
|
+
scope?: ArtifactScope;
|
|
16
|
+
strict?: boolean;
|
|
17
|
+
};
|
|
18
|
+
export type DependencyTargetInput = {
|
|
19
|
+
kind: "dependency";
|
|
20
|
+
group: string;
|
|
21
|
+
name: string;
|
|
22
|
+
version?: string;
|
|
23
|
+
versionFromProject?: boolean;
|
|
24
|
+
};
|
|
25
|
+
export type ResolveArtifactTargetInput = SourceTargetInput | WorkspaceTargetInput | DependencyTargetInput;
|
|
13
26
|
export interface ResolvedSourceArtifact {
|
|
14
27
|
artifactId: string;
|
|
15
28
|
artifactAlias?: string;
|
|
@@ -28,6 +41,26 @@ export interface ResolvedSourceArtifact {
|
|
|
28
41
|
isDecompiled: boolean;
|
|
29
42
|
resolvedAt: string;
|
|
30
43
|
}
|
|
44
|
+
export interface WorkspaceResolutionProvenance {
|
|
45
|
+
projectPath: string;
|
|
46
|
+
detected: {
|
|
47
|
+
minecraftVersion?: string;
|
|
48
|
+
compileMapping?: SourceMapping;
|
|
49
|
+
loader?: string;
|
|
50
|
+
};
|
|
51
|
+
source: string;
|
|
52
|
+
cacheHit: boolean;
|
|
53
|
+
warnings?: string[];
|
|
54
|
+
}
|
|
55
|
+
export interface DependencyResolutionProvenance {
|
|
56
|
+
group: string;
|
|
57
|
+
name: string;
|
|
58
|
+
resolvedVersion?: string;
|
|
59
|
+
source: string;
|
|
60
|
+
candidatesSeen?: string[];
|
|
61
|
+
attempts?: string[];
|
|
62
|
+
cacheHit: boolean;
|
|
63
|
+
}
|
|
31
64
|
export interface ArtifactProvenance {
|
|
32
65
|
target: SourceTargetInput;
|
|
33
66
|
resolvedAt: string;
|
|
@@ -40,6 +73,9 @@ export interface ArtifactProvenance {
|
|
|
40
73
|
repoUrl?: string;
|
|
41
74
|
};
|
|
42
75
|
transformChain: string[];
|
|
76
|
+
workspaceResolution?: WorkspaceResolutionProvenance;
|
|
77
|
+
dependencyResolution?: DependencyResolutionProvenance;
|
|
78
|
+
warnings?: string[];
|
|
43
79
|
}
|
|
44
80
|
export interface RuntimeValidationProvenance<TMapping extends RuntimeValidationNamespace = RuntimeValidationNamespace> {
|
|
45
81
|
version: string;
|
package/dist/version-service.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
+
import { buildSuggestedCall } from "./build-suggested-call.js";
|
|
4
5
|
import { createError, ERROR_CODES } from "./errors.js";
|
|
5
6
|
import { computeFileSha1 } from "./hash.js";
|
|
6
7
|
import { defaultDownloadPath, downloadToCache } from "./repo-downloader.js";
|
|
@@ -24,7 +25,7 @@ function ensureVersionDetail(value, version) {
|
|
|
24
25
|
details: {
|
|
25
26
|
version,
|
|
26
27
|
nextAction: "Use list-versions to see available Minecraft versions.",
|
|
27
|
-
|
|
28
|
+
...buildSuggestedCall({ tool: "list-versions", params: {} })
|
|
28
29
|
}
|
|
29
30
|
});
|
|
30
31
|
}
|
|
@@ -112,7 +113,7 @@ export class VersionService {
|
|
|
112
113
|
details: {
|
|
113
114
|
version: normalizedVersion,
|
|
114
115
|
nextAction: "Use list-versions to see available Minecraft versions.",
|
|
115
|
-
|
|
116
|
+
...buildSuggestedCall({ tool: "list-versions", params: {} })
|
|
116
117
|
}
|
|
117
118
|
});
|
|
118
119
|
}
|
|
@@ -144,7 +145,7 @@ export class VersionService {
|
|
|
144
145
|
details: {
|
|
145
146
|
version: normalizedVersion,
|
|
146
147
|
nextAction: "Use list-versions to see available Minecraft versions.",
|
|
147
|
-
|
|
148
|
+
...buildSuggestedCall({ tool: "list-versions", params: {} })
|
|
148
149
|
}
|
|
149
150
|
});
|
|
150
151
|
}
|
|
@@ -157,7 +158,7 @@ export class VersionService {
|
|
|
157
158
|
details: {
|
|
158
159
|
version: normalizedVersion,
|
|
159
160
|
nextAction: "Use list-versions to see available Minecraft versions.",
|
|
160
|
-
|
|
161
|
+
...buildSuggestedCall({ tool: "list-versions", params: {} })
|
|
161
162
|
}
|
|
162
163
|
});
|
|
163
164
|
}
|
|
@@ -249,7 +250,7 @@ export class VersionService {
|
|
|
249
250
|
details: {
|
|
250
251
|
version: normalizedVersion,
|
|
251
252
|
nextAction: "Use list-versions to see available Minecraft versions.",
|
|
252
|
-
|
|
253
|
+
...buildSuggestedCall({ tool: "list-versions", params: {} })
|
|
253
254
|
}
|
|
254
255
|
});
|
|
255
256
|
}
|
|
@@ -262,7 +263,7 @@ export class VersionService {
|
|
|
262
263
|
details: {
|
|
263
264
|
version: normalizedVersion,
|
|
264
265
|
nextAction: "Use list-versions to see available Minecraft versions.",
|
|
265
|
-
|
|
266
|
+
...buildSuggestedCall({ tool: "list-versions", params: {} })
|
|
266
267
|
}
|
|
267
268
|
});
|
|
268
269
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { SourceMapping } from "./types.js";
|
|
2
|
+
import type { WorkspaceProjectLoader } from "./workspace-mapping-service.js";
|
|
3
|
+
export type WorkspaceContextEvidence = {
|
|
4
|
+
source: string;
|
|
5
|
+
field: string;
|
|
6
|
+
value?: string;
|
|
7
|
+
};
|
|
8
|
+
export type WorkspaceContext = {
|
|
9
|
+
projectPath: string;
|
|
10
|
+
minecraftVersion?: string;
|
|
11
|
+
compileMapping?: SourceMapping;
|
|
12
|
+
loader?: WorkspaceProjectLoader;
|
|
13
|
+
detectedAt: number;
|
|
14
|
+
evidence: WorkspaceContextEvidence[];
|
|
15
|
+
dependencyVersions: Map<string, string>;
|
|
16
|
+
partial?: boolean;
|
|
17
|
+
};
|
|
18
|
+
export interface WorkspaceContextCache {
|
|
19
|
+
read(projectPath: string): WorkspaceContext | undefined;
|
|
20
|
+
write(ctx: WorkspaceContext): void;
|
|
21
|
+
invalidate(projectPath: string): boolean;
|
|
22
|
+
list(): WorkspaceContext[];
|
|
23
|
+
clear(): void;
|
|
24
|
+
}
|
|
25
|
+
export type WorkspaceContextCacheOptions = {
|
|
26
|
+
maxEntries?: number;
|
|
27
|
+
ttlMs?: number;
|
|
28
|
+
clock?: () => number;
|
|
29
|
+
};
|
|
30
|
+
export declare function createWorkspaceContextCache(opts?: WorkspaceContextCacheOptions): WorkspaceContextCache;
|
|
31
|
+
export declare function getProcessWorkspaceContextCache(): WorkspaceContextCache;
|
|
32
|
+
export declare function resetProcessWorkspaceContextCacheForTesting(): void;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { resolve as resolvePath } from "node:path";
|
|
2
|
+
import { LruList } from "./lru-list.js";
|
|
3
|
+
const DEFAULT_MAX_ENTRIES = 16;
|
|
4
|
+
const DEFAULT_TTL_MS = 5 * 60_000;
|
|
5
|
+
function normalizeKey(projectPath) {
|
|
6
|
+
return resolvePath(projectPath);
|
|
7
|
+
}
|
|
8
|
+
export function createWorkspaceContextCache(opts = {}) {
|
|
9
|
+
const maxEntries = opts.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
10
|
+
const ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;
|
|
11
|
+
const clock = opts.clock ?? (() => Date.now());
|
|
12
|
+
const lru = new LruList();
|
|
13
|
+
function isExpired(ctx) {
|
|
14
|
+
return clock() - ctx.detectedAt > ttlMs;
|
|
15
|
+
}
|
|
16
|
+
function evictExcess() {
|
|
17
|
+
while (lru.size > maxEntries) {
|
|
18
|
+
const oldest = lru.peekOldest();
|
|
19
|
+
if (!oldest) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
lru.remove(oldest.key);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
read(projectPath) {
|
|
27
|
+
const key = normalizeKey(projectPath);
|
|
28
|
+
const value = lru.touch(key);
|
|
29
|
+
if (!value) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
if (isExpired(value)) {
|
|
33
|
+
lru.remove(key);
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
},
|
|
38
|
+
write(ctx) {
|
|
39
|
+
const key = normalizeKey(ctx.projectPath);
|
|
40
|
+
lru.upsert(key, { ...ctx, projectPath: key });
|
|
41
|
+
evictExcess();
|
|
42
|
+
},
|
|
43
|
+
invalidate(projectPath) {
|
|
44
|
+
const key = normalizeKey(projectPath);
|
|
45
|
+
return lru.remove(key) !== undefined;
|
|
46
|
+
},
|
|
47
|
+
list() {
|
|
48
|
+
const all = lru.toArray().map((entry) => entry.value);
|
|
49
|
+
return all.filter((value) => !isExpired(value));
|
|
50
|
+
},
|
|
51
|
+
clear() {
|
|
52
|
+
lru.clear();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
let processCache;
|
|
57
|
+
export function getProcessWorkspaceContextCache() {
|
|
58
|
+
if (!processCache) {
|
|
59
|
+
processCache = createWorkspaceContextCache();
|
|
60
|
+
}
|
|
61
|
+
return processCache;
|
|
62
|
+
}
|
|
63
|
+
export function resetProcessWorkspaceContextCacheForTesting() {
|
|
64
|
+
processCache = undefined;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=workspace-context-cache.js.map
|
|
@@ -25,8 +25,24 @@ export type WorkspaceProjectLoaderOutput = {
|
|
|
25
25
|
evidence: WorkspaceLoaderEvidence[];
|
|
26
26
|
warnings: string[];
|
|
27
27
|
};
|
|
28
|
+
export type DependencyVersionResolution = {
|
|
29
|
+
resolved: true;
|
|
30
|
+
version: string;
|
|
31
|
+
source: string;
|
|
32
|
+
candidatesSeen: string[];
|
|
33
|
+
attempts: string[];
|
|
34
|
+
} | {
|
|
35
|
+
resolved: false;
|
|
36
|
+
candidatesSeen: string[];
|
|
37
|
+
attempts: string[];
|
|
38
|
+
};
|
|
39
|
+
export type DependencyVersionOptions = {
|
|
40
|
+
includeSnapshots?: boolean;
|
|
41
|
+
};
|
|
42
|
+
export declare function isSafeMavenVersionToken(token: string): boolean;
|
|
28
43
|
export declare class WorkspaceMappingService {
|
|
29
44
|
detectCompileMapping(input: WorkspaceCompileMappingInput): Promise<WorkspaceCompileMappingOutput>;
|
|
30
45
|
detectProjectMinecraftVersion(projectPath: string): Promise<string | undefined>;
|
|
46
|
+
detectDependencyVersion(projectPath: string, group: string, name: string, opts?: DependencyVersionOptions): Promise<DependencyVersionResolution>;
|
|
31
47
|
detectProjectLoader(projectPath: string): Promise<WorkspaceProjectLoaderOutput>;
|
|
32
48
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
2
3
|
import { resolve } from "node:path";
|
|
3
4
|
import fastGlob from "fast-glob";
|
|
4
5
|
import { mapWithConcurrencyLimit } from "./concurrency.js";
|
|
@@ -38,6 +39,93 @@ function detectMappingsFromContent(content) {
|
|
|
38
39
|
}
|
|
39
40
|
return detections;
|
|
40
41
|
}
|
|
42
|
+
function camelCaseDependencyName(name) {
|
|
43
|
+
const parts = name.split(/[-_]/).filter((part) => part.length > 0);
|
|
44
|
+
if (parts.length === 0) {
|
|
45
|
+
return name;
|
|
46
|
+
}
|
|
47
|
+
const [first, ...rest] = parts;
|
|
48
|
+
const head = first ?? "";
|
|
49
|
+
return (head +
|
|
50
|
+
rest
|
|
51
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
52
|
+
.join(""));
|
|
53
|
+
}
|
|
54
|
+
function lastGroupSegment(group) {
|
|
55
|
+
const segments = group.split(".").filter((segment) => segment.length > 0);
|
|
56
|
+
return segments[segments.length - 1] ?? group;
|
|
57
|
+
}
|
|
58
|
+
function buildDependencyPropertyKeys(group, name) {
|
|
59
|
+
const camelName = camelCaseDependencyName(name);
|
|
60
|
+
const groupSegment = lastGroupSegment(group);
|
|
61
|
+
const camelGroupName = camelCaseDependencyName(`${groupSegment}_${name}`);
|
|
62
|
+
const keys = [
|
|
63
|
+
`${name}_version`,
|
|
64
|
+
`${camelName}Version`,
|
|
65
|
+
`${groupSegment}_${name}_version`,
|
|
66
|
+
`${camelGroupName}Version`
|
|
67
|
+
];
|
|
68
|
+
const seen = new Set();
|
|
69
|
+
const deduped = [];
|
|
70
|
+
for (const key of keys) {
|
|
71
|
+
if (!seen.has(key)) {
|
|
72
|
+
seen.add(key);
|
|
73
|
+
deduped.push(key);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return deduped;
|
|
77
|
+
}
|
|
78
|
+
function readPropertyValue(content, key) {
|
|
79
|
+
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
80
|
+
const pattern = new RegExp(`^\\s*${escaped}\\s*=\\s*(.+?)\\s*$`, "m");
|
|
81
|
+
const match = content.match(pattern);
|
|
82
|
+
if (!match) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
const value = match[1]?.trim();
|
|
86
|
+
return value && value.length > 0 ? value : undefined;
|
|
87
|
+
}
|
|
88
|
+
function compareSemverDescending(left, right) {
|
|
89
|
+
const leftParts = left.split(/[.+-]/);
|
|
90
|
+
const rightParts = right.split(/[.+-]/);
|
|
91
|
+
const length = Math.max(leftParts.length, rightParts.length);
|
|
92
|
+
for (let index = 0; index < length; index += 1) {
|
|
93
|
+
const leftPart = leftParts[index] ?? "0";
|
|
94
|
+
const rightPart = rightParts[index] ?? "0";
|
|
95
|
+
const leftNum = /^\d+$/.test(leftPart) ? Number.parseInt(leftPart, 10) : Number.NaN;
|
|
96
|
+
const rightNum = /^\d+$/.test(rightPart) ? Number.parseInt(rightPart, 10) : Number.NaN;
|
|
97
|
+
if (!Number.isNaN(leftNum) && !Number.isNaN(rightNum)) {
|
|
98
|
+
if (leftNum !== rightNum) {
|
|
99
|
+
return rightNum - leftNum;
|
|
100
|
+
}
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (leftPart !== rightPart) {
|
|
104
|
+
return rightPart.localeCompare(leftPart);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
function isPathTraversalToken(token) {
|
|
110
|
+
return token.length === 0 || token.includes("/") || token.includes("\\") || token.includes("..") || token.includes("\0");
|
|
111
|
+
}
|
|
112
|
+
const SAFE_VERSION_TOKEN_RE = /^[A-Za-z0-9._+-]+$/;
|
|
113
|
+
export function isSafeMavenVersionToken(token) {
|
|
114
|
+
if (typeof token !== "string" || token.length === 0 || token.length > 200) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
if (token.startsWith(".") || token.includes("..")) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
return SAFE_VERSION_TOKEN_RE.test(token);
|
|
121
|
+
}
|
|
122
|
+
function resolveGradleUserHome() {
|
|
123
|
+
const configured = process.env.GRADLE_USER_HOME?.trim();
|
|
124
|
+
if (configured) {
|
|
125
|
+
return configured;
|
|
126
|
+
}
|
|
127
|
+
return resolve(homedir(), ".gradle");
|
|
128
|
+
}
|
|
41
129
|
function detectLoadersFromContent(content) {
|
|
42
130
|
const detections = [];
|
|
43
131
|
if (/\bid\s*(?:\(\s*)?["']net\.neoforged\.moddev["']\s*\)?/i.test(content)) {
|
|
@@ -159,6 +247,90 @@ export class WorkspaceMappingService {
|
|
|
159
247
|
}
|
|
160
248
|
return undefined;
|
|
161
249
|
}
|
|
250
|
+
async detectDependencyVersion(projectPath, group, name, opts) {
|
|
251
|
+
if (isPathTraversalToken(group) || isPathTraversalToken(name)) {
|
|
252
|
+
throw createError({
|
|
253
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
254
|
+
message: "Dependency group and name must not contain path traversal characters.",
|
|
255
|
+
details: { group, name }
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
const includeSnapshots = opts?.includeSnapshots === true;
|
|
259
|
+
const attempts = [];
|
|
260
|
+
const candidatesSeen = [];
|
|
261
|
+
const root = resolve(projectPath);
|
|
262
|
+
const propsPath = resolve(root, "gradle.properties");
|
|
263
|
+
let propsContent;
|
|
264
|
+
try {
|
|
265
|
+
propsContent = await readFile(propsPath, "utf8");
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
propsContent = undefined;
|
|
269
|
+
}
|
|
270
|
+
const keys = buildDependencyPropertyKeys(group, name);
|
|
271
|
+
if (propsContent !== undefined) {
|
|
272
|
+
for (const key of keys) {
|
|
273
|
+
attempts.push(`gradle.properties:${key}`);
|
|
274
|
+
const value = readPropertyValue(propsContent, key);
|
|
275
|
+
if (value) {
|
|
276
|
+
if (!isSafeMavenVersionToken(value)) {
|
|
277
|
+
attempts.push(`gradle.properties:${key}:rejected-unsafe-version`);
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
resolved: true,
|
|
282
|
+
version: value,
|
|
283
|
+
source: `gradle.properties:${key}`,
|
|
284
|
+
candidatesSeen,
|
|
285
|
+
attempts
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
for (const key of keys) {
|
|
292
|
+
attempts.push(`gradle.properties:${key}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
const modulesDir = resolve(resolveGradleUserHome(), "caches", "modules-2", "files-2.1", group, name);
|
|
296
|
+
attempts.push(`modules-2:${modulesDir}`);
|
|
297
|
+
let entries = [];
|
|
298
|
+
try {
|
|
299
|
+
entries = await readdir(modulesDir);
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
return { resolved: false, candidatesSeen, attempts };
|
|
303
|
+
}
|
|
304
|
+
const filtered = entries.filter((entry) => {
|
|
305
|
+
if (!isSafeMavenVersionToken(entry)) {
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
if (includeSnapshots) {
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
const lower = entry.toLowerCase();
|
|
312
|
+
return !lower.endsWith("-snapshot") && !lower.endsWith("-dev");
|
|
313
|
+
});
|
|
314
|
+
const sorted = [...filtered].sort(compareSemverDescending);
|
|
315
|
+
candidatesSeen.push(...sorted);
|
|
316
|
+
if (sorted.length === 0) {
|
|
317
|
+
return { resolved: false, candidatesSeen, attempts };
|
|
318
|
+
}
|
|
319
|
+
if (sorted.length > 1) {
|
|
320
|
+
return { resolved: false, candidatesSeen, attempts };
|
|
321
|
+
}
|
|
322
|
+
const chosen = sorted[0];
|
|
323
|
+
if (!chosen) {
|
|
324
|
+
return { resolved: false, candidatesSeen, attempts };
|
|
325
|
+
}
|
|
326
|
+
return {
|
|
327
|
+
resolved: true,
|
|
328
|
+
version: chosen,
|
|
329
|
+
source: `modules-2:${modulesDir}`,
|
|
330
|
+
candidatesSeen,
|
|
331
|
+
attempts
|
|
332
|
+
};
|
|
333
|
+
}
|
|
162
334
|
async detectProjectLoader(projectPath) {
|
|
163
335
|
const root = resolve(projectPath);
|
|
164
336
|
const buildFiles = (await fastGlob.glob(["build.gradle", "build.gradle.kts", "**/build.gradle", "**/build.gradle.kts"], {
|