@mcp-graph-workflow/mcp-graph 5.25.0 → 5.27.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/dist/api/router.d.ts.map +1 -1
- package/dist/api/router.js +2 -0
- package/dist/api/router.js.map +1 -1
- package/dist/api/routes/code-graph.js +1 -1
- package/dist/api/routes/code-graph.js.map +1 -1
- package/dist/api/routes/translation-project.d.ts +8 -0
- package/dist/api/routes/translation-project.d.ts.map +1 -0
- package/dist/api/routes/translation-project.js +338 -0
- package/dist/api/routes/translation-project.js.map +1 -0
- package/dist/api/routes/translation.d.ts.map +1 -1
- package/dist/api/routes/translation.js +102 -0
- package/dist/api/routes/translation.js.map +1 -1
- package/dist/core/code/code-indexer.d.ts +2 -0
- package/dist/core/code/code-indexer.d.ts.map +1 -1
- package/dist/core/code/code-indexer.js +20 -4
- package/dist/core/code/code-indexer.js.map +1 -1
- package/dist/core/code/code-types.d.ts +4 -0
- package/dist/core/code/code-types.d.ts.map +1 -1
- package/dist/core/code/code-types.js +1 -0
- package/dist/core/code/code-types.js.map +1 -1
- package/dist/core/code/ts-analyzer.d.ts.map +1 -1
- package/dist/core/code/ts-analyzer.js +77 -6
- package/dist/core/code/ts-analyzer.js.map +1 -1
- package/dist/core/config/config-schema.d.ts +10 -0
- package/dist/core/config/config-schema.d.ts.map +1 -1
- package/dist/core/config/config-schema.js +4 -1
- package/dist/core/config/config-schema.js.map +1 -1
- package/dist/core/store/migrations.d.ts.map +1 -1
- package/dist/core/store/migrations.js +41 -0
- package/dist/core/store/migrations.js.map +1 -1
- package/dist/core/translation/project-translation-orchestrator.d.ts +65 -0
- package/dist/core/translation/project-translation-orchestrator.d.ts.map +1 -0
- package/dist/core/translation/project-translation-orchestrator.js +336 -0
- package/dist/core/translation/project-translation-orchestrator.js.map +1 -0
- package/dist/core/translation/translation-project-store.d.ts +40 -0
- package/dist/core/translation/translation-project-store.d.ts.map +1 -0
- package/dist/core/translation/translation-project-store.js +195 -0
- package/dist/core/translation/translation-project-store.js.map +1 -0
- package/dist/core/translation/translation-project-types.d.ts +98 -0
- package/dist/core/translation/translation-project-types.d.ts.map +1 -0
- package/dist/core/translation/translation-project-types.js +84 -0
- package/dist/core/translation/translation-project-types.js.map +1 -0
- package/dist/core/translation/zip-extractor.d.ts +4 -0
- package/dist/core/translation/zip-extractor.d.ts.map +1 -0
- package/dist/core/translation/zip-extractor.js +194 -0
- package/dist/core/translation/zip-extractor.js.map +1 -0
- package/dist/mcp/server.js +45 -22
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/reindex-knowledge.js +1 -1
- package/dist/mcp/tools/reindex-knowledge.js.map +1 -1
- package/dist/web/dashboard/dist/assets/{benchmark-tab-sjsZBd-4.js → benchmark-tab-ChRIBQcA.js} +1 -1
- package/dist/web/dashboard/dist/assets/{circle-alert-DcTbCUEa.js → circle-alert-CDZ2Oz9A.js} +1 -1
- package/dist/web/dashboard/dist/assets/{context-tab-DNMJ2Pv-.js → context-tab-ClyJEQyQ.js} +1 -1
- package/dist/web/dashboard/dist/assets/{gitnexus-tab-CZ9LJbwz.js → gitnexus-tab-uG_IXJAC.js} +1 -1
- package/dist/web/dashboard/dist/assets/{graph-section-BdcXKKoS.js → graph-section-BfAmG5lE.js} +1 -1
- package/dist/web/dashboard/dist/assets/{graph-tab-CFzux3kf.js → graph-tab-CG9ZAKno.js} +1 -1
- package/dist/web/dashboard/dist/assets/{graph-utils-_8jpWA5z.js → graph-utils-_5ZIVYpg.js} +1 -1
- package/dist/web/dashboard/dist/assets/index-BaFYAVmk.js +218 -0
- package/dist/web/dashboard/dist/assets/index-C4f-Fp0Q.css +1 -0
- package/dist/web/dashboard/dist/assets/{index-Bny20bel.js → index-CaC7cmzW.js} +1 -1
- package/dist/web/dashboard/dist/assets/{index-DSI2SYWO.js → index-jFrjjy9L.js} +1 -1
- package/dist/web/dashboard/dist/assets/{insights-tab-DUA76KUY.js → insights-tab-EnTEAW50.js} +1 -1
- package/dist/web/dashboard/dist/assets/{journey-tab-FTd6jBta.js → journey-tab-Ba2R4l8c.js} +1 -1
- package/dist/web/dashboard/dist/assets/languages-tab-BOZaQLA6.js +128 -0
- package/dist/web/dashboard/dist/assets/{loader-circle-D1DpGd2u.js → loader-circle-DIYk9wxS.js} +1 -1
- package/dist/web/dashboard/dist/assets/{logs-tab-CzL36_Qy.js → logs-tab-ZWCruIzU.js} +1 -1
- package/dist/web/dashboard/dist/assets/{lsp-tab-DdHhJq_H.js → lsp-tab-B_xnLDB4.js} +9 -19
- package/dist/web/dashboard/dist/assets/{memories-tab-CzgCahnV.js → memories-tab-EeBOo09e.js} +1 -1
- package/dist/web/dashboard/dist/assets/{prd-backlog-tab-C8Cl7swI.js → prd-backlog-tab-BByZFrjw.js} +1 -1
- package/dist/web/dashboard/dist/assets/{refresh-cw-DSYiHmbh.js → refresh-cw-Ds0I1g9D.js} +1 -1
- package/dist/web/dashboard/dist/assets/{siebel-tab-B4XOaUGz.js → siebel-tab-BndR6AA4.js} +3 -3
- package/dist/web/dashboard/dist/assets/{skills-tab-CkpnwPJY.js → skills-tab-DT6taugG.js} +1 -1
- package/dist/web/dashboard/dist/assets/{style-BOQDk6qp.js → style-C5z-qKN9.js} +1 -1
- package/dist/web/dashboard/dist/assets/triangle-alert-DA1VDEuy.js +26 -0
- package/dist/web/dashboard/dist/index.html +2 -2
- package/package.json +4 -2
- package/dist/web/dashboard/dist/assets/index-CITYTuX5.js +0 -218
- package/dist/web/dashboard/dist/assets/index-CvAguK65.css +0 -1
- package/dist/web/dashboard/dist/assets/languages-tab-B_nhFL2q.js +0 -70
- package/dist/web/dashboard/dist/assets/triangle-alert-Bf6Ly19h.js +0 -16
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Translation Orchestrator — coordinates full project translation
|
|
3
|
+
* by delegating to TranslationOrchestrator for each file.
|
|
4
|
+
*
|
|
5
|
+
* Flow: createFromZip → analyzeProject → prepareFile → (AI generates) → finalizeFile → generateDownloadZip
|
|
6
|
+
*/
|
|
7
|
+
import { TranslationOrchestrator } from "./translation-orchestrator.js";
|
|
8
|
+
import { TranslationProjectStore } from "./translation-project-store.js";
|
|
9
|
+
import { TranslationStore } from "./translation-store.js";
|
|
10
|
+
import type { TranslationProject, TranslationProjectSummary } from "./translation-project-types.js";
|
|
11
|
+
export declare class ProjectTranslationOrchestrator {
|
|
12
|
+
private readonly orchestrator;
|
|
13
|
+
private readonly projectStore;
|
|
14
|
+
private readonly translationStore;
|
|
15
|
+
constructor(orchestrator: TranslationOrchestrator, projectStore: TranslationProjectStore, translationStore: TranslationStore);
|
|
16
|
+
/**
|
|
17
|
+
* Create a translation project from a ZIP file.
|
|
18
|
+
* Extracts files, creates the project, and adds each file to the store.
|
|
19
|
+
*/
|
|
20
|
+
createFromZip(projectId: string, zipPath: string, targetLanguage: string, name?: string): TranslationProject;
|
|
21
|
+
/**
|
|
22
|
+
* Analyze all files in the project, processing in batches.
|
|
23
|
+
* Updates each file with analysis results and computes project-level confidence.
|
|
24
|
+
*/
|
|
25
|
+
analyzeProject(translationProjectId: string): TranslationProject;
|
|
26
|
+
/**
|
|
27
|
+
* Prepare a single file for translation.
|
|
28
|
+
* Validates file status, creates a translation job, and returns the prompt.
|
|
29
|
+
*/
|
|
30
|
+
prepareFile(translationProjectId: string, fileId: string): {
|
|
31
|
+
jobId: string;
|
|
32
|
+
prompt: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Finalize a translated file with the AI-generated code.
|
|
36
|
+
* Updates file status to done and recomputes project confidence.
|
|
37
|
+
*/
|
|
38
|
+
finalizeFile(translationProjectId: string, fileId: string, generatedCode: string): {
|
|
39
|
+
job: {
|
|
40
|
+
id: string;
|
|
41
|
+
status: string;
|
|
42
|
+
targetCode?: string;
|
|
43
|
+
confidenceScore?: number;
|
|
44
|
+
};
|
|
45
|
+
evidence: unknown;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Compute and return a summary of the translation project.
|
|
49
|
+
*/
|
|
50
|
+
getProjectSummary(translationProjectId: string): TranslationProjectSummary;
|
|
51
|
+
/**
|
|
52
|
+
* Generate a downloadable ZIP with translated files and prompts for untranslated ones.
|
|
53
|
+
*/
|
|
54
|
+
generateDownloadZip(translationProjectId: string): Buffer;
|
|
55
|
+
/**
|
|
56
|
+
* Determine if a file is fully deterministic based on analysis results.
|
|
57
|
+
* A file is deterministic if it has no ambiguous constructs.
|
|
58
|
+
*/
|
|
59
|
+
private isFileDeterministic;
|
|
60
|
+
/**
|
|
61
|
+
* Replace the file extension in a path.
|
|
62
|
+
*/
|
|
63
|
+
private replaceExtension;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=project-translation-orchestrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-translation-orchestrator.d.ts","sourceRoot":"","sources":["../../../src/core/translation/project-translation-orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,KAAK,EACV,kBAAkB,EAClB,yBAAyB,EAC1B,MAAM,gCAAgC,CAAC;AAsCxC,qBAAa,8BAA8B;IAEvC,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBAFhB,YAAY,EAAE,uBAAuB,EACrC,YAAY,EAAE,uBAAuB,EACrC,gBAAgB,EAAE,gBAAgB;IAGrD;;;OAGG;IACH,aAAa,CACX,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EACtB,IAAI,CAAC,EAAE,MAAM,GACZ,kBAAkB;IAsCrB;;;OAGG;IACH,cAAc,CAAC,oBAAoB,EAAE,MAAM,GAAG,kBAAkB;IAwFhE;;;OAGG;IACH,WAAW,CACT,oBAAoB,EAAE,MAAM,EAC5B,MAAM,EAAE,MAAM,GACb;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IAuCpC;;;OAGG;IACH,YAAY,CACV,oBAAoB,EAAE,MAAM,EAC5B,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,GACpB;QAAE,GAAG,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,UAAU,CAAC,EAAE,MAAM,CAAC;YAAC,eAAe,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE;IAuC5G;;OAEG;IACH,iBAAiB,CAAC,oBAAoB,EAAE,MAAM,GAAG,yBAAyB;IAmD1E;;OAEG;IACH,mBAAmB,CAAC,oBAAoB,EAAE,MAAM,GAAG,MAAM;IAuDzD;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAQ3B;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAIzB"}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Translation Orchestrator — coordinates full project translation
|
|
3
|
+
* by delegating to TranslationOrchestrator for each file.
|
|
4
|
+
*
|
|
5
|
+
* Flow: createFromZip → analyzeProject → prepareFile → (AI generates) → finalizeFile → generateDownloadZip
|
|
6
|
+
*/
|
|
7
|
+
import AdmZip from "adm-zip";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { logger } from "../utils/logger.js";
|
|
10
|
+
import { TranslationError } from "../utils/errors.js";
|
|
11
|
+
import { extractZip } from "./zip-extractor.js";
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Constants
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
const BATCH_SIZE = 10;
|
|
16
|
+
const UCR_CONFIDENCE_THRESHOLD = 0.7;
|
|
17
|
+
/**
|
|
18
|
+
* Maps source language to its canonical file extension for translated output.
|
|
19
|
+
*/
|
|
20
|
+
const LANGUAGE_TO_EXTENSION = {
|
|
21
|
+
typescript: ".ts",
|
|
22
|
+
javascript: ".js",
|
|
23
|
+
python: ".py",
|
|
24
|
+
java: ".java",
|
|
25
|
+
csharp: ".cs",
|
|
26
|
+
go: ".go",
|
|
27
|
+
rust: ".rs",
|
|
28
|
+
ruby: ".rb",
|
|
29
|
+
php: ".php",
|
|
30
|
+
swift: ".swift",
|
|
31
|
+
kotlin: ".kt",
|
|
32
|
+
scala: ".scala",
|
|
33
|
+
cpp: ".cpp",
|
|
34
|
+
lua: ".lua",
|
|
35
|
+
dart: ".dart",
|
|
36
|
+
elixir: ".ex",
|
|
37
|
+
haskell: ".hs",
|
|
38
|
+
};
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// Orchestrator
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
export class ProjectTranslationOrchestrator {
|
|
43
|
+
orchestrator;
|
|
44
|
+
projectStore;
|
|
45
|
+
translationStore;
|
|
46
|
+
constructor(orchestrator, projectStore, translationStore) {
|
|
47
|
+
this.orchestrator = orchestrator;
|
|
48
|
+
this.projectStore = projectStore;
|
|
49
|
+
this.translationStore = translationStore;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Create a translation project from a ZIP file.
|
|
53
|
+
* Extracts files, creates the project, and adds each file to the store.
|
|
54
|
+
*/
|
|
55
|
+
createFromZip(projectId, zipPath, targetLanguage, name) {
|
|
56
|
+
const files = extractZip(zipPath);
|
|
57
|
+
if (files.length === 0) {
|
|
58
|
+
throw new TranslationError("ZIP file contains no translatable source files");
|
|
59
|
+
}
|
|
60
|
+
const projectName = name ?? path.basename(zipPath, path.extname(zipPath));
|
|
61
|
+
const project = this.projectStore.createProject({
|
|
62
|
+
projectId,
|
|
63
|
+
name: projectName,
|
|
64
|
+
targetLanguage,
|
|
65
|
+
totalFiles: files.length,
|
|
66
|
+
});
|
|
67
|
+
for (const file of files) {
|
|
68
|
+
this.projectStore.addFile({
|
|
69
|
+
translationProjectId: project.id,
|
|
70
|
+
filePath: file.relativePath,
|
|
71
|
+
sourceCode: file.content,
|
|
72
|
+
sourceLanguage: file.detectedLanguage,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
this.projectStore.updateProject(project.id, { totalFiles: files.length });
|
|
76
|
+
logger.info("project-translation:createFromZip", {
|
|
77
|
+
translationProjectId: project.id,
|
|
78
|
+
projectId,
|
|
79
|
+
zipPath,
|
|
80
|
+
targetLanguage,
|
|
81
|
+
fileCount: files.length,
|
|
82
|
+
});
|
|
83
|
+
return this.projectStore.getProject(project.id);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Analyze all files in the project, processing in batches.
|
|
87
|
+
* Updates each file with analysis results and computes project-level confidence.
|
|
88
|
+
*/
|
|
89
|
+
analyzeProject(translationProjectId) {
|
|
90
|
+
const project = this.projectStore.getProject(translationProjectId);
|
|
91
|
+
if (!project) {
|
|
92
|
+
throw new TranslationError(`Translation project not found: ${translationProjectId}`);
|
|
93
|
+
}
|
|
94
|
+
const files = this.projectStore.getFiles(translationProjectId);
|
|
95
|
+
logger.info("project-translation:analyzeProject:start", {
|
|
96
|
+
translationProjectId,
|
|
97
|
+
fileCount: files.length,
|
|
98
|
+
});
|
|
99
|
+
this.projectStore.updateProject(translationProjectId, { status: "analyzing" });
|
|
100
|
+
let processedCount = 0;
|
|
101
|
+
for (let i = 0; i < files.length; i += BATCH_SIZE) {
|
|
102
|
+
const batch = files.slice(i, i + BATCH_SIZE);
|
|
103
|
+
for (const file of batch) {
|
|
104
|
+
try {
|
|
105
|
+
this.projectStore.updateFile(file.id, { status: "analyzing" });
|
|
106
|
+
const analysis = this.orchestrator.analyzeSource(file.sourceCode, { languageHint: file.sourceLanguage, targetLanguage: project.targetLanguage }, file.filePath, project.projectId);
|
|
107
|
+
const isDeterministic = this.isFileDeterministic(analysis);
|
|
108
|
+
this.projectStore.updateFile(file.id, {
|
|
109
|
+
status: "analyzed",
|
|
110
|
+
analysis: analysis,
|
|
111
|
+
confidenceScore: analysis.estimatedTranslatability,
|
|
112
|
+
deterministic: isDeterministic,
|
|
113
|
+
sourceLanguage: file.sourceLanguage ?? analysis.detectedLanguage,
|
|
114
|
+
});
|
|
115
|
+
processedCount++;
|
|
116
|
+
logger.debug("project-translation:analyzeProject:file", {
|
|
117
|
+
translationProjectId,
|
|
118
|
+
fileId: file.id,
|
|
119
|
+
filePath: file.filePath,
|
|
120
|
+
translatability: analysis.estimatedTranslatability,
|
|
121
|
+
deterministic: isDeterministic,
|
|
122
|
+
progress: `${processedCount}/${files.length}`,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
127
|
+
logger.error("project-translation:analyzeProject:fileError", {
|
|
128
|
+
translationProjectId,
|
|
129
|
+
fileId: file.id,
|
|
130
|
+
filePath: file.filePath,
|
|
131
|
+
error: message,
|
|
132
|
+
});
|
|
133
|
+
this.projectStore.updateFile(file.id, {
|
|
134
|
+
status: "failed",
|
|
135
|
+
errorMessage: message,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const confidence = this.projectStore.computeProjectConfidence(translationProjectId);
|
|
141
|
+
this.projectStore.updateProject(translationProjectId, {
|
|
142
|
+
status: "ready",
|
|
143
|
+
processedFiles: processedCount,
|
|
144
|
+
overallConfidence: confidence.overallConfidence,
|
|
145
|
+
deterministicPct: confidence.deterministicPct,
|
|
146
|
+
});
|
|
147
|
+
logger.info("project-translation:analyzeProject:done", {
|
|
148
|
+
translationProjectId,
|
|
149
|
+
processedFiles: processedCount,
|
|
150
|
+
totalFiles: files.length,
|
|
151
|
+
overallConfidence: confidence.overallConfidence,
|
|
152
|
+
deterministicPct: confidence.deterministicPct,
|
|
153
|
+
});
|
|
154
|
+
return this.projectStore.getProject(translationProjectId);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Prepare a single file for translation.
|
|
158
|
+
* Validates file status, creates a translation job, and returns the prompt.
|
|
159
|
+
*/
|
|
160
|
+
prepareFile(translationProjectId, fileId) {
|
|
161
|
+
const project = this.projectStore.getProject(translationProjectId);
|
|
162
|
+
if (!project) {
|
|
163
|
+
throw new TranslationError(`Translation project not found: ${translationProjectId}`);
|
|
164
|
+
}
|
|
165
|
+
const file = this.projectStore.getFile(fileId);
|
|
166
|
+
if (!file) {
|
|
167
|
+
throw new TranslationError(`Translation project file not found: ${fileId}`);
|
|
168
|
+
}
|
|
169
|
+
if (file.translationProjectId !== translationProjectId) {
|
|
170
|
+
throw new TranslationError(`File ${fileId} does not belong to project ${translationProjectId}`);
|
|
171
|
+
}
|
|
172
|
+
if (file.status !== "analyzed" && file.status !== "failed") {
|
|
173
|
+
throw new TranslationError(`File ${fileId} is in status '${file.status}', expected 'analyzed' or 'failed'`);
|
|
174
|
+
}
|
|
175
|
+
const result = this.orchestrator.prepareTranslation({
|
|
176
|
+
projectId: project.projectId,
|
|
177
|
+
sourceCode: file.sourceCode,
|
|
178
|
+
sourceLanguage: file.sourceLanguage,
|
|
179
|
+
targetLanguage: project.targetLanguage,
|
|
180
|
+
scope: "module",
|
|
181
|
+
});
|
|
182
|
+
this.projectStore.updateFile(fileId, {
|
|
183
|
+
status: "translating",
|
|
184
|
+
jobId: result.jobId,
|
|
185
|
+
});
|
|
186
|
+
return { jobId: result.jobId, prompt: result.prompt };
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Finalize a translated file with the AI-generated code.
|
|
190
|
+
* Updates file status to done and recomputes project confidence.
|
|
191
|
+
*/
|
|
192
|
+
finalizeFile(translationProjectId, fileId, generatedCode) {
|
|
193
|
+
const project = this.projectStore.getProject(translationProjectId);
|
|
194
|
+
if (!project) {
|
|
195
|
+
throw new TranslationError(`Translation project not found: ${translationProjectId}`);
|
|
196
|
+
}
|
|
197
|
+
const file = this.projectStore.getFile(fileId);
|
|
198
|
+
if (!file) {
|
|
199
|
+
throw new TranslationError(`Translation project file not found: ${fileId}`);
|
|
200
|
+
}
|
|
201
|
+
if (file.translationProjectId !== translationProjectId) {
|
|
202
|
+
throw new TranslationError(`File ${fileId} does not belong to project ${translationProjectId}`);
|
|
203
|
+
}
|
|
204
|
+
if (!file.jobId) {
|
|
205
|
+
throw new TranslationError(`File ${fileId} has no jobId — was prepareFile called?`);
|
|
206
|
+
}
|
|
207
|
+
const result = this.orchestrator.finalizeTranslation(file.jobId, generatedCode);
|
|
208
|
+
this.projectStore.updateFile(fileId, {
|
|
209
|
+
status: "done",
|
|
210
|
+
confidenceScore: result.evidence.confidenceScore,
|
|
211
|
+
});
|
|
212
|
+
const confidence = this.projectStore.computeProjectConfidence(translationProjectId);
|
|
213
|
+
this.projectStore.updateProject(translationProjectId, {
|
|
214
|
+
overallConfidence: confidence.overallConfidence,
|
|
215
|
+
deterministicPct: confidence.deterministicPct,
|
|
216
|
+
});
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Compute and return a summary of the translation project.
|
|
221
|
+
*/
|
|
222
|
+
getProjectSummary(translationProjectId) {
|
|
223
|
+
const project = this.projectStore.getProject(translationProjectId);
|
|
224
|
+
if (!project) {
|
|
225
|
+
throw new TranslationError(`Translation project not found: ${translationProjectId}`);
|
|
226
|
+
}
|
|
227
|
+
const files = this.projectStore.getFiles(translationProjectId);
|
|
228
|
+
let pendingFiles = 0;
|
|
229
|
+
let analyzingFiles = 0;
|
|
230
|
+
let analyzedFiles = 0;
|
|
231
|
+
let translatingFiles = 0;
|
|
232
|
+
let translatedFiles = 0;
|
|
233
|
+
let failedFiles = 0;
|
|
234
|
+
for (const file of files) {
|
|
235
|
+
switch (file.status) {
|
|
236
|
+
case "pending":
|
|
237
|
+
pendingFiles++;
|
|
238
|
+
break;
|
|
239
|
+
case "analyzing":
|
|
240
|
+
analyzingFiles++;
|
|
241
|
+
break;
|
|
242
|
+
case "analyzed":
|
|
243
|
+
analyzedFiles++;
|
|
244
|
+
break;
|
|
245
|
+
case "translating":
|
|
246
|
+
translatingFiles++;
|
|
247
|
+
break;
|
|
248
|
+
case "done":
|
|
249
|
+
translatedFiles++;
|
|
250
|
+
break;
|
|
251
|
+
case "failed":
|
|
252
|
+
failedFiles++;
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
const confidence = this.projectStore.computeProjectConfidence(translationProjectId);
|
|
257
|
+
return {
|
|
258
|
+
overallConfidence: confidence.overallConfidence,
|
|
259
|
+
deterministicPct: confidence.deterministicPct,
|
|
260
|
+
totalFiles: files.length,
|
|
261
|
+
analyzedFiles: analyzedFiles + translatingFiles + translatedFiles,
|
|
262
|
+
translatedFiles,
|
|
263
|
+
failedFiles,
|
|
264
|
+
pendingFiles: pendingFiles + analyzingFiles,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Generate a downloadable ZIP with translated files and prompts for untranslated ones.
|
|
269
|
+
*/
|
|
270
|
+
generateDownloadZip(translationProjectId) {
|
|
271
|
+
const project = this.projectStore.getProject(translationProjectId);
|
|
272
|
+
if (!project) {
|
|
273
|
+
throw new TranslationError(`Translation project not found: ${translationProjectId}`);
|
|
274
|
+
}
|
|
275
|
+
const files = this.projectStore.getFiles(translationProjectId);
|
|
276
|
+
const zip = new AdmZip();
|
|
277
|
+
logger.info("project-translation:generateDownloadZip", {
|
|
278
|
+
translationProjectId,
|
|
279
|
+
fileCount: files.length,
|
|
280
|
+
});
|
|
281
|
+
for (const file of files) {
|
|
282
|
+
if (file.status === "done" && file.jobId) {
|
|
283
|
+
const job = this.translationStore.getJob(file.jobId);
|
|
284
|
+
if (job?.targetCode) {
|
|
285
|
+
const targetExt = LANGUAGE_TO_EXTENSION[project.targetLanguage] ?? path.extname(file.filePath);
|
|
286
|
+
const targetPath = this.replaceExtension(file.filePath, targetExt);
|
|
287
|
+
zip.addFile(targetPath, Buffer.from(job.targetCode, "utf-8"));
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
// Not done — add original source + prompt sidecar
|
|
292
|
+
zip.addFile(file.filePath, Buffer.from(file.sourceCode, "utf-8"));
|
|
293
|
+
try {
|
|
294
|
+
const prepared = this.orchestrator.prepareTranslation({
|
|
295
|
+
projectId: project.projectId,
|
|
296
|
+
sourceCode: file.sourceCode,
|
|
297
|
+
sourceLanguage: file.sourceLanguage,
|
|
298
|
+
targetLanguage: project.targetLanguage,
|
|
299
|
+
scope: "module",
|
|
300
|
+
});
|
|
301
|
+
const sidecarPath = file.filePath + ".prompt.md";
|
|
302
|
+
zip.addFile(sidecarPath, Buffer.from(prepared.prompt, "utf-8"));
|
|
303
|
+
}
|
|
304
|
+
catch (err) {
|
|
305
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
306
|
+
logger.debug("project-translation:generateDownloadZip:promptError", {
|
|
307
|
+
fileId: file.id,
|
|
308
|
+
filePath: file.filePath,
|
|
309
|
+
error: message,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return zip.toBuffer();
|
|
314
|
+
}
|
|
315
|
+
// ---------------------------------------------------------------------------
|
|
316
|
+
// Private helpers
|
|
317
|
+
// ---------------------------------------------------------------------------
|
|
318
|
+
/**
|
|
319
|
+
* Determine if a file is fully deterministic based on analysis results.
|
|
320
|
+
* A file is deterministic if it has no ambiguous constructs.
|
|
321
|
+
*/
|
|
322
|
+
isFileDeterministic(analysis) {
|
|
323
|
+
if (analysis.ambiguousConstructs && analysis.ambiguousConstructs.length > 0) {
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
return analysis.estimatedTranslatability >= UCR_CONFIDENCE_THRESHOLD;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Replace the file extension in a path.
|
|
330
|
+
*/
|
|
331
|
+
replaceExtension(filePath, newExt) {
|
|
332
|
+
const parsed = path.parse(filePath);
|
|
333
|
+
return path.join(parsed.dir, parsed.name + newExt);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
//# sourceMappingURL=project-translation-orchestrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-translation-orchestrator.js","sourceRoot":"","sources":["../../../src/core/translation/project-translation-orchestrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Translation Project Store — SQLite CRUD for translation_projects and translation_project_files tables.
|
|
3
|
+
* Follows the same pattern as translation-store.ts (shared Database, prepared statements).
|
|
4
|
+
*/
|
|
5
|
+
import type Database from "better-sqlite3";
|
|
6
|
+
import type { TranslationProject, TranslationProjectStatus, TranslationProjectFile, TranslationProjectFileStatus, CreateTranslationProjectInput, AddTranslationProjectFileInput } from "./translation-project-types.js";
|
|
7
|
+
export declare class TranslationProjectStore {
|
|
8
|
+
private readonly db;
|
|
9
|
+
constructor(db: Database.Database);
|
|
10
|
+
createProject(input: CreateTranslationProjectInput): TranslationProject;
|
|
11
|
+
getProject(id: string): TranslationProject | null;
|
|
12
|
+
listProjects(projectId: string): TranslationProject[];
|
|
13
|
+
updateProject(id: string, input: Partial<{
|
|
14
|
+
name: string;
|
|
15
|
+
status: TranslationProjectStatus;
|
|
16
|
+
sourceLanguage: string;
|
|
17
|
+
totalFiles: number;
|
|
18
|
+
processedFiles: number;
|
|
19
|
+
overallConfidence: number;
|
|
20
|
+
deterministicPct: number;
|
|
21
|
+
}>): TranslationProject | null;
|
|
22
|
+
deleteProject(id: string): boolean;
|
|
23
|
+
addFile(input: AddTranslationProjectFileInput): TranslationProjectFile;
|
|
24
|
+
getFile(fileId: string): TranslationProjectFile | null;
|
|
25
|
+
getFiles(translationProjectId: string): TranslationProjectFile[];
|
|
26
|
+
updateFile(fileId: string, input: Partial<{
|
|
27
|
+
status: TranslationProjectFileStatus;
|
|
28
|
+
jobId: string;
|
|
29
|
+
deterministic: boolean;
|
|
30
|
+
analysis: Record<string, unknown>;
|
|
31
|
+
confidenceScore: number;
|
|
32
|
+
errorMessage: string;
|
|
33
|
+
sourceLanguage: string;
|
|
34
|
+
}>): TranslationProjectFile | null;
|
|
35
|
+
computeProjectConfidence(translationProjectId: string): {
|
|
36
|
+
overallConfidence: number;
|
|
37
|
+
deterministicPct: number;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=translation-project-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translation-project-store.d.ts","sourceRoot":"","sources":["../../../src/core/translation/translation-project-store.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EACV,kBAAkB,EAClB,wBAAwB,EACxB,sBAAsB,EACtB,4BAA4B,EAC5B,6BAA6B,EAC7B,8BAA8B,EAC/B,MAAM,gCAAgC,CAAC;AA4ExC,qBAAa,uBAAuB;IACtB,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,QAAQ,CAAC,QAAQ;IAElD,aAAa,CAAC,KAAK,EAAE,6BAA6B,GAAG,kBAAkB;IAsBvE,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI;IAOjD,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,kBAAkB,EAAE;IAOrD,aAAa,CACX,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,OAAO,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,wBAAwB,CAAC;QACjC,cAAc,EAAE,MAAM,CAAC;QACvB,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC,GACD,kBAAkB,GAAG,IAAI;IAqB5B,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAUlC,OAAO,CAAC,KAAK,EAAE,8BAA8B,GAAG,sBAAsB;IAqBtE,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,sBAAsB,GAAG,IAAI;IAOtD,QAAQ,CAAC,oBAAoB,EAAE,MAAM,GAAG,sBAAsB,EAAE;IAOhE,UAAU,CACR,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,CAAC;QACb,MAAM,EAAE,4BAA4B,CAAC;QACrC,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,OAAO,CAAC;QACvB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAClC,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC,GACD,sBAAsB,GAAG,IAAI;IAqBhC,wBAAwB,CAAC,oBAAoB,EAAE,MAAM,GAAG;QAAE,iBAAiB,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE;CAiChH"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Translation Project Store — SQLite CRUD for translation_projects and translation_project_files tables.
|
|
3
|
+
* Follows the same pattern as translation-store.ts (shared Database, prepared statements).
|
|
4
|
+
*/
|
|
5
|
+
import { generateId } from "../utils/id.js";
|
|
6
|
+
import { logger } from "../utils/logger.js";
|
|
7
|
+
// --- Row mappers ---
|
|
8
|
+
function rowToProject(row) {
|
|
9
|
+
return {
|
|
10
|
+
id: row.id,
|
|
11
|
+
projectId: row.project_id,
|
|
12
|
+
name: row.name,
|
|
13
|
+
...(row.source_language != null && { sourceLanguage: row.source_language }),
|
|
14
|
+
targetLanguage: row.target_language,
|
|
15
|
+
status: row.status,
|
|
16
|
+
totalFiles: row.total_files,
|
|
17
|
+
processedFiles: row.processed_files,
|
|
18
|
+
...(row.overall_confidence != null && { overallConfidence: row.overall_confidence }),
|
|
19
|
+
...(row.deterministic_pct != null && { deterministicPct: row.deterministic_pct }),
|
|
20
|
+
createdAt: row.created_at,
|
|
21
|
+
updatedAt: row.updated_at,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function rowToFile(row) {
|
|
25
|
+
return {
|
|
26
|
+
id: row.id,
|
|
27
|
+
translationProjectId: row.translation_project_id,
|
|
28
|
+
filePath: row.file_path,
|
|
29
|
+
sourceCode: row.source_code,
|
|
30
|
+
...(row.source_language != null && { sourceLanguage: row.source_language }),
|
|
31
|
+
status: row.status,
|
|
32
|
+
...(row.job_id != null && { jobId: row.job_id }),
|
|
33
|
+
...(row.deterministic != null && { deterministic: row.deterministic === 1 }),
|
|
34
|
+
...(row.analysis != null && { analysis: JSON.parse(row.analysis) }),
|
|
35
|
+
...(row.confidence_score != null && { confidenceScore: row.confidence_score }),
|
|
36
|
+
...(row.error_message != null && { errorMessage: row.error_message }),
|
|
37
|
+
createdAt: row.created_at,
|
|
38
|
+
updatedAt: row.updated_at,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// --- Store ---
|
|
42
|
+
export class TranslationProjectStore {
|
|
43
|
+
db;
|
|
44
|
+
constructor(db) {
|
|
45
|
+
this.db = db;
|
|
46
|
+
}
|
|
47
|
+
createProject(input) {
|
|
48
|
+
const id = generateId();
|
|
49
|
+
const now = new Date().toISOString();
|
|
50
|
+
this.db.prepare(`
|
|
51
|
+
INSERT INTO translation_projects (id, project_id, name, source_language, target_language, status, total_files, processed_files, created_at, updated_at)
|
|
52
|
+
VALUES (?, ?, ?, ?, ?, 'uploading', ?, 0, ?, ?)
|
|
53
|
+
`).run(id, input.projectId, input.name, input.sourceLanguage ?? null, input.targetLanguage, input.totalFiles ?? 0, now, now);
|
|
54
|
+
logger.info("translation-project:create", { projectId: id, name: input.name });
|
|
55
|
+
return this.getProject(id);
|
|
56
|
+
}
|
|
57
|
+
getProject(id) {
|
|
58
|
+
const row = this.db.prepare("SELECT * FROM translation_projects WHERE id = ?").get(id);
|
|
59
|
+
return row ? rowToProject(row) : null;
|
|
60
|
+
}
|
|
61
|
+
listProjects(projectId) {
|
|
62
|
+
const rows = this.db.prepare("SELECT * FROM translation_projects WHERE project_id = ? ORDER BY created_at DESC").all(projectId);
|
|
63
|
+
return rows.map(rowToProject);
|
|
64
|
+
}
|
|
65
|
+
updateProject(id, input) {
|
|
66
|
+
const existing = this.getProject(id);
|
|
67
|
+
if (!existing)
|
|
68
|
+
return null;
|
|
69
|
+
const sets = ["updated_at = ?"];
|
|
70
|
+
const values = [new Date().toISOString()];
|
|
71
|
+
if (input.name !== undefined) {
|
|
72
|
+
sets.push("name = ?");
|
|
73
|
+
values.push(input.name);
|
|
74
|
+
}
|
|
75
|
+
if (input.status !== undefined) {
|
|
76
|
+
sets.push("status = ?");
|
|
77
|
+
values.push(input.status);
|
|
78
|
+
}
|
|
79
|
+
if (input.sourceLanguage !== undefined) {
|
|
80
|
+
sets.push("source_language = ?");
|
|
81
|
+
values.push(input.sourceLanguage);
|
|
82
|
+
}
|
|
83
|
+
if (input.totalFiles !== undefined) {
|
|
84
|
+
sets.push("total_files = ?");
|
|
85
|
+
values.push(input.totalFiles);
|
|
86
|
+
}
|
|
87
|
+
if (input.processedFiles !== undefined) {
|
|
88
|
+
sets.push("processed_files = ?");
|
|
89
|
+
values.push(input.processedFiles);
|
|
90
|
+
}
|
|
91
|
+
if (input.overallConfidence !== undefined) {
|
|
92
|
+
sets.push("overall_confidence = ?");
|
|
93
|
+
values.push(input.overallConfidence);
|
|
94
|
+
}
|
|
95
|
+
if (input.deterministicPct !== undefined) {
|
|
96
|
+
sets.push("deterministic_pct = ?");
|
|
97
|
+
values.push(input.deterministicPct);
|
|
98
|
+
}
|
|
99
|
+
this.db.prepare(`UPDATE translation_projects SET ${sets.join(", ")} WHERE id = ?`).run(...values, id);
|
|
100
|
+
logger.debug("translation-project:update", { projectId: id, fields: Object.keys(input) });
|
|
101
|
+
return this.getProject(id);
|
|
102
|
+
}
|
|
103
|
+
deleteProject(id) {
|
|
104
|
+
// CASCADE: delete files first, then project
|
|
105
|
+
this.db.prepare("DELETE FROM translation_project_files WHERE translation_project_id = ?").run(id);
|
|
106
|
+
const result = this.db.prepare("DELETE FROM translation_projects WHERE id = ?").run(id);
|
|
107
|
+
if (result.changes > 0) {
|
|
108
|
+
logger.info("translation-project:delete", { projectId: id });
|
|
109
|
+
}
|
|
110
|
+
return result.changes > 0;
|
|
111
|
+
}
|
|
112
|
+
addFile(input) {
|
|
113
|
+
const id = generateId();
|
|
114
|
+
const now = new Date().toISOString();
|
|
115
|
+
this.db.prepare(`
|
|
116
|
+
INSERT INTO translation_project_files (id, translation_project_id, file_path, source_code, source_language, status, created_at, updated_at)
|
|
117
|
+
VALUES (?, ?, ?, ?, ?, 'pending', ?, ?)
|
|
118
|
+
`).run(id, input.translationProjectId, input.filePath, input.sourceCode, input.sourceLanguage ?? null, now, now);
|
|
119
|
+
logger.debug("translation-project:addFile", { fileId: id, filePath: input.filePath });
|
|
120
|
+
return this.getFile(id);
|
|
121
|
+
}
|
|
122
|
+
getFile(fileId) {
|
|
123
|
+
const row = this.db.prepare("SELECT * FROM translation_project_files WHERE id = ?").get(fileId);
|
|
124
|
+
return row ? rowToFile(row) : null;
|
|
125
|
+
}
|
|
126
|
+
getFiles(translationProjectId) {
|
|
127
|
+
const rows = this.db.prepare("SELECT * FROM translation_project_files WHERE translation_project_id = ? ORDER BY file_path ASC").all(translationProjectId);
|
|
128
|
+
return rows.map(rowToFile);
|
|
129
|
+
}
|
|
130
|
+
updateFile(fileId, input) {
|
|
131
|
+
const existing = this.getFile(fileId);
|
|
132
|
+
if (!existing)
|
|
133
|
+
return null;
|
|
134
|
+
const sets = ["updated_at = ?"];
|
|
135
|
+
const values = [new Date().toISOString()];
|
|
136
|
+
if (input.status !== undefined) {
|
|
137
|
+
sets.push("status = ?");
|
|
138
|
+
values.push(input.status);
|
|
139
|
+
}
|
|
140
|
+
if (input.jobId !== undefined) {
|
|
141
|
+
sets.push("job_id = ?");
|
|
142
|
+
values.push(input.jobId);
|
|
143
|
+
}
|
|
144
|
+
if (input.deterministic !== undefined) {
|
|
145
|
+
sets.push("deterministic = ?");
|
|
146
|
+
values.push(input.deterministic ? 1 : 0);
|
|
147
|
+
}
|
|
148
|
+
if (input.analysis !== undefined) {
|
|
149
|
+
sets.push("analysis = ?");
|
|
150
|
+
values.push(JSON.stringify(input.analysis));
|
|
151
|
+
}
|
|
152
|
+
if (input.confidenceScore !== undefined) {
|
|
153
|
+
sets.push("confidence_score = ?");
|
|
154
|
+
values.push(input.confidenceScore);
|
|
155
|
+
}
|
|
156
|
+
if (input.errorMessage !== undefined) {
|
|
157
|
+
sets.push("error_message = ?");
|
|
158
|
+
values.push(input.errorMessage);
|
|
159
|
+
}
|
|
160
|
+
if (input.sourceLanguage !== undefined) {
|
|
161
|
+
sets.push("source_language = ?");
|
|
162
|
+
values.push(input.sourceLanguage);
|
|
163
|
+
}
|
|
164
|
+
this.db.prepare(`UPDATE translation_project_files SET ${sets.join(", ")} WHERE id = ?`).run(...values, fileId);
|
|
165
|
+
logger.debug("translation-project:updateFile", { fileId, fields: Object.keys(input) });
|
|
166
|
+
return this.getFile(fileId);
|
|
167
|
+
}
|
|
168
|
+
computeProjectConfidence(translationProjectId) {
|
|
169
|
+
const files = this.getFiles(translationProjectId);
|
|
170
|
+
if (files.length === 0) {
|
|
171
|
+
return { overallConfidence: 0, deterministicPct: 0 };
|
|
172
|
+
}
|
|
173
|
+
let totalLoc = 0;
|
|
174
|
+
let weightedConfidence = 0;
|
|
175
|
+
let deterministicLoc = 0;
|
|
176
|
+
for (const file of files) {
|
|
177
|
+
const loc = file.sourceCode.split("\n").length;
|
|
178
|
+
totalLoc += loc;
|
|
179
|
+
if (file.confidenceScore != null) {
|
|
180
|
+
weightedConfidence += file.confidenceScore * loc;
|
|
181
|
+
}
|
|
182
|
+
if (file.deterministic === true) {
|
|
183
|
+
deterministicLoc += loc;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const overallConfidence = totalLoc > 0 ? weightedConfidence / totalLoc : 0;
|
|
187
|
+
const deterministicPct = totalLoc > 0 ? (deterministicLoc / totalLoc) * 100 : 0;
|
|
188
|
+
// Round to 4 decimal places for confidence, 2 for percentage
|
|
189
|
+
return {
|
|
190
|
+
overallConfidence: Math.round(overallConfidence * 10000) / 10000,
|
|
191
|
+
deterministicPct: Math.round(deterministicPct * 100) / 100,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=translation-project-store.js.map
|