gt 2.18.1 → 2.20.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.
Files changed (100) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/api/collectUserEditDiffs.js +23 -23
  3. package/dist/api/collectUserEditDiffs.js.map +1 -1
  4. package/dist/api/downloadFileBatch.js +7 -4
  5. package/dist/api/downloadFileBatch.js.map +1 -1
  6. package/dist/api/saveLocalEdits.js +2 -2
  7. package/dist/api/saveLocalEdits.js.map +1 -1
  8. package/dist/cli/base.d.ts +2 -0
  9. package/dist/cli/base.js +45 -2
  10. package/dist/cli/base.js.map +1 -1
  11. package/dist/cli/commands/api.d.ts +17 -0
  12. package/dist/cli/commands/api.js +153 -0
  13. package/dist/cli/commands/api.js.map +1 -0
  14. package/dist/cli/commands/upload.js +15 -5
  15. package/dist/cli/commands/upload.js.map +1 -1
  16. package/dist/cli/commands/utils/validation.d.ts +4 -0
  17. package/dist/cli/commands/utils/validation.js +12 -5
  18. package/dist/cli/commands/utils/validation.js.map +1 -1
  19. package/dist/config/generateSettings.js +7 -0
  20. package/dist/config/generateSettings.js.map +1 -1
  21. package/dist/console/index.d.ts +1 -0
  22. package/dist/console/index.js +2 -1
  23. package/dist/console/index.js.map +1 -1
  24. package/dist/formats/files/aggregateFiles.js +20 -8
  25. package/dist/formats/files/aggregateFiles.js.map +1 -1
  26. package/dist/formats/files/androidLocale.d.ts +7 -0
  27. package/dist/formats/files/androidLocale.js +32 -0
  28. package/dist/formats/files/androidLocale.js.map +1 -0
  29. package/dist/formats/files/fileMapping.js +31 -24
  30. package/dist/formats/files/fileMapping.js.map +1 -1
  31. package/dist/formats/files/localePath.d.ts +3 -0
  32. package/dist/formats/files/localePath.js +18 -0
  33. package/dist/formats/files/localePath.js.map +1 -0
  34. package/dist/formats/files/supportedFiles.d.ts +2 -1
  35. package/dist/formats/files/supportedFiles.js +4 -2
  36. package/dist/formats/files/supportedFiles.js.map +1 -1
  37. package/dist/formats/files/transformFormat.d.ts +2 -0
  38. package/dist/formats/files/transformFormat.js +6 -3
  39. package/dist/formats/files/transformFormat.js.map +1 -1
  40. package/dist/fs/config/parseFilesConfig.js +5 -1
  41. package/dist/fs/config/parseFilesConfig.js.map +1 -1
  42. package/dist/fs/fileContent.d.ts +65 -0
  43. package/dist/fs/fileContent.js +171 -0
  44. package/dist/fs/fileContent.js.map +1 -0
  45. package/dist/generated/version.d.ts +1 -1
  46. package/dist/generated/version.js +1 -1
  47. package/dist/generated/version.js.map +1 -1
  48. package/dist/state/recentDownloads.d.ts +3 -2
  49. package/dist/state/recentDownloads.js.map +1 -1
  50. package/dist/translation/reviewSetupWarning.js +3 -3
  51. package/dist/translation/reviewSetupWarning.js.map +1 -1
  52. package/dist/utils/api.d.ts +194 -0
  53. package/dist/utils/api.js +268 -0
  54. package/dist/utils/api.js.map +1 -0
  55. package/dist/utils/persistPostprocessHashes.d.ts +1 -7
  56. package/dist/utils/persistPostprocessHashes.js +15 -1
  57. package/dist/utils/persistPostprocessHashes.js.map +1 -1
  58. package/dist/workflows/download.js +4 -4
  59. package/dist/workflows/download.js.map +1 -1
  60. package/dist/workflows/enqueue.js +4 -4
  61. package/dist/workflows/enqueue.js.map +1 -1
  62. package/dist/workflows/publish.js +2 -2
  63. package/dist/workflows/publish.js.map +1 -1
  64. package/dist/workflows/setupProject.js +4 -4
  65. package/dist/workflows/setupProject.js.map +1 -1
  66. package/dist/workflows/stage.js +7 -7
  67. package/dist/workflows/stage.js.map +1 -1
  68. package/dist/workflows/steps/BranchStep.d.ts +2 -2
  69. package/dist/workflows/steps/BranchStep.js +9 -9
  70. package/dist/workflows/steps/BranchStep.js.map +1 -1
  71. package/dist/workflows/steps/DownloadStep.d.ts +3 -3
  72. package/dist/workflows/steps/DownloadStep.js +3 -3
  73. package/dist/workflows/steps/DownloadStep.js.map +1 -1
  74. package/dist/workflows/steps/EnqueueStep.d.ts +5 -3
  75. package/dist/workflows/steps/EnqueueStep.js.map +1 -1
  76. package/dist/workflows/steps/PollJobsStep.d.ts +3 -3
  77. package/dist/workflows/steps/PollJobsStep.js +6 -6
  78. package/dist/workflows/steps/PollJobsStep.js.map +1 -1
  79. package/dist/workflows/steps/PublishStep.d.ts +3 -3
  80. package/dist/workflows/steps/PublishStep.js +3 -3
  81. package/dist/workflows/steps/PublishStep.js.map +1 -1
  82. package/dist/workflows/steps/SetupStep.d.ts +6 -4
  83. package/dist/workflows/steps/SetupStep.js.map +1 -1
  84. package/dist/workflows/steps/TagStep.d.ts +3 -3
  85. package/dist/workflows/steps/TagStep.js +3 -3
  86. package/dist/workflows/steps/TagStep.js.map +1 -1
  87. package/dist/workflows/steps/UploadSourcesStep.d.ts +3 -3
  88. package/dist/workflows/steps/UploadSourcesStep.js +8 -6
  89. package/dist/workflows/steps/UploadSourcesStep.js.map +1 -1
  90. package/dist/workflows/steps/UploadTranslationsStep.d.ts +3 -3
  91. package/dist/workflows/steps/UploadTranslationsStep.js +24 -6
  92. package/dist/workflows/steps/UploadTranslationsStep.js.map +1 -1
  93. package/dist/workflows/upload.js +4 -4
  94. package/dist/workflows/upload.js.map +1 -1
  95. package/dist/workflows/utils/filterFilesForEnqueue.d.ts +2 -2
  96. package/dist/workflows/utils/filterFilesForEnqueue.js.map +1 -1
  97. package/dist/workflows/utils/queryCompletedTranslations.d.ts +3 -3
  98. package/dist/workflows/utils/queryCompletedTranslations.js +2 -2
  99. package/dist/workflows/utils/queryCompletedTranslations.js.map +1 -1
  100. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"reviewSetupWarning.js","names":[],"sources":["../../src/translation/reviewSetupWarning.ts"],"sourcesContent":["import path from 'node:path';\nimport chalk from 'chalk';\nimport type { FileToUpload } from 'generaltranslation/types';\nimport { logger } from '../console/logger.js';\nimport { gt } from '../utils/gt.js';\nimport { Settings } from '../types/index.js';\n\nconst PROJECT_INFO_TIMEOUT_MS = 10_000;\n\n/**\n * Warns when an upload contains review-gated content but the project\n * auto-approves new translations, pointing the user at the dashboard project\n * settings page. The CLI cannot change the auto-approve setting itself —\n * review setup happens in the dashboard.\n *\n * The project's auto-approve setting is fetched lazily (only when the upload\n * actually contains review-gated content):\n * - false: manual review is already the project workflow — no warning\n * - true: requiresReview config has no gating effect — warn\n * - unavailable (older API, dry runs without credentials): warn with\n * conditional wording, since auto-approve is the platform default\n */\nexport async function warnManualReviewSetup(\n settings: Settings,\n files: FileToUpload[]\n): Promise<void> {\n const requiresReviewPaths = settings.files?.requiresReviewPaths;\n\n const hasReviewGatedFile = files.some((file) => {\n if (file.fileFormat === 'GTJSON') {\n // Uploaded GTJSON metadata carries the platform's snake_case key\n return Object.values(file.formatMetadata ?? {}).some(\n (metadata) =>\n (metadata as { requires_review?: boolean })?.requires_review === true\n );\n }\n return requiresReviewPaths?.has(path.resolve(process.cwd(), file.fileName));\n });\n if (!hasReviewGatedFile) return;\n\n let autoApprove: boolean | undefined;\n try {\n autoApprove = (\n await gt.getProjectInfo({ timeout: PROJECT_INFO_TIMEOUT_MS })\n ).autoApprove;\n } catch {\n // Setting unavailable — fall through to the conditional wording\n }\n if (autoApprove === false) return;\n\n const settingsUrl = settings.projectId\n ? `${settings.dashboardUrl}/project/${settings.projectId}/settings`\n : settings.dashboardUrl;\n const autoApproveClause =\n autoApprove === true\n ? 'but this project approves new translations automatically.'\n : 'but new translations are approved automatically unless auto-approval is turned off for this project.';\n // \"Auto approve translations\" matches the dashboard setting title exactly.\n const message =\n `Some of your content requires review (requiresReview), ${autoApproveClause} ` +\n `To review translations before they are used, disable \"Auto approve translations\" in your project settings:`;\n logger.warn(\n chalk.yellow(\n [...wrapWords(message, 70), chalk.cyan(settingsUrl)].join('\\n')\n )\n );\n}\n\n/**\n * Greedy word wrap so warning lines render at a consistent width instead of\n * one terminal-width line followed by stubby fragments.\n */\nfunction wrapWords(text: string, width: number): string[] {\n const lines: string[] = [];\n let line = '';\n for (const word of text.split(' ')) {\n if (line && line.length + 1 + word.length > width) {\n lines.push(line);\n line = word;\n } else {\n line = line ? `${line} ${word}` : word;\n }\n }\n if (line) lines.push(line);\n return lines;\n}\n"],"mappings":";;;;;AAOA,MAAM,0BAA0B;;;;;;;;;;;;;;AAehC,eAAsB,sBACpB,UACA,OACe;CACf,MAAM,sBAAsB,SAAS,OAAO;AAY5C,KAAI,CAVuB,MAAM,MAAM,SAAS;AAC9C,MAAI,KAAK,eAAe,SAEtB,QAAO,OAAO,OAAO,KAAK,kBAAkB,EAAE,CAAC,CAAC,MAC7C,aACE,UAA4C,oBAAoB,KACpE;AAEH,SAAO,qBAAqB,IAAI,KAAK,QAAQ,QAAQ,KAAK,EAAE,KAAK,SAAS,CAAC;GAEtD,CAAE;CAEzB,IAAI;AACJ,KAAI;AACF,iBACE,MAAM,GAAG,eAAe,EAAE,SAAS,yBAAyB,CAAC,EAC7D;SACI;AAGR,KAAI,gBAAgB,MAAO;CAE3B,MAAM,cAAc,SAAS,YACzB,GAAG,SAAS,aAAa,WAAW,SAAS,UAAU,aACvD,SAAS;CAMb,MAAM,UACJ,0DALA,gBAAgB,OACZ,8DACA,uGAGwE;AAE9E,QAAO,KACL,MAAM,OACJ,CAAC,GAAG,UAAU,SAAS,GAAG,EAAE,MAAM,KAAK,YAAY,CAAC,CAAC,KAAK,KAAK,CAChE,CACF;;;;;;AAOH,SAAS,UAAU,MAAc,OAAyB;CACxD,MAAM,QAAkB,EAAE;CAC1B,IAAI,OAAO;AACX,MAAK,MAAM,QAAQ,KAAK,MAAM,IAAI,CAChC,KAAI,QAAQ,KAAK,SAAS,IAAI,KAAK,SAAS,OAAO;AACjD,QAAM,KAAK,KAAK;AAChB,SAAO;OAEP,QAAO,OAAO,GAAG,KAAK,GAAG,SAAS;AAGtC,KAAI,KAAM,OAAM,KAAK,KAAK;AAC1B,QAAO"}
1
+ {"version":3,"file":"reviewSetupWarning.js","names":[],"sources":["../../src/translation/reviewSetupWarning.ts"],"sourcesContent":["import path from 'node:path';\nimport chalk from 'chalk';\nimport type { FileToUpload } from 'generaltranslation/types';\nimport { logger } from '../console/logger.js';\nimport { api } from '../utils/api.js';\nimport { Settings } from '../types/index.js';\n\nconst PROJECT_INFO_TIMEOUT_MS = 10_000;\n\n/**\n * Warns when an upload contains review-gated content but the project\n * auto-approves new translations, pointing the user at the dashboard project\n * settings page. The CLI cannot change the auto-approve setting itself —\n * review setup happens in the dashboard.\n *\n * The project's auto-approve setting is fetched lazily (only when the upload\n * actually contains review-gated content):\n * - false: manual review is already the project workflow — no warning\n * - true: requiresReview config has no gating effect — warn\n * - unavailable (older API, dry runs without credentials): warn with\n * conditional wording, since auto-approve is the platform default\n */\nexport async function warnManualReviewSetup(\n settings: Settings,\n files: FileToUpload[]\n): Promise<void> {\n const requiresReviewPaths = settings.files?.requiresReviewPaths;\n\n const hasReviewGatedFile = files.some((file) => {\n if (file.fileFormat === 'GTJSON') {\n // Uploaded GTJSON metadata carries the platform's snake_case key\n return Object.values(file.formatMetadata ?? {}).some(\n (metadata) =>\n typeof metadata === 'object' &&\n metadata !== null &&\n 'requires_review' in metadata &&\n metadata.requires_review === true\n );\n }\n return requiresReviewPaths?.has(path.resolve(process.cwd(), file.fileName));\n });\n if (!hasReviewGatedFile) return;\n\n let autoApprove: boolean | undefined;\n try {\n autoApprove = (await api.getProjectInfo(PROJECT_INFO_TIMEOUT_MS))\n .autoApprove;\n } catch {\n // Setting unavailable — fall through to the conditional wording\n }\n if (autoApprove === false) return;\n\n const settingsUrl = settings.projectId\n ? `${settings.dashboardUrl}/project/${settings.projectId}/settings`\n : settings.dashboardUrl;\n const autoApproveClause =\n autoApprove === true\n ? 'but this project approves new translations automatically.'\n : 'but new translations are approved automatically unless auto-approval is turned off for this project.';\n // \"Auto approve translations\" matches the dashboard setting title exactly.\n const message =\n `Some of your content requires review (requiresReview), ${autoApproveClause} ` +\n `To review translations before they are used, disable \"Auto approve translations\" in your project settings:`;\n logger.warn(\n chalk.yellow(\n [...wrapWords(message, 70), chalk.cyan(settingsUrl)].join('\\n')\n )\n );\n}\n\n/**\n * Greedy word wrap so warning lines render at a consistent width instead of\n * one terminal-width line followed by stubby fragments.\n */\nfunction wrapWords(text: string, width: number): string[] {\n const lines: string[] = [];\n let line = '';\n for (const word of text.split(' ')) {\n if (line && line.length + 1 + word.length > width) {\n lines.push(line);\n line = word;\n } else {\n line = line ? `${line} ${word}` : word;\n }\n }\n if (line) lines.push(line);\n return lines;\n}\n"],"mappings":";;;;;AAOA,MAAM,0BAA0B;;;;;;;;;;;;;;AAehC,eAAsB,sBACpB,UACA,OACe;CACf,MAAM,sBAAsB,SAAS,OAAO;AAe5C,KAAI,CAbuB,MAAM,MAAM,SAAS;AAC9C,MAAI,KAAK,eAAe,SAEtB,QAAO,OAAO,OAAO,KAAK,kBAAkB,EAAE,CAAC,CAAC,MAC7C,aACC,OAAO,aAAa,YACpB,aAAa,QACb,qBAAqB,YACrB,SAAS,oBAAoB,KAChC;AAEH,SAAO,qBAAqB,IAAI,KAAK,QAAQ,QAAQ,KAAK,EAAE,KAAK,SAAS,CAAC;GAEtD,CAAE;CAEzB,IAAI;AACJ,KAAI;AACF,iBAAe,MAAM,IAAI,eAAe,wBAAwB,EAC7D;SACG;AAGR,KAAI,gBAAgB,MAAO;CAE3B,MAAM,cAAc,SAAS,YACzB,GAAG,SAAS,aAAa,WAAW,SAAS,UAAU,aACvD,SAAS;CAMb,MAAM,UACJ,0DALA,gBAAgB,OACZ,8DACA,uGAGwE;AAE9E,QAAO,KACL,MAAM,OACJ,CAAC,GAAG,UAAU,SAAS,GAAG,EAAE,MAAM,KAAK,YAAY,CAAC,CAAC,KAAK,KAAK,CAChE,CACF;;;;;;AAOH,SAAS,UAAU,MAAc,OAAyB;CACxD,MAAM,QAAkB,EAAE;CAC1B,IAAI,OAAO;AACX,MAAK,MAAM,QAAQ,KAAK,MAAM,IAAI,CAChC,KAAI,QAAQ,KAAK,SAAS,IAAI,KAAK,SAAS,OAAO;AACjD,QAAM,KAAK,KAAK;AAChB,SAAO;OAEP,QAAO,OAAO,GAAG,KAAK,GAAG,SAAS;AAGtC,KAAI,KAAM,OAAM,KAAK,KAAK;AAC1B,QAAO"}
@@ -0,0 +1,194 @@
1
+ import type { CustomMapping } from '@generaltranslation/format/types';
2
+ import { type ApiClientConfig, type AwaitJobsOptions, type CreateBranchData, type CreateProjectData, type CreateTagData, type DownloadFilesData, type EnqueueFileTranslationsData, type GenerateProjectContextData, type GetBranchInfoData, type GetFileInfoData, type ProcessFileMovesData, type PublishFilesData, type SubmitUserEditDiffsData, type UploadSourceFilesData, type UploadTranslationsData } from 'generaltranslation/api';
3
+ export declare function configureApiClient(config: ApiClientConfig & {
4
+ customMapping?: CustomMapping;
5
+ }): void;
6
+ export declare const api: {
7
+ queryBranchData(body: GetBranchInfoData["body"]): Promise<{
8
+ branches: Array<import("@generaltranslation/api").Branch>;
9
+ defaultBranch: import("@generaltranslation/api").Branch & ({
10
+ [key: string]: unknown;
11
+ } | null);
12
+ }>;
13
+ createBranch(body: CreateBranchData["body"]): Promise<{
14
+ branch: import("@generaltranslation/api").Branch;
15
+ }>;
16
+ queryFileData(body: GetFileInfoData["body"]): Promise<{
17
+ translatedFiles: {
18
+ locale: string;
19
+ branchId: string;
20
+ fileId: string;
21
+ versionId: string;
22
+ fileFormat: import("@generaltranslation/api").FileFormat;
23
+ dataFormat: string | null;
24
+ createdAt: string;
25
+ updatedAt: string;
26
+ approvedAt: string | null;
27
+ publishedAt: string | null;
28
+ completedAt: string | null;
29
+ }[];
30
+ sourceFiles: {
31
+ sourceLocale: string;
32
+ locales: string[];
33
+ branchId: string;
34
+ fileId: string;
35
+ versionId: string;
36
+ fileName: string;
37
+ fileFormat: import("@generaltranslation/api").FileFormat;
38
+ dataFormat: string | null;
39
+ createdAt: string;
40
+ updatedAt: string;
41
+ publishedAt: string | null;
42
+ }[];
43
+ }>;
44
+ checkJobStatus(jobIds: string[]): Promise<({
45
+ status: "queued";
46
+ jobId: string;
47
+ } | {
48
+ status: "processing";
49
+ jobId: string;
50
+ } | {
51
+ status: "failed";
52
+ jobId: string;
53
+ error: {
54
+ message: string | null;
55
+ };
56
+ } | {
57
+ status: "completed";
58
+ jobId: string;
59
+ } | {
60
+ status: "unknown";
61
+ jobId: string;
62
+ })[]>;
63
+ getProjectInfo(timeoutMs: number): Promise<{
64
+ id: string;
65
+ name: string;
66
+ orgId: string;
67
+ defaultLocale: string | null;
68
+ currentLocales: Array<string>;
69
+ autoApprove: boolean;
70
+ }>;
71
+ resolveAliasLocale(locale: string): string;
72
+ downloadFileBatch(files: DownloadFilesData["body"]): Promise<{
73
+ files: {
74
+ data: string;
75
+ locale?: string;
76
+ id: string;
77
+ branchId: string;
78
+ fileId: string;
79
+ versionId: string;
80
+ fileName?: string;
81
+ metadata: import("@generaltranslation/api").JsonObject;
82
+ fileFormat: import("@generaltranslation/api").FileFormat;
83
+ }[];
84
+ count: number;
85
+ pending: {
86
+ branchId: string;
87
+ fileId: string;
88
+ locale: string;
89
+ versionId: string;
90
+ }[];
91
+ }>;
92
+ publishFiles(files: PublishFilesData["body"]["files"]): Promise<{
93
+ results: Array<{
94
+ fileId: string;
95
+ versionId: string;
96
+ locale?: string;
97
+ branchId: string;
98
+ success: boolean;
99
+ error?: string;
100
+ }>;
101
+ }>;
102
+ submitUserEditDiffs(body: SubmitUserEditDiffsData["body"]): Promise<{
103
+ filesProcessed: number;
104
+ entriesReceived: number;
105
+ message: string;
106
+ }[]>;
107
+ createTag(body: CreateTagData["body"]): Promise<{
108
+ tag: {
109
+ id: string;
110
+ tagId: string;
111
+ message: string | null;
112
+ createdAt: string;
113
+ updatedAt: string;
114
+ };
115
+ }>;
116
+ createProject(body: CreateProjectData["body"]): Promise<{
117
+ project: {
118
+ id: string;
119
+ name: string;
120
+ orgId: string;
121
+ defaultLocale: string;
122
+ };
123
+ }>;
124
+ getOrphanedFiles(branchId: string, fileIds: string[]): Promise<{
125
+ orphanedFiles: Array<{
126
+ fileId: string;
127
+ versionId: string;
128
+ fileName: string;
129
+ }>;
130
+ }>;
131
+ processFileMoves(moves: ProcessFileMovesData["body"]["moves"], options: Pick<ProcessFileMovesData["body"], "branchId">): Promise<{
132
+ results: {
133
+ oldFileId: string;
134
+ newFileId: string;
135
+ success: boolean;
136
+ newSourceFileId?: string;
137
+ clonedTranslationsCount?: number;
138
+ error?: string;
139
+ }[];
140
+ summary: {
141
+ total: number;
142
+ succeeded: number;
143
+ failed: number;
144
+ };
145
+ }>;
146
+ setupProject(files: GenerateProjectContextData["body"]["files"], options?: Omit<GenerateProjectContextData["body"], "files">): Promise<{
147
+ status: "completed";
148
+ } | {
149
+ setupJobId: string;
150
+ status: "queued";
151
+ }>;
152
+ awaitJobs(jobIds: readonly string[], options?: AwaitJobsOptions): Promise<import("@generaltranslation/api").AwaitJobsResult>;
153
+ enqueueFiles(files: EnqueueFileTranslationsData["body"]["files"], options: {
154
+ sourceLocale?: string;
155
+ targetLocales: string[];
156
+ modelProvider?: string;
157
+ force?: boolean;
158
+ }): Promise<{
159
+ jobData: {
160
+ [k: string]: any;
161
+ };
162
+ locales: string[];
163
+ message: string;
164
+ }>;
165
+ uploadSourceFiles(files: Array<{
166
+ source: UploadSourceFilesData["body"]["data"][number]["source"];
167
+ }>, options: {
168
+ sourceLocale: string;
169
+ }): Promise<{
170
+ uploadedFiles: {
171
+ branchId?: string;
172
+ fileId: string;
173
+ versionId: string;
174
+ fileName: string;
175
+ fileFormat: import("@generaltranslation/api").FileFormat;
176
+ dataFormat?: string;
177
+ locale?: string;
178
+ }[];
179
+ }>;
180
+ uploadTranslations(files: UploadTranslationsData["body"]["data"], options: {
181
+ sourceLocale: string;
182
+ }): Promise<{
183
+ uploadedFiles: {
184
+ branchId?: string;
185
+ fileId: string;
186
+ versionId: string;
187
+ fileName: string;
188
+ fileFormat: import("@generaltranslation/api").FileFormat;
189
+ dataFormat?: string;
190
+ locale?: string;
191
+ }[];
192
+ }>;
193
+ };
194
+ export type ApiClient = typeof api;
@@ -0,0 +1,268 @@
1
+ import { unwrapApiResult } from "generaltranslation/internal";
2
+ import { resolveAliasLocale, resolveCanonicalLocale } from "@generaltranslation/format";
3
+ import { DEFAULT_BATCH_SIZE, createApiClient, createBranch, createProject, createTag, decodeFileContent, downloadFiles, encodeFileContent, enqueueFileTranslations, generateProjectContext, getBranchInfo, getFileInfo, getOrphanedFiles, getProjectInfo, getTranslationJobInfo, pollJobs, processBatches, processFileMoves, publishFiles, submitUserEditDiffs, uploadSourceFiles, uploadTranslations } from "generaltranslation/api";
4
+ //#region src/utils/api.ts
5
+ let client;
6
+ let configuredClientConfig;
7
+ let customMapping;
8
+ function getConfiguredClient() {
9
+ if (!client || !configuredClientConfig) throw new Error("API client not configured — call configureApiClient first");
10
+ return {
11
+ client,
12
+ config: configuredClientConfig
13
+ };
14
+ }
15
+ function getClient() {
16
+ return getConfiguredClient().client;
17
+ }
18
+ function getClientConfig() {
19
+ return getConfiguredClient().config;
20
+ }
21
+ function configureApiClient(config) {
22
+ const { customMapping: mapping, ...clientConfig } = config;
23
+ client = createApiClient(clientConfig);
24
+ configuredClientConfig = clientConfig;
25
+ customMapping = mapping;
26
+ }
27
+ const api = {
28
+ async queryBranchData(body) {
29
+ return unwrapApiResult(await getBranchInfo({
30
+ body,
31
+ client: getClient()
32
+ }));
33
+ },
34
+ async createBranch(body) {
35
+ return unwrapApiResult(await createBranch({
36
+ body,
37
+ client: getClient()
38
+ }));
39
+ },
40
+ async queryFileData(body) {
41
+ const result = unwrapApiResult(await getFileInfo({
42
+ body: {
43
+ ...body,
44
+ translatedFiles: body.translatedFiles?.map((file) => ({
45
+ ...file,
46
+ locale: resolveCanonicalLocale(file.locale, customMapping)
47
+ }))
48
+ },
49
+ client: getClient()
50
+ }));
51
+ return {
52
+ ...result,
53
+ translatedFiles: result.translatedFiles.map((file) => ({
54
+ ...file,
55
+ locale: resolveAliasLocale(file.locale, customMapping)
56
+ })),
57
+ sourceFiles: result.sourceFiles.map((file) => ({
58
+ ...file,
59
+ sourceLocale: resolveAliasLocale(file.sourceLocale, customMapping),
60
+ locales: file.locales.map((locale) => resolveAliasLocale(locale, customMapping))
61
+ }))
62
+ };
63
+ },
64
+ async checkJobStatus(jobIds) {
65
+ return unwrapApiResult(await getTranslationJobInfo({
66
+ body: { jobIds },
67
+ client: getClient()
68
+ }));
69
+ },
70
+ async getProjectInfo(timeoutMs) {
71
+ const config = getClientConfig();
72
+ if (!config.projectId) throw new Error("Project ID is required to fetch project information");
73
+ return unwrapApiResult(await getProjectInfo({
74
+ client: createApiClient({
75
+ ...config,
76
+ timeoutMs
77
+ }),
78
+ path: { projectId: config.projectId }
79
+ }));
80
+ },
81
+ resolveAliasLocale(locale) {
82
+ return resolveAliasLocale(locale, customMapping);
83
+ },
84
+ async downloadFileBatch(files) {
85
+ if (files.length === 0) return {
86
+ files: [],
87
+ count: 0,
88
+ pending: []
89
+ };
90
+ const request = async (batch) => unwrapApiResult(await downloadFiles({
91
+ body: batch.map((file) => ({
92
+ ...file,
93
+ locale: file.locale ? resolveCanonicalLocale(file.locale, customMapping) : void 0
94
+ })),
95
+ client: getClient()
96
+ }));
97
+ const responses = await processBatches(files, async (batch) => [await request(batch)]);
98
+ return {
99
+ files: responses.flatMap((response) => response.files.map((file) => ({
100
+ ...file,
101
+ ...file.locale && { locale: resolveAliasLocale(file.locale, customMapping) },
102
+ data: decodeFileContent(file.data, file.fileFormat)
103
+ }))),
104
+ count: responses.reduce((count, response) => count + response.count, 0),
105
+ pending: responses.flatMap((response) => response.pending ?? [])
106
+ };
107
+ },
108
+ async publishFiles(files) {
109
+ return unwrapApiResult(await publishFiles({
110
+ body: { files },
111
+ client: getClient()
112
+ }));
113
+ },
114
+ async submitUserEditDiffs(body) {
115
+ return processBatches(body.diffs, async (diffs) => [unwrapApiResult(await submitUserEditDiffs({
116
+ body: {
117
+ projectId: body.projectId,
118
+ diffs: diffs.map((diff) => ({
119
+ ...diff,
120
+ locale: resolveCanonicalLocale(diff.locale, customMapping)
121
+ }))
122
+ },
123
+ client: getClient()
124
+ }))]);
125
+ },
126
+ async createTag(body) {
127
+ return unwrapApiResult(await createTag({
128
+ body,
129
+ client: getClient()
130
+ }));
131
+ },
132
+ async createProject(body) {
133
+ return unwrapApiResult(await createProject({
134
+ body: {
135
+ ...body,
136
+ defaultLocale: resolveCanonicalLocale(body.defaultLocale, customMapping)
137
+ },
138
+ client: getClient()
139
+ }));
140
+ },
141
+ async getOrphanedFiles(branchId, fileIds) {
142
+ const request = async (batch) => unwrapApiResult(await getOrphanedFiles({
143
+ body: {
144
+ branchId,
145
+ fileIds: batch
146
+ },
147
+ client: getClient()
148
+ }));
149
+ if (fileIds.length === 0) return request([]);
150
+ const results = await processBatches(fileIds, async (batch) => [await request(batch)]);
151
+ const orphanedFiles = new Map(results[0].orphanedFiles.map((file) => [file.fileId, file]));
152
+ for (const result of results.slice(1)) {
153
+ const batchFileIds = new Set(result.orphanedFiles.map((file) => file.fileId));
154
+ for (const fileId of orphanedFiles.keys()) if (!batchFileIds.has(fileId)) orphanedFiles.delete(fileId);
155
+ }
156
+ return { orphanedFiles: [...orphanedFiles.values()] };
157
+ },
158
+ async processFileMoves(moves, options) {
159
+ const result = await processBatches(moves, async (batch) => {
160
+ return unwrapApiResult(await processFileMoves({
161
+ body: {
162
+ branchId: options.branchId,
163
+ moves: batch
164
+ },
165
+ client: getClient()
166
+ })).results;
167
+ });
168
+ const succeeded = result.filter(({ success }) => success).length;
169
+ return {
170
+ results: result,
171
+ summary: {
172
+ total: moves.length,
173
+ succeeded,
174
+ failed: result.length - succeeded
175
+ }
176
+ };
177
+ },
178
+ async setupProject(files, options = {}) {
179
+ return unwrapApiResult(await generateProjectContext({
180
+ body: {
181
+ files: files.map(({ branchId, fileId, versionId }) => ({
182
+ branchId,
183
+ fileId,
184
+ versionId
185
+ })),
186
+ locales: options.locales?.map((locale) => resolveCanonicalLocale(locale, customMapping)),
187
+ force: options.force
188
+ },
189
+ client: getClient()
190
+ }));
191
+ },
192
+ async awaitJobs(jobIds, options) {
193
+ return pollJobs(jobIds, async (pendingJobIds, signal) => unwrapApiResult(await getTranslationJobInfo({
194
+ body: { jobIds: pendingJobIds },
195
+ client: getClient(),
196
+ signal
197
+ })), options);
198
+ },
199
+ async enqueueFiles(files, options) {
200
+ const targetLocales = options.targetLocales.map((locale) => resolveCanonicalLocale(locale, customMapping));
201
+ const result = await processBatches(files, async (batch) => {
202
+ const response = unwrapApiResult(await enqueueFileTranslations({
203
+ body: {
204
+ files: batch.map(({ branchId, fileId, versionId, fileName, transformFormat }) => ({
205
+ branchId,
206
+ fileId,
207
+ versionId,
208
+ fileName,
209
+ transformFormat
210
+ })),
211
+ targetLocales,
212
+ sourceLocale: options.sourceLocale ? resolveCanonicalLocale(options.sourceLocale, customMapping) : void 0,
213
+ modelProvider: options.modelProvider,
214
+ force: options.force
215
+ },
216
+ client: getClient()
217
+ }));
218
+ const jobData = "jobData" in response ? response.jobData : response.data;
219
+ return Object.entries(jobData);
220
+ });
221
+ return {
222
+ jobData: Object.fromEntries(result),
223
+ locales: targetLocales,
224
+ message: `Successfully enqueued ${result.length} file translation jobs in ${Math.ceil(files.length / DEFAULT_BATCH_SIZE)} batch(es)`
225
+ };
226
+ },
227
+ async uploadSourceFiles(files, options) {
228
+ const sourceLocale = resolveCanonicalLocale(options.sourceLocale, customMapping);
229
+ return { uploadedFiles: await processBatches(files, async (batch) => {
230
+ return unwrapApiResult(await uploadSourceFiles({
231
+ body: {
232
+ data: batch.map(({ source }) => ({ source: {
233
+ ...source,
234
+ content: encodeFileContent(source.content, source.fileFormat),
235
+ locale: resolveCanonicalLocale(source.locale, customMapping)
236
+ } })),
237
+ sourceLocale
238
+ },
239
+ client: getClient()
240
+ })).uploadedFiles;
241
+ }) };
242
+ },
243
+ async uploadTranslations(files, options) {
244
+ return { uploadedFiles: await processBatches(files, async (batch) => {
245
+ return unwrapApiResult(await uploadTranslations({
246
+ body: {
247
+ data: batch.map(({ source, translations }) => ({
248
+ source: {
249
+ ...source,
250
+ content: encodeFileContent(source.content, source.fileFormat)
251
+ },
252
+ translations: translations.map((translation) => ({
253
+ ...translation,
254
+ locale: resolveCanonicalLocale(translation.locale, customMapping),
255
+ content: encodeFileContent(translation.content, translation.fileFormat)
256
+ }))
257
+ })),
258
+ sourceLocale: resolveCanonicalLocale(options.sourceLocale, customMapping)
259
+ },
260
+ client: getClient()
261
+ })).uploadedFiles;
262
+ }) };
263
+ }
264
+ };
265
+ //#endregion
266
+ export { api, configureApiClient };
267
+
268
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","names":[],"sources":["../../src/utils/api.ts"],"sourcesContent":["import {\n resolveAliasLocale,\n resolveCanonicalLocale,\n} from '@generaltranslation/format';\nimport type { CustomMapping } from '@generaltranslation/format/types';\nimport {\n createApiClient,\n createBranch,\n createProject,\n createTag,\n decodeFileContent,\n DEFAULT_BATCH_SIZE,\n downloadFiles,\n encodeFileContent,\n enqueueFileTranslations,\n generateProjectContext,\n getBranchInfo,\n getFileInfo,\n getOrphanedFiles,\n getProjectInfo,\n getTranslationJobInfo,\n pollJobs,\n processBatches,\n processFileMoves,\n publishFiles,\n submitUserEditDiffs,\n uploadSourceFiles,\n uploadTranslations,\n type ApiClientConfig,\n type AwaitJobsOptions,\n type CreateBranchData,\n type CreateProjectData,\n type CreateTagData,\n type DownloadFilesData,\n type EnqueueFileTranslationsData,\n type GenerateProjectContextData,\n type GetBranchInfoData,\n type GetFileInfoData,\n type ProcessFileMovesData,\n type PublishFilesData,\n type SubmitUserEditDiffsData,\n type UploadSourceFilesData,\n type UploadTranslationsData,\n} from 'generaltranslation/api';\nimport { unwrapApiResult } from 'generaltranslation/internal';\n\nlet client: ReturnType<typeof createApiClient> | undefined;\nlet configuredClientConfig: ApiClientConfig | undefined;\nlet customMapping: CustomMapping | undefined;\n\nfunction getConfiguredClient(): {\n client: ReturnType<typeof createApiClient>;\n config: ApiClientConfig;\n} {\n if (!client || !configuredClientConfig) {\n throw new Error(\n 'API client not configured — call configureApiClient first'\n );\n }\n return { client, config: configuredClientConfig };\n}\n\nfunction getClient(): ReturnType<typeof createApiClient> {\n return getConfiguredClient().client;\n}\n\nfunction getClientConfig(): ApiClientConfig {\n return getConfiguredClient().config;\n}\n\nexport function configureApiClient(\n config: ApiClientConfig & { customMapping?: CustomMapping }\n): void {\n const { customMapping: mapping, ...clientConfig } = config;\n client = createApiClient(clientConfig);\n // Retain the resolved config for the one project-info call that deliberately\n // uses a shorter timeout than normal translation requests.\n configuredClientConfig = clientConfig;\n customMapping = mapping;\n}\n\nexport const api = {\n async queryBranchData(body: GetBranchInfoData['body']) {\n return unwrapApiResult(await getBranchInfo({ body, client: getClient() }));\n },\n\n async createBranch(body: CreateBranchData['body']) {\n return unwrapApiResult(await createBranch({ body, client: getClient() }));\n },\n\n async queryFileData(body: GetFileInfoData['body']) {\n const result = unwrapApiResult(\n await getFileInfo({\n body: {\n ...body,\n translatedFiles: body.translatedFiles?.map((file) => ({\n ...file,\n locale: resolveCanonicalLocale(file.locale, customMapping),\n })),\n },\n client: getClient(),\n })\n );\n return {\n ...result,\n translatedFiles: result.translatedFiles.map((file) => ({\n ...file,\n locale: resolveAliasLocale(file.locale, customMapping),\n })),\n sourceFiles: result.sourceFiles.map((file) => ({\n ...file,\n sourceLocale: resolveAliasLocale(file.sourceLocale, customMapping),\n locales: file.locales.map((locale) =>\n resolveAliasLocale(locale, customMapping)\n ),\n })),\n };\n },\n\n async checkJobStatus(jobIds: string[]) {\n return unwrapApiResult(\n await getTranslationJobInfo({\n body: { jobIds },\n client: getClient(),\n })\n );\n },\n\n async getProjectInfo(timeoutMs: number) {\n const config = getClientConfig();\n if (!config.projectId) {\n throw new Error('Project ID is required to fetch project information');\n }\n const projectInfoClient = createApiClient({ ...config, timeoutMs });\n return unwrapApiResult(\n await getProjectInfo({\n client: projectInfoClient,\n path: { projectId: config.projectId },\n })\n );\n },\n\n resolveAliasLocale(locale: string) {\n return resolveAliasLocale(locale, customMapping);\n },\n\n async downloadFileBatch(files: DownloadFilesData['body']) {\n if (files.length === 0) return { files: [], count: 0, pending: [] };\n\n const request = async (batch: DownloadFilesData['body']) =>\n unwrapApiResult(\n await downloadFiles({\n body: batch.map((file) => ({\n ...file,\n locale: file.locale\n ? resolveCanonicalLocale(file.locale, customMapping)\n : undefined,\n })),\n client: getClient(),\n })\n );\n const responses = await processBatches(files, async (batch) => [\n await request(batch),\n ]);\n return {\n files: responses.flatMap((response) =>\n response.files.map((file) => ({\n ...file,\n ...(file.locale && {\n locale: resolveAliasLocale(file.locale, customMapping),\n }),\n data: decodeFileContent(file.data, file.fileFormat),\n }))\n ),\n count: responses.reduce((count, response) => count + response.count, 0),\n pending: responses.flatMap((response) => response.pending ?? []),\n };\n },\n\n async publishFiles(files: PublishFilesData['body']['files']) {\n return unwrapApiResult(\n await publishFiles({ body: { files }, client: getClient() })\n );\n },\n\n async submitUserEditDiffs(body: SubmitUserEditDiffsData['body']) {\n return processBatches(body.diffs, async (diffs) => [\n unwrapApiResult(\n await submitUserEditDiffs({\n body: {\n projectId: body.projectId,\n diffs: diffs.map((diff) => ({\n ...diff,\n locale: resolveCanonicalLocale(diff.locale, customMapping),\n })),\n },\n client: getClient(),\n })\n ),\n ]);\n },\n\n async createTag(body: CreateTagData['body']) {\n return unwrapApiResult(await createTag({ body, client: getClient() }));\n },\n\n async createProject(body: CreateProjectData['body']) {\n return unwrapApiResult(\n await createProject({\n body: {\n ...body,\n defaultLocale: resolveCanonicalLocale(\n body.defaultLocale,\n customMapping\n ),\n },\n client: getClient(),\n })\n );\n },\n\n // ponytail: duplicates core/src/translate/getOrphanedFiles.ts batching +\n // intersection; delete one copy when utils/gt.ts is removed.\n async getOrphanedFiles(branchId: string, fileIds: string[]) {\n const request = async (batch: string[]) =>\n unwrapApiResult(\n await getOrphanedFiles({\n body: { branchId, fileIds: batch },\n client: getClient(),\n })\n );\n\n if (fileIds.length === 0) return request([]);\n\n const results = await processBatches(fileIds, async (batch) => [\n await request(batch),\n ]);\n const orphanedFiles = new Map(\n results[0].orphanedFiles.map((file) => [file.fileId, file])\n );\n for (const result of results.slice(1)) {\n const batchFileIds = new Set(\n result.orphanedFiles.map((file) => file.fileId)\n );\n for (const fileId of orphanedFiles.keys()) {\n if (!batchFileIds.has(fileId)) orphanedFiles.delete(fileId);\n }\n }\n return { orphanedFiles: [...orphanedFiles.values()] };\n },\n\n async processFileMoves(\n moves: ProcessFileMovesData['body']['moves'],\n options: Pick<ProcessFileMovesData['body'], 'branchId'>\n ) {\n const result = await processBatches(moves, async (batch) => {\n const response = unwrapApiResult(\n await processFileMoves({\n body: { branchId: options.branchId, moves: batch },\n client: getClient(),\n })\n );\n return response.results;\n });\n const succeeded = result.filter(({ success }) => success).length;\n\n return {\n results: result,\n summary: {\n total: moves.length,\n succeeded,\n failed: result.length - succeeded,\n },\n };\n },\n\n async setupProject(\n files: GenerateProjectContextData['body']['files'],\n options: Omit<GenerateProjectContextData['body'], 'files'> = {}\n ) {\n return unwrapApiResult(\n await generateProjectContext({\n body: {\n files: files.map(({ branchId, fileId, versionId }) => ({\n branchId,\n fileId,\n versionId,\n })),\n locales: options.locales?.map((locale) =>\n resolveCanonicalLocale(locale, customMapping)\n ),\n force: options.force,\n },\n client: getClient(),\n })\n );\n },\n\n async awaitJobs(jobIds: readonly string[], options?: AwaitJobsOptions) {\n // Poll through unwrapApiResult so HTTP failures throw ApiError with the\n // status code instead of the decoded response body.\n return pollJobs(\n jobIds,\n async (pendingJobIds, signal) =>\n unwrapApiResult(\n await getTranslationJobInfo({\n body: { jobIds: pendingJobIds },\n client: getClient(),\n signal,\n })\n ),\n options\n );\n },\n\n async enqueueFiles(\n files: EnqueueFileTranslationsData['body']['files'],\n options: {\n sourceLocale?: string;\n targetLocales: string[];\n modelProvider?: string;\n force?: boolean;\n }\n ) {\n const targetLocales = options.targetLocales.map((locale) =>\n resolveCanonicalLocale(locale, customMapping)\n );\n const result = await processBatches(files, async (batch) => {\n const response = unwrapApiResult(\n await enqueueFileTranslations({\n body: {\n files: batch.map(\n ({ branchId, fileId, versionId, fileName, transformFormat }) => ({\n branchId,\n fileId,\n versionId,\n fileName,\n transformFormat,\n })\n ),\n targetLocales,\n sourceLocale: options.sourceLocale\n ? resolveCanonicalLocale(options.sourceLocale, customMapping)\n : undefined,\n // The CLI intentionally accepts custom model-provider strings beyond\n // the OpenAPI ANTHROPIC|OPENAI|XAI|GOOGLE enum; preserve wire behavior.\n modelProvider:\n options.modelProvider as EnqueueFileTranslationsData['body']['modelProvider'],\n force: options.force,\n },\n client: getClient(),\n })\n );\n const jobData = 'jobData' in response ? response.jobData : response.data;\n return Object.entries(jobData);\n });\n\n return {\n jobData: Object.fromEntries(result),\n locales: targetLocales,\n message: `Successfully enqueued ${result.length} file translation jobs in ${Math.ceil(files.length / DEFAULT_BATCH_SIZE)} batch(es)`,\n };\n },\n\n async uploadSourceFiles(\n files: Array<{\n source: UploadSourceFilesData['body']['data'][number]['source'];\n }>,\n options: { sourceLocale: string }\n ) {\n const sourceLocale = resolveCanonicalLocale(\n options.sourceLocale,\n customMapping\n );\n const result = await processBatches(files, async (batch) => {\n const response = unwrapApiResult(\n await uploadSourceFiles({\n body: {\n data: batch.map(({ source }) => ({\n source: {\n ...source,\n content: encodeFileContent(source.content, source.fileFormat),\n locale: resolveCanonicalLocale(source.locale, customMapping),\n },\n })),\n sourceLocale,\n },\n client: getClient(),\n })\n );\n return response.uploadedFiles;\n });\n\n return { uploadedFiles: result };\n },\n\n async uploadTranslations(\n files: UploadTranslationsData['body']['data'],\n options: { sourceLocale: string }\n ) {\n const result = await processBatches(files, async (batch) => {\n const response = unwrapApiResult(\n await uploadTranslations({\n body: {\n data: batch.map(({ source, translations }) => ({\n source: {\n ...source,\n content: encodeFileContent(source.content, source.fileFormat),\n },\n translations: translations.map((translation) => ({\n ...translation,\n locale: resolveCanonicalLocale(\n translation.locale,\n customMapping\n ),\n content: encodeFileContent(\n translation.content,\n translation.fileFormat\n ),\n })),\n })),\n sourceLocale: resolveCanonicalLocale(\n options.sourceLocale,\n customMapping\n ),\n },\n client: getClient(),\n })\n );\n return response.uploadedFiles;\n });\n\n return { uploadedFiles: result };\n },\n};\n\nexport type ApiClient = typeof api;\n"],"mappings":"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"}
@@ -1,12 +1,6 @@
1
+ import type { DownloadMeta } from '../state/recentDownloads.js';
1
2
  import type { Settings } from '../types/index.js';
2
- type DownloadMeta = {
3
- branchId: string;
4
- fileId: string;
5
- versionId: string;
6
- locale: string;
7
- };
8
3
  /**
9
4
  * Persist postprocessed content hashes for recently downloaded files into gt-lock.json.
10
5
  */
11
6
  export declare function persistPostProcessHashes(settings: Settings, includeFiles: Set<string> | undefined, downloadedMeta: Map<string, DownloadMeta>): void;
12
- export {};
@@ -1,4 +1,9 @@
1
+ import { logger } from "../console/logger.js";
1
2
  import { hashStringSync } from "./hash.js";
3
+ import { getRelative } from "../fs/findFilepath.js";
4
+ import { fileEncodingSkipReason } from "../console/index.js";
5
+ import { readFileContent } from "../fs/fileContent.js";
6
+ import { recordWarning } from "../state/translateWarnings.js";
2
7
  import { findOrCreateEntry, readLockfile, writeLockfile } from "../fs/config/downloadedVersions.js";
3
8
  import * as fs$1 from "node:fs";
4
9
  //#region src/utils/persistPostprocessHashes.ts
@@ -18,7 +23,16 @@ function persistPostProcessHashes(settings, includeFiles, downloadedMeta) {
18
23
  const meta = downloadedMeta.get(filePath);
19
24
  if (!meta) continue;
20
25
  if (!fs$1.existsSync(filePath)) continue;
21
- const hash = hashStringSync(fs$1.readFileSync(filePath, "utf8"));
26
+ let hash;
27
+ try {
28
+ hash = hashStringSync(readFileContent(filePath, meta.fileFormat));
29
+ } catch (error) {
30
+ const relativePath = getRelative(filePath);
31
+ const reason = fileEncodingSkipReason(error);
32
+ logger.warn(`Skipping ${relativePath}: ${reason}`);
33
+ recordWarning("skipped_file", relativePath, reason);
34
+ continue;
35
+ }
22
36
  const entry = findOrCreateEntry(entryMap, data.entries, meta.fileId, meta.versionId);
23
37
  const existing = entry.translations[meta.locale] || {};
24
38
  if (existing.postProcessHash !== hash) {
@@ -1 +1 @@
1
- {"version":3,"file":"persistPostprocessHashes.js","names":["fs"],"sources":["../../src/utils/persistPostprocessHashes.ts"],"sourcesContent":["import * as fs from 'node:fs';\nimport {\n findOrCreateEntry,\n readLockfile,\n writeLockfile,\n} from '../fs/config/downloadedVersions.js';\nimport { hashStringSync } from './hash.js';\nimport type { Settings } from '../types/index.js';\n\ntype DownloadMeta = {\n branchId: string;\n fileId: string;\n versionId: string;\n locale: string;\n};\n\n/**\n * Persist postprocessed content hashes for recently downloaded files into gt-lock.json.\n */\nexport function persistPostProcessHashes(\n settings: Settings,\n includeFiles: Set<string> | undefined,\n downloadedMeta: Map<string, DownloadMeta>\n): void {\n if (!includeFiles || includeFiles.size === 0 || downloadedMeta.size === 0) {\n return;\n }\n\n const branchId = findDownloadedBranchId(includeFiles, downloadedMeta);\n if (!branchId) return;\n\n const { data, entryMap, originalV1 } = readLockfile({\n ...settings,\n _branchId: branchId,\n });\n let lockUpdated = false;\n\n for (const filePath of includeFiles) {\n const meta = downloadedMeta.get(filePath);\n if (!meta) continue;\n if (!fs.existsSync(filePath)) continue;\n\n const content = fs.readFileSync(filePath, 'utf8');\n const hash = hashStringSync(content);\n\n const entry = findOrCreateEntry(\n entryMap,\n data.entries,\n meta.fileId,\n meta.versionId\n );\n\n const existing = entry.translations[meta.locale] || {};\n\n if (existing.postProcessHash !== hash) {\n entry.translations[meta.locale] = {\n ...existing,\n postProcessHash: hash,\n };\n lockUpdated = true;\n }\n }\n\n if (lockUpdated) {\n writeLockfile(data, originalV1);\n }\n}\n\nfunction findDownloadedBranchId(\n includeFiles: Set<string>,\n downloadedMeta: Map<string, DownloadMeta>\n): string | undefined {\n for (const filePath of includeFiles) {\n const meta = downloadedMeta.get(filePath);\n if (meta) return meta.branchId;\n }\n return undefined;\n}\n"],"mappings":";;;;;;;AAmBA,SAAgB,yBACd,UACA,cACA,gBACM;AACN,KAAI,CAAC,gBAAgB,aAAa,SAAS,KAAK,eAAe,SAAS,EACtE;CAGF,MAAM,WAAW,uBAAuB,cAAc,eAAe;AACrE,KAAI,CAAC,SAAU;CAEf,MAAM,EAAE,MAAM,UAAU,eAAe,aAAa;EAClD,GAAG;EACH,WAAW;EACZ,CAAC;CACF,IAAI,cAAc;AAElB,MAAK,MAAM,YAAY,cAAc;EACnC,MAAM,OAAO,eAAe,IAAI,SAAS;AACzC,MAAI,CAAC,KAAM;AACX,MAAI,CAACA,KAAG,WAAW,SAAS,CAAE;EAG9B,MAAM,OAAO,eADGA,KAAG,aAAa,UAAU,OACP,CAAC;EAEpC,MAAM,QAAQ,kBACZ,UACA,KAAK,SACL,KAAK,QACL,KAAK,UACN;EAED,MAAM,WAAW,MAAM,aAAa,KAAK,WAAW,EAAE;AAEtD,MAAI,SAAS,oBAAoB,MAAM;AACrC,SAAM,aAAa,KAAK,UAAU;IAChC,GAAG;IACH,iBAAiB;IAClB;AACD,iBAAc;;;AAIlB,KAAI,YACF,eAAc,MAAM,WAAW;;AAInC,SAAS,uBACP,cACA,gBACoB;AACpB,MAAK,MAAM,YAAY,cAAc;EACnC,MAAM,OAAO,eAAe,IAAI,SAAS;AACzC,MAAI,KAAM,QAAO,KAAK"}
1
+ {"version":3,"file":"persistPostprocessHashes.js","names":["fs"],"sources":["../../src/utils/persistPostprocessHashes.ts"],"sourcesContent":["import * as fs from 'node:fs';\nimport {\n findOrCreateEntry,\n readLockfile,\n writeLockfile,\n} from '../fs/config/downloadedVersions.js';\nimport { hashStringSync } from './hash.js';\nimport { fileEncodingSkipReason } from '../console/index.js';\nimport { logger } from '../console/logger.js';\nimport { getRelative } from '../fs/findFilepath.js';\nimport { recordWarning } from '../state/translateWarnings.js';\nimport { readFileContent } from '../fs/fileContent.js';\nimport type { DownloadMeta } from '../state/recentDownloads.js';\nimport type { Settings } from '../types/index.js';\n\n/**\n * Persist postprocessed content hashes for recently downloaded files into gt-lock.json.\n */\nexport function persistPostProcessHashes(\n settings: Settings,\n includeFiles: Set<string> | undefined,\n downloadedMeta: Map<string, DownloadMeta>\n): void {\n if (!includeFiles || includeFiles.size === 0 || downloadedMeta.size === 0) {\n return;\n }\n\n const branchId = findDownloadedBranchId(includeFiles, downloadedMeta);\n if (!branchId) return;\n\n const { data, entryMap, originalV1 } = readLockfile({\n ...settings,\n _branchId: branchId,\n });\n let lockUpdated = false;\n\n for (const filePath of includeFiles) {\n const meta = downloadedMeta.get(filePath);\n if (!meta) continue;\n if (!fs.existsSync(filePath)) continue;\n\n // The hash stands for the file's pipeline content, which is what every\n // other producer and consumer of it compares against.\n let hash: string;\n try {\n hash = hashStringSync(readFileContent(filePath, meta.fileFormat));\n } catch (error) {\n // The translation is already written; failing here would lose the whole\n // run's lockfile update over one unreadable file. Skip it and report it\n const relativePath = getRelative(filePath);\n const reason = fileEncodingSkipReason(error);\n logger.warn(`Skipping ${relativePath}: ${reason}`);\n recordWarning('skipped_file', relativePath, reason);\n continue;\n }\n\n const entry = findOrCreateEntry(\n entryMap,\n data.entries,\n meta.fileId,\n meta.versionId\n );\n\n const existing = entry.translations[meta.locale] || {};\n\n if (existing.postProcessHash !== hash) {\n entry.translations[meta.locale] = {\n ...existing,\n postProcessHash: hash,\n };\n lockUpdated = true;\n }\n }\n\n if (lockUpdated) {\n writeLockfile(data, originalV1);\n }\n}\n\nfunction findDownloadedBranchId(\n includeFiles: Set<string>,\n downloadedMeta: Map<string, DownloadMeta>\n): string | undefined {\n for (const filePath of includeFiles) {\n const meta = downloadedMeta.get(filePath);\n if (meta) return meta.branchId;\n }\n return undefined;\n}\n"],"mappings":";;;;;;;;;;;;AAkBA,SAAgB,yBACd,UACA,cACA,gBACM;AACN,KAAI,CAAC,gBAAgB,aAAa,SAAS,KAAK,eAAe,SAAS,EACtE;CAGF,MAAM,WAAW,uBAAuB,cAAc,eAAe;AACrE,KAAI,CAAC,SAAU;CAEf,MAAM,EAAE,MAAM,UAAU,eAAe,aAAa;EAClD,GAAG;EACH,WAAW;EACZ,CAAC;CACF,IAAI,cAAc;AAElB,MAAK,MAAM,YAAY,cAAc;EACnC,MAAM,OAAO,eAAe,IAAI,SAAS;AACzC,MAAI,CAAC,KAAM;AACX,MAAI,CAACA,KAAG,WAAW,SAAS,CAAE;EAI9B,IAAI;AACJ,MAAI;AACF,UAAO,eAAe,gBAAgB,UAAU,KAAK,WAAW,CAAC;WAC1D,OAAO;GAGd,MAAM,eAAe,YAAY,SAAS;GAC1C,MAAM,SAAS,uBAAuB,MAAM;AAC5C,UAAO,KAAK,YAAY,aAAa,IAAI,SAAS;AAClD,iBAAc,gBAAgB,cAAc,OAAO;AACnD;;EAGF,MAAM,QAAQ,kBACZ,UACA,KAAK,SACL,KAAK,QACL,KAAK,UACN;EAED,MAAM,WAAW,MAAM,aAAa,KAAK,WAAW,EAAE;AAEtD,MAAI,SAAS,oBAAoB,MAAM;AACrC,SAAM,aAAa,KAAK,UAAU;IAChC,GAAG;IACH,iBAAiB;IAClB;AACD,iBAAc;;;AAIlB,KAAI,YACF,eAAc,MAAM,WAAW;;AAInC,SAAS,uBACP,cACA,gBACoB;AACpB,MAAK,MAAM,YAAY,cAAc;EACnC,MAAM,OAAO,eAAe,IAAI,SAAS;AACzC,MAAI,KAAM,QAAO,KAAK"}
@@ -1,7 +1,7 @@
1
1
  import { logger } from "../console/logger.js";
2
- import { gt } from "../utils/gt.js";
3
2
  import { logErrorAndExit } from "../console/logging.js";
4
3
  import { branchResolutionError } from "../console/index.js";
4
+ import { api } from "../utils/api.js";
5
5
  import { BranchStep } from "./steps/BranchStep.js";
6
6
  import { recordWarning } from "../state/translateWarnings.js";
7
7
  import { clearLocaleDirs } from "../fs/clearLocaleDirs.js";
@@ -24,7 +24,7 @@ import path from "node:path";
24
24
  */
25
25
  async function runDownloadWorkflow({ fileVersionData, jobData, branchData, locales, timeoutDuration, resolveOutputPath, options, inlineLibrary, forceRetranslation, forceDownload }) {
26
26
  if (!branchData) {
27
- const branchResult = await new BranchStep(gt, options).run();
27
+ const branchResult = await new BranchStep(api, options).run();
28
28
  if (!branchResult) return logErrorAndExit(branchResolutionError);
29
29
  branchData = branchResult;
30
30
  }
@@ -41,7 +41,7 @@ async function runDownloadWorkflow({ fileVersionData, jobData, branchData, local
41
41
  };
42
42
  let pollTimedOut = false;
43
43
  if (jobData) {
44
- const pollResult = await new PollTranslationJobsStep(gt, inlineLibrary).run({
44
+ const pollResult = await new PollTranslationJobsStep(api, inlineLibrary).run({
45
45
  fileTracker,
46
46
  fileQueryData,
47
47
  jobData,
@@ -65,7 +65,7 @@ async function runDownloadWorkflow({ fileVersionData, jobData, branchData, local
65
65
  resolveOutputPath,
66
66
  options
67
67
  });
68
- const downloadResult = await new DownloadTranslationsStep(gt, settingsForBranch, inlineLibrary).run({
68
+ const downloadResult = await new DownloadTranslationsStep(api, settingsForBranch, inlineLibrary).run({
69
69
  fileTracker,
70
70
  resolveOutputPath,
71
71
  forceDownload