gtx-cli 2.5.0-alpha.0 → 2.5.0-alpha.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.
Files changed (93) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/api/collectUserEditDiffs.d.ts +2 -7
  3. package/dist/api/collectUserEditDiffs.js +33 -78
  4. package/dist/api/downloadFileBatch.d.ts +11 -10
  5. package/dist/api/downloadFileBatch.js +120 -127
  6. package/dist/api/saveLocalEdits.js +18 -15
  7. package/dist/cli/base.js +1 -1
  8. package/dist/cli/commands/stage.d.ts +8 -2
  9. package/dist/cli/commands/stage.js +25 -7
  10. package/dist/cli/commands/translate.d.ts +4 -2
  11. package/dist/cli/commands/translate.js +5 -6
  12. package/dist/cli/flags.js +4 -1
  13. package/dist/config/generateSettings.js +10 -0
  14. package/dist/console/colors.d.ts +0 -1
  15. package/dist/console/colors.js +0 -3
  16. package/dist/console/index.d.ts +0 -6
  17. package/dist/console/index.js +2 -13
  18. package/dist/console/logging.d.ts +1 -1
  19. package/dist/console/logging.js +3 -4
  20. package/dist/formats/files/translate.d.ts +2 -2
  21. package/dist/formats/files/translate.js +31 -5
  22. package/dist/fs/config/downloadedVersions.d.ts +10 -3
  23. package/dist/fs/config/downloadedVersions.js +8 -0
  24. package/dist/fs/config/updateVersions.d.ts +2 -1
  25. package/dist/git/branches.d.ts +7 -0
  26. package/dist/git/branches.js +88 -0
  27. package/dist/react/{jsx/utils/jsxParsing → data-_gt}/addGTIdentifierToSyntaxTree.d.ts +1 -2
  28. package/dist/react/{jsx/utils/jsxParsing → data-_gt}/addGTIdentifierToSyntaxTree.js +6 -30
  29. package/dist/react/jsx/evaluateJsx.d.ts +6 -5
  30. package/dist/react/jsx/evaluateJsx.js +4 -32
  31. package/dist/react/jsx/trimJsxStringChildren.d.ts +7 -0
  32. package/dist/react/jsx/trimJsxStringChildren.js +122 -0
  33. package/dist/react/jsx/utils/constants.d.ts +0 -2
  34. package/dist/react/jsx/utils/constants.js +2 -11
  35. package/dist/react/jsx/utils/parseJsx.d.ts +21 -0
  36. package/dist/react/jsx/utils/parseJsx.js +259 -0
  37. package/dist/react/jsx/utils/parseStringFunction.js +141 -4
  38. package/dist/react/parse/createInlineUpdates.js +70 -19
  39. package/dist/types/branch.d.ts +14 -0
  40. package/dist/types/branch.js +1 -0
  41. package/dist/types/data.d.ts +1 -1
  42. package/dist/types/files.d.ts +7 -0
  43. package/dist/types/index.d.ts +7 -0
  44. package/dist/utils/SpinnerManager.d.ts +30 -0
  45. package/dist/utils/SpinnerManager.js +73 -0
  46. package/dist/utils/gitDiff.js +18 -16
  47. package/dist/workflow/BranchStep.d.ts +13 -0
  48. package/dist/workflow/BranchStep.js +131 -0
  49. package/dist/workflow/DownloadStep.d.ts +19 -0
  50. package/dist/workflow/DownloadStep.js +127 -0
  51. package/dist/workflow/EnqueueStep.d.ts +15 -0
  52. package/dist/workflow/EnqueueStep.js +33 -0
  53. package/dist/workflow/PollJobsStep.d.ts +31 -0
  54. package/dist/workflow/PollJobsStep.js +284 -0
  55. package/dist/workflow/SetupStep.d.ts +16 -0
  56. package/dist/workflow/SetupStep.js +71 -0
  57. package/dist/workflow/UploadStep.d.ts +21 -0
  58. package/dist/workflow/UploadStep.js +72 -0
  59. package/dist/workflow/UserEditDiffsStep.d.ts +11 -0
  60. package/dist/workflow/UserEditDiffsStep.js +30 -0
  61. package/dist/workflow/Workflow.d.ts +4 -0
  62. package/dist/workflow/Workflow.js +2 -0
  63. package/dist/workflow/download.d.ts +22 -0
  64. package/dist/workflow/download.js +104 -0
  65. package/dist/workflow/stage.d.ts +14 -0
  66. package/dist/workflow/stage.js +76 -0
  67. package/package.json +4 -5
  68. package/dist/api/checkFileTranslations.d.ts +0 -23
  69. package/dist/api/checkFileTranslations.js +0 -281
  70. package/dist/api/sendFiles.d.ts +0 -17
  71. package/dist/api/sendFiles.js +0 -127
  72. package/dist/api/sendUserEdits.d.ts +0 -19
  73. package/dist/api/sendUserEdits.js +0 -15
  74. package/dist/cli/commands/edits.d.ts +0 -8
  75. package/dist/cli/commands/edits.js +0 -32
  76. package/dist/react/jsx/utils/buildImportMap.d.ts +0 -9
  77. package/dist/react/jsx/utils/buildImportMap.js +0 -30
  78. package/dist/react/jsx/utils/getPathsAndAliases.d.ts +0 -17
  79. package/dist/react/jsx/utils/getPathsAndAliases.js +0 -89
  80. package/dist/react/jsx/utils/jsxParsing/handleChildrenWhitespace.d.ts +0 -6
  81. package/dist/react/jsx/utils/jsxParsing/handleChildrenWhitespace.js +0 -199
  82. package/dist/react/jsx/utils/jsxParsing/multiplication/findMultiplicationNode.d.ts +0 -13
  83. package/dist/react/jsx/utils/jsxParsing/multiplication/findMultiplicationNode.js +0 -42
  84. package/dist/react/jsx/utils/jsxParsing/multiplication/multiplyJsxTree.d.ts +0 -5
  85. package/dist/react/jsx/utils/jsxParsing/multiplication/multiplyJsxTree.js +0 -69
  86. package/dist/react/jsx/utils/jsxParsing/parseJsx.d.ts +0 -60
  87. package/dist/react/jsx/utils/jsxParsing/parseJsx.js +0 -949
  88. package/dist/react/jsx/utils/jsxParsing/parseTProps.d.ts +0 -8
  89. package/dist/react/jsx/utils/jsxParsing/parseTProps.js +0 -47
  90. package/dist/react/jsx/utils/jsxParsing/types.d.ts +0 -48
  91. package/dist/react/jsx/utils/jsxParsing/types.js +0 -34
  92. package/dist/react/jsx/utils/resolveImportPath.d.ts +0 -11
  93. package/dist/react/jsx/utils/resolveImportPath.js +0 -111
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # gtx-cli
2
2
 
3
+ ## 2.4.15
4
+
5
+ ### Patch Changes
6
+
7
+ - [#792](https://github.com/generaltranslation/gt/pull/792) [`b6d6869`](https://github.com/generaltranslation/gt/commit/b6d686917316f6ed44130a54509459a7f9ee35fa) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Skipping over JSON and YAML files with failing parse to avoid crashing
8
+
9
+ ## 2.4.14
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies [[`3da05a1`](https://github.com/generaltranslation/gt/commit/3da05a12a37a62ace3c7e321aa2fed5a4af52ad9)]:
14
+ - generaltranslation@7.9.1
15
+
3
16
  ## 2.4.13
4
17
 
5
18
  ### Patch Changes
@@ -1,14 +1,9 @@
1
1
  import { Settings } from '../types/index.js';
2
- type UploadedFileRef = {
3
- fileId: string;
4
- versionId: string;
5
- fileName: string;
6
- };
2
+ import { FileReference } from 'generaltranslation/types';
7
3
  /**
8
4
  * Collects local user edits by diffing the latest downloaded server translation version
9
5
  * against the current local translation file, and submits the diffs upstream.
10
6
  *
11
7
  * Must run before enqueueing new translations so rules are available to the generator.
12
8
  */
13
- export declare function collectAndSendUserEditDiffs(uploadedFiles: UploadedFileRef[], settings: Settings): Promise<void>;
14
- export {};
9
+ export declare function collectAndSendUserEditDiffs(files: FileReference[], settings: Settings): Promise<void>;
@@ -3,48 +3,41 @@ import * as path from 'node:path';
3
3
  import { getDownloadedVersions } from '../fs/config/downloadedVersions.js';
4
4
  import { createFileMapping } from '../formats/files/fileMapping.js';
5
5
  import { getGitUnifiedDiff } from '../utils/gitDiff.js';
6
- import { sendUserEditDiffs } from './sendUserEdits.js';
7
6
  import { gt } from '../utils/gt.js';
8
- const MAX_DIFF_BATCH_BYTES = 1_500_000;
9
- const MAX_DOWNLOAD_BATCH = 100;
7
+ import os from 'node:os';
8
+ import { randomUUID } from 'node:crypto';
10
9
  /**
11
10
  * Collects local user edits by diffing the latest downloaded server translation version
12
11
  * against the current local translation file, and submits the diffs upstream.
13
12
  *
14
13
  * Must run before enqueueing new translations so rules are available to the generator.
15
14
  */
16
- export async function collectAndSendUserEditDiffs(uploadedFiles, settings) {
15
+ export async function collectAndSendUserEditDiffs(files, settings) {
17
16
  if (!settings.files)
18
17
  return;
19
18
  const { resolvedPaths, placeholderPaths, transformPaths } = settings.files;
20
19
  const fileMapping = createFileMapping(resolvedPaths, placeholderPaths, transformPaths, settings.locales, settings.defaultLocale);
21
20
  const downloadedVersions = getDownloadedVersions(settings.configDirectory);
22
- const tempDir = path.join(settings.configDirectory, 'tmp');
21
+ const tempDir = path.join(os.tmpdir(), randomUUID());
23
22
  if (!fs.existsSync(tempDir))
24
23
  fs.mkdirSync(tempDir, { recursive: true });
25
24
  const candidates = [];
26
- for (const uploadedFile of uploadedFiles) {
25
+ for (const uploadedFile of files) {
27
26
  for (const locale of settings.locales) {
28
- const resolvedLocale = gt.resolveAliasLocale(locale);
29
27
  const outputPath = fileMapping[locale]?.[uploadedFile.fileName] ?? null;
30
28
  if (!outputPath)
31
29
  continue;
32
30
  if (!fs.existsSync(outputPath))
33
31
  continue;
34
- const lockKeyById = uploadedFile.fileId
35
- ? `${uploadedFile.fileId}:${resolvedLocale}`
36
- : null;
37
- const lockKeyByName = `${uploadedFile.fileName}:${resolvedLocale}`;
38
- const lockEntry = (lockKeyById && downloadedVersions.entries[lockKeyById]) ||
39
- downloadedVersions.entries[lockKeyByName];
40
- const versionId = lockEntry?.versionId;
41
- if (!versionId)
32
+ const downloadedVersion = downloadedVersions.entries?.[uploadedFile.branchId]?.[uploadedFile.fileId]?.[uploadedFile.versionId]?.[locale];
33
+ if (!downloadedVersion)
42
34
  continue;
43
35
  candidates.push({
36
+ branchId: uploadedFile.branchId,
44
37
  fileName: uploadedFile.fileName,
45
38
  fileId: uploadedFile.fileId,
46
- versionId,
47
- locale: resolvedLocale,
39
+ versionId: uploadedFile.versionId,
40
+ locale: locale,
48
41
  outputPath,
49
42
  });
50
43
  }
@@ -53,59 +46,39 @@ export async function collectAndSendUserEditDiffs(uploadedFiles, settings) {
53
46
  if (candidates.length > 0) {
54
47
  const fileQueryData = candidates.map((c) => ({
55
48
  versionId: c.versionId,
56
- fileName: c.fileName,
57
49
  locale: c.locale,
50
+ fileId: c.fileId,
51
+ branchId: c.branchId,
58
52
  }));
59
53
  // Single batched check to obtain translation IDs
60
- const checkResponse = await gt.checkFileTranslations(fileQueryData);
61
- const translations = (checkResponse?.translations || []).filter((t) => t && t.isReady && t.id && t.fileName && t.locale);
62
- // Map fileName:resolvedLocale -> translationId
63
- const idByKey = new Map();
64
- for (const t of translations) {
65
- const resolved = gt.resolveAliasLocale(t.locale);
66
- idByKey.set(`${t.fileName}:${resolved}`, t.id);
67
- }
68
- // Collect translation IDs in batches and download contents
69
- const ids = [];
70
- for (const c of candidates) {
71
- const id = idByKey.get(`${c.fileName}:${c.locale}`);
72
- if (id)
73
- ids.push(id);
74
- }
75
- // Helper to chunk array
76
- function chunk(arr, size) {
77
- const res = [];
78
- for (let i = 0; i < arr.length; i += size)
79
- res.push(arr.slice(i, i + size));
80
- return res;
81
- }
54
+ const checkResponse = await gt.queryFileData({
55
+ translatedFiles: fileQueryData,
56
+ });
57
+ const translatedFiles = checkResponse.translatedFiles?.filter((t) => t.completedAt) ?? [];
82
58
  const serverContentByKey = new Map();
83
- for (const idChunk of chunk(ids, MAX_DOWNLOAD_BATCH)) {
84
- try {
85
- const resp = await gt.downloadFileBatch(idChunk);
86
- const files = resp?.files || [];
87
- for (const f of files) {
88
- // Find corresponding candidate key via idByKey reverse lookup
89
- for (const [key, id] of idByKey.entries()) {
90
- if (id === f.id) {
91
- serverContentByKey.set(key, f.data);
92
- break;
93
- }
94
- }
95
- }
96
- }
97
- catch {
98
- // Ignore chunk failures; proceed with what we have
59
+ try {
60
+ const resp = await gt.downloadFileBatch(translatedFiles.map((file) => ({
61
+ branchId: file.branchId,
62
+ fileId: file.fileId,
63
+ locale: file.locale,
64
+ versionId: file.versionId,
65
+ })));
66
+ const files = resp?.files || [];
67
+ for (const f of files) {
68
+ serverContentByKey.set(`${f.branchId}:${f.fileId}:${f.versionId}:${f.locale}`, f.data);
99
69
  }
100
70
  }
71
+ catch {
72
+ // Ignore chunk failures; proceed with what we have
73
+ }
101
74
  // Compute diffs using fetched server contents
102
75
  for (const c of candidates) {
103
- const key = `${c.fileName}:${c.locale}`;
76
+ const key = `${c.branchId}:${c.fileId}:${c.versionId}:${c.locale}`;
104
77
  const serverContent = serverContentByKey.get(key);
105
78
  if (!serverContent)
106
79
  continue;
107
80
  try {
108
- const safeName = Buffer.from(`${c.fileName}:${c.locale}`)
81
+ const safeName = Buffer.from(`${c.branchId}:${c.fileId}:${c.versionId}:${c.locale}`)
109
82
  .toString('base64')
110
83
  .replace(/=+$/g, '');
111
84
  const tempServerFile = path.join(tempDir, `${safeName}.server`);
@@ -121,6 +94,7 @@ export async function collectAndSendUserEditDiffs(uploadedFiles, settings) {
121
94
  fileName: c.fileName,
122
95
  locale: c.locale,
123
96
  diff,
97
+ branchId: c.branchId,
124
98
  versionId: c.versionId,
125
99
  fileId: c.fileId,
126
100
  localContent,
@@ -133,25 +107,6 @@ export async function collectAndSendUserEditDiffs(uploadedFiles, settings) {
133
107
  }
134
108
  }
135
109
  if (collectedDiffs.length > 0) {
136
- // Batch by payload size
137
- const maxBatchBytes = MAX_DIFF_BATCH_BYTES;
138
- const batches = [];
139
- let currentBatch = [];
140
- for (const d of collectedDiffs) {
141
- const tentative = [...currentBatch, d];
142
- const bytes = Buffer.byteLength(JSON.stringify({ projectId: settings.projectId, diffs: tentative }), 'utf8');
143
- if (bytes > maxBatchBytes && currentBatch.length > 0) {
144
- batches.push(currentBatch);
145
- currentBatch = [d];
146
- }
147
- else {
148
- currentBatch = tentative;
149
- }
150
- }
151
- if (currentBatch.length > 0)
152
- batches.push(currentBatch);
153
- for (const batch of batches) {
154
- await sendUserEditDiffs(batch, settings);
155
- }
110
+ await gt.submitUserEditDiffs({ diffs: collectedDiffs });
156
111
  }
157
112
  }
@@ -1,16 +1,17 @@
1
1
  import { Settings } from '../types/index.js';
2
- export type BatchedFiles = Array<{
3
- translationId: string;
2
+ import type { FileStatusTracker } from '../workflow/PollJobsStep.js';
3
+ export type BatchedFiles = {
4
+ branchId: string;
5
+ fileId: string;
6
+ versionId: string;
7
+ locale: string;
4
8
  outputPath: string;
5
9
  inputPath: string;
6
- locale: string;
7
- fileLocale: string;
8
- fileId?: string;
9
- versionId?: string;
10
- }>;
10
+ }[];
11
11
  export type DownloadFileBatchResult = {
12
- successful: string[];
13
- failed: string[];
12
+ successful: BatchedFiles;
13
+ failed: BatchedFiles;
14
+ skipped: BatchedFiles;
14
15
  };
15
16
  /**
16
17
  * Downloads multiple translation files in a single batch request
@@ -19,4 +20,4 @@ export type DownloadFileBatchResult = {
19
20
  * @param retryDelay - Delay between retries in milliseconds
20
21
  * @returns Object containing successful and failed file IDs
21
22
  */
22
- export declare function downloadFileBatch(files: BatchedFiles, options: Settings, maxRetries?: number, retryDelay?: number, forceDownload?: boolean): Promise<DownloadFileBatchResult>;
23
+ export declare function downloadFileBatch(fileTracker: FileStatusTracker, files: BatchedFiles, options: Settings, forceDownload?: boolean): Promise<DownloadFileBatchResult>;
@@ -6,7 +6,7 @@ import { validateJsonSchema } from '../formats/json/utils.js';
6
6
  import { validateYamlSchema } from '../formats/yaml/utils.js';
7
7
  import { mergeJson } from '../formats/json/mergeJson.js';
8
8
  import mergeYaml from '../formats/yaml/mergeYaml.js';
9
- import { getDownloadedVersions, saveDownloadedVersions, } from '../fs/config/downloadedVersions.js';
9
+ import { getDownloadedVersions, saveDownloadedVersions, ensureNestedObject, } from '../fs/config/downloadedVersions.js';
10
10
  import { recordDownloaded } from '../state/recentDownloads.js';
11
11
  import stringify from 'fast-json-stable-stringify';
12
12
  /**
@@ -16,152 +16,145 @@ import stringify from 'fast-json-stable-stringify';
16
16
  * @param retryDelay - Delay between retries in milliseconds
17
17
  * @returns Object containing successful and failed file IDs
18
18
  */
19
- export async function downloadFileBatch(files, options, maxRetries = 3, retryDelay = 1000, forceDownload = false) {
19
+ export async function downloadFileBatch(fileTracker, files, options, forceDownload = false) {
20
20
  // Local record of what version was last downloaded for each fileName:locale
21
21
  const downloadedVersions = getDownloadedVersions(options.configDirectory);
22
22
  let didUpdateDownloadedLock = false;
23
- let retries = 0;
24
- const fileIds = files.map((file) => file.translationId);
25
- const result = { successful: [], failed: [] };
23
+ // Create a map of requested file keys to the file object
24
+ const requestedFileMap = new Map(files.map((file) => [
25
+ `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`,
26
+ file,
27
+ ]));
28
+ const result = {
29
+ successful: [],
30
+ failed: [],
31
+ skipped: [],
32
+ };
26
33
  // Create a map of translationId to outputPath for easier lookup
27
- const outputPathMap = new Map(files.map((file) => [file.translationId, file.outputPath]));
28
- const inputPathMap = new Map(files.map((file) => [file.translationId, file.inputPath]));
29
- const fileIdMap = new Map(files.map((file) => [file.translationId, file.fileId]));
30
- const localeMap = new Map(files.map((file) => [
31
- file.translationId,
32
- gt.resolveAliasLocale(file.locale),
34
+ const outputPathMap = new Map(files.map((file) => [
35
+ `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`,
36
+ file.outputPath,
33
37
  ]));
34
- const versionMap = new Map(files.map((file) => [file.translationId, file.versionId]));
35
- while (retries <= maxRetries) {
36
- try {
37
- // Download the files
38
- const responseData = await gt.downloadFileBatch(fileIds);
39
- const downloadedFiles = responseData.files || [];
40
- // Process each file in the response
41
- for (const file of downloadedFiles) {
42
- try {
43
- const translationId = file.id;
44
- const outputPath = outputPathMap.get(translationId);
45
- const inputPath = inputPathMap.get(translationId);
46
- const locale = localeMap.get(translationId);
47
- const fileId = fileIdMap.get(translationId);
48
- const versionId = versionMap.get(translationId);
49
- if (!outputPath || !inputPath) {
50
- logWarning(`No input/output path found for file: ${translationId}`);
51
- result.failed.push(translationId);
52
- continue;
53
- }
54
- // Ensure the directory exists
55
- const dir = path.dirname(outputPath);
56
- if (!fs.existsSync(dir)) {
57
- fs.mkdirSync(dir, { recursive: true });
58
- }
59
- // If a local translation already exists for the same source version, skip overwrite
60
- const keyId = fileId || inputPath;
61
- const downloadedKey = `${keyId}:${locale}`;
62
- const alreadyDownloadedVersion = downloadedVersions.entries[downloadedKey]?.versionId;
63
- const fileExists = fs.existsSync(outputPath);
64
- if (!forceDownload &&
65
- fileExists &&
66
- versionId &&
67
- alreadyDownloadedVersion === versionId) {
68
- result.successful.push(translationId);
69
- continue;
70
- }
71
- let data = file.data;
72
- if (options.options?.jsonSchema && locale) {
73
- const jsonSchema = validateJsonSchema(options.options, inputPath);
74
- if (jsonSchema) {
75
- const originalContent = fs.readFileSync(inputPath, 'utf8');
76
- if (originalContent) {
77
- data = mergeJson(originalContent, inputPath, options.options, [
78
- {
79
- translatedContent: file.data,
80
- targetLocale: locale,
81
- },
82
- ], options.defaultLocale)[0];
83
- }
38
+ try {
39
+ // Download the files
40
+ const responseData = await gt.downloadFileBatch(files.map((file) => ({
41
+ fileId: file.fileId,
42
+ branchId: file.branchId,
43
+ versionId: file.versionId,
44
+ locale: file.locale,
45
+ })));
46
+ const downloadedFiles = responseData.files || [];
47
+ // Process each file in the response
48
+ for (const file of downloadedFiles) {
49
+ const fileKey = `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`;
50
+ const requestedFile = requestedFileMap.get(fileKey);
51
+ if (!requestedFile) {
52
+ continue;
53
+ }
54
+ try {
55
+ const outputPath = outputPathMap.get(fileKey);
56
+ const fileProperties = fileTracker.completed.get(fileKey);
57
+ if (!outputPath || !fileProperties) {
58
+ logWarning(`No input/output path found for file: ${fileKey}`);
59
+ result.failed.push(requestedFile);
60
+ continue;
61
+ }
62
+ const { fileId, versionId, locale, branchId, fileName: inputPath, } = fileProperties;
63
+ // Ensure the directory exists
64
+ const dir = path.dirname(outputPath);
65
+ if (!fs.existsSync(dir)) {
66
+ fs.mkdirSync(dir, { recursive: true });
67
+ }
68
+ // If a local translation already exists for the same source version, skip overwrite
69
+ const downloadedVersion = downloadedVersions.entries[branchId]?.[fileId]?.[versionId]?.[locale];
70
+ const fileExists = fs.existsSync(outputPath);
71
+ if (!forceDownload && fileExists && downloadedVersion) {
72
+ result.skipped.push(requestedFile);
73
+ continue;
74
+ }
75
+ let data = file.data;
76
+ if (options.options?.jsonSchema && locale) {
77
+ const jsonSchema = validateJsonSchema(options.options, inputPath);
78
+ if (jsonSchema) {
79
+ const originalContent = fs.readFileSync(inputPath, 'utf8');
80
+ if (originalContent) {
81
+ data = mergeJson(originalContent, inputPath, options.options, [
82
+ {
83
+ translatedContent: file.data,
84
+ targetLocale: locale,
85
+ },
86
+ ], options.defaultLocale)[0];
84
87
  }
85
88
  }
86
- if (options.options?.yamlSchema && locale) {
87
- const yamlSchema = validateYamlSchema(options.options, inputPath);
88
- if (yamlSchema) {
89
- const originalContent = fs.readFileSync(inputPath, 'utf8');
90
- if (originalContent) {
91
- data = mergeYaml(originalContent, inputPath, options.options, [
92
- {
93
- translatedContent: file.data,
94
- targetLocale: locale,
95
- },
96
- ], options.defaultLocale)[0];
97
- }
89
+ }
90
+ if (options.options?.yamlSchema && locale) {
91
+ const yamlSchema = validateYamlSchema(options.options, inputPath);
92
+ if (yamlSchema) {
93
+ const originalContent = fs.readFileSync(inputPath, 'utf8');
94
+ if (originalContent) {
95
+ data = mergeYaml(originalContent, inputPath, options.options, [
96
+ {
97
+ translatedContent: file.data,
98
+ targetLocale: locale,
99
+ },
100
+ ], options.defaultLocale)[0];
98
101
  }
99
102
  }
100
- // If the file is a GTJSON file, stable sort the order and format the data
101
- if (file.fileFormat === 'GTJSON') {
102
- try {
103
- const jsonData = JSON.parse(data);
104
- const sortedData = stringify(jsonData); // stably sort with fast-json-stable-stringify
105
- const sortedJsonData = JSON.parse(sortedData);
106
- data = JSON.stringify(sortedJsonData, null, 2); // format the data
107
- }
108
- catch (error) {
109
- logWarning(`Failed to sort GTJson file: ${file.id}: ` + error);
110
- }
103
+ }
104
+ // If the file is a GTJSON file, stable sort the order and format the data
105
+ if (file.fileFormat === 'GTJSON') {
106
+ try {
107
+ const jsonData = JSON.parse(data);
108
+ const sortedData = stringify(jsonData); // stably sort with fast-json-stable-stringify
109
+ const sortedJsonData = JSON.parse(sortedData);
110
+ data = JSON.stringify(sortedJsonData, null, 2); // format the data
111
111
  }
112
- // Write the file to disk
113
- await fs.promises.writeFile(outputPath, data);
114
- // Track as downloaded
115
- recordDownloaded(outputPath);
116
- result.successful.push(translationId);
117
- if (versionId) {
118
- downloadedVersions.entries[downloadedKey] = {
119
- versionId,
120
- fileId: fileId || undefined,
121
- fileName: inputPath,
122
- updatedAt: new Date().toISOString(),
123
- };
124
- didUpdateDownloadedLock = true;
112
+ catch (error) {
113
+ logWarning(`Failed to sort GTJson file: ${file.id}: ` + error);
125
114
  }
126
115
  }
127
- catch (error) {
128
- logError(`Error saving file ${file.id}: ` + error);
129
- result.failed.push(file.id);
130
- }
131
- }
132
- // Add any files that weren't in the response to the failed list
133
- const downloadedIds = new Set(downloadedFiles.map((file) => file.id));
134
- for (const fileId of fileIds) {
135
- if (!downloadedIds.has(fileId) && !result.failed.includes(fileId)) {
136
- result.failed.push(fileId);
116
+ // Write the file to disk
117
+ await fs.promises.writeFile(outputPath, data);
118
+ // Track as downloaded
119
+ recordDownloaded(outputPath);
120
+ result.successful.push(requestedFile);
121
+ if (branchId && fileId && versionId && locale) {
122
+ ensureNestedObject(downloadedVersions.entries, [
123
+ branchId,
124
+ fileId,
125
+ versionId,
126
+ locale,
127
+ ]);
128
+ downloadedVersions.entries[branchId][fileId][versionId][locale] = {
129
+ updatedAt: new Date().toISOString(),
130
+ };
131
+ didUpdateDownloadedLock = true;
137
132
  }
138
133
  }
139
- // Persist any updates to the downloaded map at the end of a successful cycle
140
- if (didUpdateDownloadedLock) {
141
- saveDownloadedVersions(options.configDirectory, downloadedVersions);
142
- didUpdateDownloadedLock = false;
134
+ catch (error) {
135
+ logError(`Error saving file ${fileKey}: ` + error);
136
+ result.failed.push(requestedFile);
143
137
  }
144
- return result;
145
138
  }
146
- catch (error) {
147
- // If we've retried too many times, log an error and return false
148
- if (retries >= maxRetries) {
149
- logError(`Error downloading files in batch after ${maxRetries + 1} attempts: ` +
150
- error);
151
- // Mark all files as failed
152
- result.failed = [...fileIds];
153
- if (didUpdateDownloadedLock) {
154
- saveDownloadedVersions(options.configDirectory, downloadedVersions);
155
- }
156
- return result;
139
+ // Add any files that weren't in the response to the failed list
140
+ const downloadedFileKeys = new Set(downloadedFiles.map((file) => `${file.branchId}:${file.fileId}:${file.versionId}:${file.locale}`));
141
+ for (const [fileKey, requestedFile] of requestedFileMap.entries()) {
142
+ if (!downloadedFileKeys.has(fileKey)) {
143
+ result.failed.push(requestedFile);
157
144
  }
158
- // Increment retry counter and wait before next attempt
159
- retries++;
160
- await new Promise((resolve) => setTimeout(resolve, retryDelay));
161
145
  }
146
+ // Persist any updates to the downloaded map at the end of a successful cycle
147
+ if (didUpdateDownloadedLock) {
148
+ saveDownloadedVersions(options.configDirectory, downloadedVersions);
149
+ didUpdateDownloadedLock = false;
150
+ }
151
+ return result;
152
+ }
153
+ catch (error) {
154
+ logError(`An unexpected error occurred while downloading files: ` + error);
162
155
  }
163
156
  // Mark all files as failed if we get here
164
- result.failed = [...fileIds];
157
+ result.failed = [...requestedFileMap.values()];
165
158
  if (didUpdateDownloadedLock) {
166
159
  saveDownloadedVersions(options.configDirectory, downloadedVersions);
167
160
  }
@@ -1,6 +1,8 @@
1
- import { gt } from '../utils/gt.js';
2
1
  import { aggregateFiles } from '../formats/files/translate.js';
3
2
  import { collectAndSendUserEditDiffs } from './collectUserEditDiffs.js';
3
+ import { gt } from '../utils/gt.js';
4
+ import { BranchStep } from '../workflow/BranchStep.js';
5
+ import { logErrorAndExit } from '../console/logging.js';
4
6
  /**
5
7
  * Uploads current source files to obtain file references, then collects and sends
6
8
  * diffs for all locales based on last downloaded versions. Does not enqueue translations.
@@ -12,19 +14,20 @@ export async function saveLocalEdits(settings) {
12
14
  const files = await aggregateFiles(settings);
13
15
  if (!files.length)
14
16
  return;
15
- const uploads = files.map(({ content, fileName, fileFormat, dataFormat }) => ({
16
- source: {
17
- content,
18
- fileName,
19
- fileFormat,
20
- dataFormat,
21
- locale: settings.defaultLocale,
22
- },
17
+ // run branch query to get branch id
18
+ // Run the branch step
19
+ const branchStep = new BranchStep(gt, settings);
20
+ const branchResult = await branchStep.run();
21
+ await branchStep.wait();
22
+ if (!branchResult) {
23
+ logErrorAndExit('Failed to resolve git branch information.');
24
+ }
25
+ const uploads = files.map((file) => ({
26
+ fileName: file.fileName,
27
+ fileFormat: file.fileFormat,
28
+ branchId: branchResult.currentBranch.id,
29
+ fileId: file.fileId,
30
+ versionId: file.versionId,
23
31
  }));
24
- // Upload sources only to get file references, then compute diffs
25
- const upload = await gt.uploadSourceFiles(uploads, {
26
- sourceLocale: settings.defaultLocale,
27
- modelProvider: settings.modelProvider,
28
- });
29
- await collectAndSendUserEditDiffs(upload.uploadedFiles, settings);
32
+ await collectAndSendUserEditDiffs(uploads, settings);
30
33
  }
package/dist/cli/base.js CHANGED
@@ -94,7 +94,7 @@ export class BaseCLI {
94
94
  if (!settings.stageTranslations) {
95
95
  const results = await handleStage(initOptions, settings, this.library, false);
96
96
  if (results) {
97
- await handleTranslate(initOptions, settings, results);
97
+ await handleTranslate(initOptions, settings, results.fileVersionData, results.jobData, results.branchData);
98
98
  }
99
99
  }
100
100
  else {
@@ -1,5 +1,11 @@
1
1
  import { Settings, SupportedLibraries, TranslateFlags } from '../../types/index.js';
2
- import { SendFilesResult } from '../../api/sendFiles.js';
2
+ import type { EnqueueFilesResult } from 'generaltranslation/types';
3
+ import { FileTranslationData } from '../../workflow/download.js';
4
+ import { BranchData } from '../../types/branch.js';
3
5
  export declare const TEMPLATE_FILE_NAME = "__INTERNAL_GT_TEMPLATE_NAME__";
4
6
  export declare const TEMPLATE_FILE_ID: string;
5
- export declare function handleStage(options: TranslateFlags, settings: Settings, library: SupportedLibraries, stage: boolean): Promise<SendFilesResult | undefined>;
7
+ export declare function handleStage(options: TranslateFlags, settings: Settings, library: SupportedLibraries, stage: boolean): Promise<{
8
+ fileVersionData: FileTranslationData | undefined;
9
+ jobData: EnqueueFilesResult | undefined;
10
+ branchData: BranchData | undefined;
11
+ } | null>;