@intlayer/cli 6.0.2 → 6.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +249 -56
  2. package/dist/cjs/build.cjs +8 -0
  3. package/dist/cjs/build.cjs.map +1 -1
  4. package/dist/cjs/cli.cjs +32 -5
  5. package/dist/cjs/cli.cjs.map +1 -1
  6. package/dist/cjs/fill/index.cjs +114 -59
  7. package/dist/cjs/fill/index.cjs.map +1 -1
  8. package/dist/cjs/index.cjs +4 -2
  9. package/dist/cjs/index.cjs.map +1 -1
  10. package/dist/cjs/liveSync.cjs +6 -30
  11. package/dist/cjs/liveSync.cjs.map +1 -1
  12. package/dist/cjs/pull.cjs +1 -1
  13. package/dist/cjs/pull.cjs.map +1 -1
  14. package/dist/cjs/push/pullLog.cjs.map +1 -0
  15. package/dist/cjs/{push.cjs → push/push.cjs} +2 -2
  16. package/dist/cjs/push/push.cjs.map +1 -0
  17. package/dist/cjs/reviewDoc.cjs +148 -96
  18. package/dist/cjs/reviewDoc.cjs.map +1 -1
  19. package/dist/cjs/test/index.cjs +2 -0
  20. package/dist/cjs/test/index.cjs.map +1 -1
  21. package/dist/cjs/translateDoc.cjs +39 -42
  22. package/dist/cjs/translateDoc.cjs.map +1 -1
  23. package/dist/cjs/utils/mapChunksBetweenFiles.cjs +145 -0
  24. package/dist/cjs/utils/mapChunksBetweenFiles.cjs.map +1 -0
  25. package/dist/cjs/watch.cjs +42 -0
  26. package/dist/cjs/watch.cjs.map +1 -0
  27. package/dist/esm/build.mjs +12 -1
  28. package/dist/esm/build.mjs.map +1 -1
  29. package/dist/esm/cli.mjs +32 -5
  30. package/dist/esm/cli.mjs.map +1 -1
  31. package/dist/esm/fill/index.mjs +114 -59
  32. package/dist/esm/fill/index.mjs.map +1 -1
  33. package/dist/esm/index.mjs +2 -1
  34. package/dist/esm/index.mjs.map +1 -1
  35. package/dist/esm/liveSync.mjs +10 -31
  36. package/dist/esm/liveSync.mjs.map +1 -1
  37. package/dist/esm/pull.mjs +1 -1
  38. package/dist/esm/pull.mjs.map +1 -1
  39. package/dist/esm/push/pullLog.mjs.map +1 -0
  40. package/dist/esm/{push.mjs → push/push.mjs} +2 -2
  41. package/dist/esm/push/push.mjs.map +1 -0
  42. package/dist/esm/reviewDoc.mjs +151 -97
  43. package/dist/esm/reviewDoc.mjs.map +1 -1
  44. package/dist/esm/test/index.mjs +2 -0
  45. package/dist/esm/test/index.mjs.map +1 -1
  46. package/dist/esm/translateDoc.mjs +41 -43
  47. package/dist/esm/translateDoc.mjs.map +1 -1
  48. package/dist/esm/utils/mapChunksBetweenFiles.mjs +121 -0
  49. package/dist/esm/utils/mapChunksBetweenFiles.mjs.map +1 -0
  50. package/dist/esm/watch.mjs +21 -0
  51. package/dist/esm/watch.mjs.map +1 -0
  52. package/dist/types/build.d.ts +2 -0
  53. package/dist/types/build.d.ts.map +1 -1
  54. package/dist/types/cli.d.ts.map +1 -1
  55. package/dist/types/fill/index.d.ts.map +1 -1
  56. package/dist/types/index.d.ts +2 -1
  57. package/dist/types/index.d.ts.map +1 -1
  58. package/dist/types/liveSync.d.ts +1 -1
  59. package/dist/types/liveSync.d.ts.map +1 -1
  60. package/dist/types/push/pullLog.d.ts.map +1 -0
  61. package/dist/types/push/push.d.ts.map +1 -0
  62. package/dist/types/reviewDoc.d.ts +2 -2
  63. package/dist/types/reviewDoc.d.ts.map +1 -1
  64. package/dist/types/test/index.d.ts.map +1 -1
  65. package/dist/types/translateDoc.d.ts.map +1 -1
  66. package/dist/types/utils/mapChunksBetweenFiles.d.ts +12 -0
  67. package/dist/types/utils/mapChunksBetweenFiles.d.ts.map +1 -0
  68. package/dist/types/watch.d.ts +13 -0
  69. package/dist/types/watch.d.ts.map +1 -0
  70. package/package.json +21 -21
  71. package/dist/cjs/pullLog.cjs.map +0 -1
  72. package/dist/cjs/push.cjs.map +0 -1
  73. package/dist/esm/pullLog.mjs.map +0 -1
  74. package/dist/esm/push.mjs.map +0 -1
  75. package/dist/types/pullLog.d.ts.map +0 -1
  76. package/dist/types/push.d.ts.map +0 -1
  77. /package/dist/cjs/{pullLog.cjs → push/pullLog.cjs} +0 -0
  78. /package/dist/esm/{pullLog.mjs → push/pullLog.mjs} +0 -0
  79. /package/dist/types/{pullLog.d.ts → push/pullLog.d.ts} +0 -0
  80. /package/dist/types/{push.d.ts → push/push.d.ts} +0 -0
@@ -63,6 +63,8 @@ const fill = async (options) => {
63
63
  const maxLocaleLength = Math.max(
64
64
  ...locales.map((locale) => (0, import_chokidar.formatLocale)(locale).length)
65
65
  );
66
+ const dictionariesRecord = (0, import_dictionaries_entry.getDictionaries)(configuration);
67
+ const translationTasks = [];
66
68
  for (const targetUnmergedDictionary of targetUnmergedDictionaries) {
67
69
  const dictionaryPreset = (0, import_config.colon)(
68
70
  [
@@ -73,7 +75,6 @@ const fill = async (options) => {
73
75
  { colSize: maxKeyLength + 6 }
74
76
  );
75
77
  const dictionaryKey = targetUnmergedDictionary.key;
76
- const dictionariesRecord = (0, import_dictionaries_entry.getDictionaries)(configuration);
77
78
  const mainDictionaryToProcess = dictionariesRecord[dictionaryKey];
78
79
  const sourceLocale = targetUnmergedDictionary.locale ?? baseLocale;
79
80
  if (!mainDictionaryToProcess) {
@@ -115,7 +116,6 @@ const fill = async (options) => {
115
116
  );
116
117
  continue;
117
118
  }
118
- const result = [];
119
119
  let outputLocalesList = outputLocales;
120
120
  if (mode === "complete") {
121
121
  const missingLocales = (0, import_core.getMissingLocalesContent)(
@@ -138,70 +138,117 @@ const fill = async (options) => {
138
138
  );
139
139
  continue;
140
140
  }
141
- const translationResults = await (0, import_chokidar.parallelize)(
142
- outputLocalesList,
143
- async (targetLocale) => {
144
- const localePreset = (0, import_config.colon)(
145
- [
146
- (0, import_config.colorize)("[", import_config.ANSIColors.GREY_DARK),
147
- (0, import_chokidar.formatLocale)(targetLocale),
148
- (0, import_config.colorize)("]", import_config.ANSIColors.GREY_DARK)
149
- ].join(""),
150
- { colSize: maxLocaleLength }
151
- );
152
- appLogger(
153
- `${dictionaryPreset}${localePreset} Preparing translation for dictionary from ${(0, import_chokidar.formatLocale)(sourceLocale)} to ${(0, import_chokidar.formatLocale)(targetLocale)}`,
154
- {
155
- level: "info"
156
- }
157
- );
158
- const presetOutputContent = (0, import_core.getLocalisedContent)(
159
- mainDictionaryToProcess,
160
- targetLocale,
161
- { dictionaryKey, keyPath: [] }
162
- );
163
- try {
164
- const translationResult = await intlayerAPI.ai.translateJSON({
165
- entryFileContent: sourceLocaleContent.content,
166
- // Should be JSON, ensure getLocalisedContent provides this.
167
- presetOutputContent: presetOutputContent.content,
168
- // Should be JSON
169
- dictionaryDescription: mainDictionaryToProcess.description ?? "",
170
- entryLocale: sourceLocale,
171
- outputLocale: targetLocale,
172
- mode,
173
- aiOptions: options.aiOptions
174
- });
175
- if (!translationResult.data?.fileContent) {
176
- appLogger(`${dictionaryPreset}${localePreset} No content result`, {
177
- level: "error"
178
- });
179
- return null;
180
- }
181
- const processedPerLocaleDictionary = (0, import_chokidar.processPerLocaleDictionary)({
182
- ...mainDictionaryToProcess,
183
- content: translationResult.data?.fileContent,
184
- locale: targetLocale
185
- });
186
- return processedPerLocaleDictionary;
187
- } catch (error) {
141
+ for (const targetLocale of outputLocalesList) {
142
+ const localePreset = (0, import_config.colon)(
143
+ [
144
+ (0, import_config.colorize)("[", import_config.ANSIColors.GREY_DARK),
145
+ (0, import_chokidar.formatLocale)(targetLocale),
146
+ (0, import_config.colorize)("]", import_config.ANSIColors.GREY_DARK)
147
+ ].join(""),
148
+ { colSize: maxLocaleLength }
149
+ );
150
+ translationTasks.push({
151
+ dictionaryKey,
152
+ sourceLocale,
153
+ targetLocale,
154
+ dictionaryPreset,
155
+ localePreset
156
+ });
157
+ }
158
+ }
159
+ const translationResults = await (0, import_chokidar.parallelize)(
160
+ translationTasks,
161
+ async (task) => {
162
+ const mainDictionaryToProcess = dictionariesRecord[task.dictionaryKey];
163
+ appLogger(
164
+ `${task.dictionaryPreset}${task.localePreset} Preparing translation for dictionary from ${(0, import_chokidar.formatLocale)(task.sourceLocale)} to ${(0, import_chokidar.formatLocale)(task.targetLocale)}`,
165
+ {
166
+ level: "info"
167
+ }
168
+ );
169
+ const sourceLocaleContent = (0, import_core.getFilterTranslationsOnlyContent)(
170
+ mainDictionaryToProcess,
171
+ task.sourceLocale,
172
+ { dictionaryKey: task.dictionaryKey, keyPath: [] }
173
+ );
174
+ const presetOutputContent = (0, import_core.getLocalisedContent)(
175
+ mainDictionaryToProcess,
176
+ task.targetLocale,
177
+ { dictionaryKey: task.dictionaryKey, keyPath: [] }
178
+ );
179
+ try {
180
+ const translationResult = await intlayerAPI.ai.translateJSON({
181
+ entryFileContent: sourceLocaleContent.content,
182
+ presetOutputContent: presetOutputContent.content,
183
+ dictionaryDescription: mainDictionaryToProcess.description ?? "",
184
+ entryLocale: task.sourceLocale,
185
+ outputLocale: task.targetLocale,
186
+ mode,
187
+ aiOptions: options.aiOptions
188
+ });
189
+ if (!translationResult.data?.fileContent) {
188
190
  appLogger(
189
- `${dictionaryPreset}${localePreset} ${(0, import_config.colorize)("Error filling", import_config.ANSIColors.RED)}: ` + error,
191
+ `${task.dictionaryPreset}${task.localePreset} No content result`,
190
192
  {
191
193
  level: "error"
192
194
  }
193
195
  );
194
- return null;
196
+ return { key: task.dictionaryKey, result: null };
195
197
  }
196
- },
197
- options.nbConcurrentTranslations ?? NB_CONCURRENT_TRANSLATIONS
198
- );
199
- translationResults.forEach((translationResult) => {
200
- if (translationResult) {
201
- result.push(translationResult);
198
+ const processedPerLocaleDictionary = (0, import_chokidar.processPerLocaleDictionary)({
199
+ ...mainDictionaryToProcess,
200
+ content: translationResult.data?.fileContent,
201
+ locale: task.targetLocale
202
+ });
203
+ return {
204
+ key: task.dictionaryKey,
205
+ result: processedPerLocaleDictionary
206
+ };
207
+ } catch (error) {
208
+ appLogger(
209
+ `${task.dictionaryPreset}${task.localePreset} ${(0, import_config.colorize)("Error filling", import_config.ANSIColors.RED)}: ` + error,
210
+ {
211
+ level: "error"
212
+ }
213
+ );
214
+ return { key: task.dictionaryKey, result: null };
202
215
  }
203
- });
204
- const dictionaryToMerge = mode === "review" ? [...result, mainDictionaryToProcess] : [mainDictionaryToProcess, ...result];
216
+ },
217
+ options.nbConcurrentTranslations ?? NB_CONCURRENT_TRANSLATIONS
218
+ );
219
+ const resultsByDictionary = /* @__PURE__ */ new Map();
220
+ for (const item of translationResults) {
221
+ if (item?.result) {
222
+ const list = resultsByDictionary.get(item.key) ?? [];
223
+ list.push(item.result);
224
+ resultsByDictionary.set(item.key, list);
225
+ }
226
+ }
227
+ for (const targetUnmergedDictionary of targetUnmergedDictionaries) {
228
+ const dictionaryKey = targetUnmergedDictionary.key;
229
+ const mainDictionaryToProcess = dictionariesRecord[dictionaryKey];
230
+ const sourceLocale = targetUnmergedDictionary.locale ?? baseLocale;
231
+ if (!mainDictionaryToProcess || !targetUnmergedDictionary.filePath) {
232
+ continue;
233
+ }
234
+ let outputLocalesList = outputLocales;
235
+ if (mode === "complete") {
236
+ const missingLocales = (0, import_core.getMissingLocalesContent)(
237
+ mainDictionaryToProcess,
238
+ outputLocales,
239
+ {
240
+ dictionaryKey: mainDictionaryToProcess.key,
241
+ keyPath: [],
242
+ plugins: []
243
+ }
244
+ );
245
+ outputLocalesList = missingLocales;
246
+ }
247
+ if (outputLocalesList.length === 0) {
248
+ continue;
249
+ }
250
+ const perLocaleResults = resultsByDictionary.get(dictionaryKey) ?? [];
251
+ const dictionaryToMerge = mode === "review" ? [...perLocaleResults, mainDictionaryToProcess] : [mainDictionaryToProcess, ...perLocaleResults];
205
252
  const mergedResults = (0, import_chokidar.mergeDictionaries)(dictionaryToMerge);
206
253
  let formattedDict = targetUnmergedDictionary;
207
254
  if (formattedDict.locale) {
@@ -232,6 +279,14 @@ const fill = async (options) => {
232
279
  formattedDict.filePath
233
280
  );
234
281
  if (formattedDict.filePath) {
282
+ const dictionaryPreset = (0, import_config.colon)(
283
+ [
284
+ (0, import_config.colorize)(" - [", import_config.ANSIColors.GREY_DARK),
285
+ (0, import_config.colorizeKey)(targetUnmergedDictionary.key),
286
+ (0, import_config.colorize)("]", import_config.ANSIColors.GREY_DARK)
287
+ ].join(""),
288
+ { colSize: maxKeyLength + 6 }
289
+ );
235
290
  appLogger(
236
291
  `${dictionaryPreset} Content declaration written to ${(0, import_chokidar.formatPath)(formattedDict.filePath)}`,
237
292
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/fill/index.ts"],"sourcesContent":["import { AIOptions, getIntlayerAPIProxy } from '@intlayer/api'; // Importing only getAiAPI for now\nimport {\n formatLocale,\n formatPath,\n ListGitFilesOptions,\n mergeDictionaries,\n parallelize,\n prepareIntlayer,\n processPerLocaleDictionary,\n reduceDictionaryContent,\n writeContentDeclaration,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colon,\n colorize,\n colorizeKey,\n colorizePath,\n getAppLogger,\n getConfiguration,\n Locales,\n} from '@intlayer/config';\nimport {\n type ContentNode,\n type Dictionary,\n getFilterTranslationsOnlyContent,\n getLocalisedContent,\n getMissingLocalesContent,\n} from '@intlayer/core';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport { relative } from 'path';\nimport {\n ensureArray,\n GetTargetDictionaryOptions,\n getTargetUnmergedDictionaries,\n} from '../getTargetDictionary';\nimport { checkAIAccess } from '../utils/checkAccess';\nimport { autoFill } from './autoFill';\n\nconst NB_CONCURRENT_TRANSLATIONS = 8;\n\n// Arguments for the fill function\nexport type FillOptions = {\n sourceLocale?: Locales;\n outputLocales?: Locales | Locales[];\n mode?: 'complete' | 'review';\n gitOptions?: ListGitFilesOptions;\n aiOptions?: AIOptions; // Added aiOptions to be passed to translateJSON\n verbose?: boolean;\n nbConcurrentTranslations?: number;\n build?: boolean;\n} & GetTargetDictionaryOptions;\n\n/**\n * Fill translations based on the provided options.\n */\nexport const fill = async (options: FillOptions): Promise<void> => {\n const configuration = getConfiguration(options.configOptions);\n const appLogger = getAppLogger(configuration, {\n config: {\n prefix: '',\n },\n });\n\n if (options.build) {\n await prepareIntlayer(configuration);\n }\n\n const { defaultLocale, locales } = configuration.internationalization;\n const mode = options.mode ?? 'complete';\n const baseLocale = options.sourceLocale ?? defaultLocale;\n const outputLocales = (\n options.outputLocales ? ensureArray(options.outputLocales) : locales\n ).filter((locale) => locale !== baseLocale);\n\n const hasAIAccess = await checkAIAccess(configuration, options.aiOptions);\n\n if (!hasAIAccess) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, configuration);\n\n const targetUnmergedDictionaries =\n await getTargetUnmergedDictionaries(options);\n\n const affectedDictionaryKeys = new Set<string>();\n targetUnmergedDictionaries.forEach((dict) => {\n affectedDictionaryKeys.add(dict.key);\n });\n\n appLogger([\n 'Affected dictionary keys for processing:',\n Array.from(affectedDictionaryKeys)\n .map((key) => colorizeKey(key))\n .join(', '),\n ]);\n\n const maxKeyLength = Math.max(\n ...targetUnmergedDictionaries.map((dict) => dict.key.length)\n );\n const maxLocaleLength = Math.max(\n ...locales.map((locale) => formatLocale(locale).length)\n );\n\n for (const targetUnmergedDictionary of targetUnmergedDictionaries) {\n const dictionaryPreset = colon(\n [\n colorize(' - [', ANSIColors.GREY_DARK),\n colorizeKey(targetUnmergedDictionary.key),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: maxKeyLength + 6 }\n );\n\n const dictionaryKey = targetUnmergedDictionary.key;\n const dictionariesRecord = getDictionaries(configuration);\n\n const mainDictionaryToProcess: Dictionary =\n dictionariesRecord[dictionaryKey];\n\n const sourceLocale: Locales =\n (targetUnmergedDictionary.locale as Locales) ?? baseLocale;\n\n if (!mainDictionaryToProcess) {\n appLogger(\n `${dictionaryPreset} Dictionary not found in dictionariesRecord. Skipping.`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n if (!targetUnmergedDictionary.filePath) {\n appLogger(`${dictionaryPreset} Dictionary has no file path. Skipping.`, {\n level: 'warn',\n });\n continue;\n }\n\n const relativePath = relative(\n configuration.content.baseDir,\n targetUnmergedDictionary.filePath\n );\n\n appLogger(\n `${dictionaryPreset} Processing content declaration: ${colorizePath(relativePath)}`,\n {\n level: 'info',\n }\n );\n\n const sourceLocaleContent = getFilterTranslationsOnlyContent(\n mainDictionaryToProcess as unknown as ContentNode,\n sourceLocale,\n { dictionaryKey, keyPath: [] }\n );\n\n if (Object.keys(sourceLocaleContent).length === 0) {\n appLogger(\n `${dictionaryPreset} No content found for dictionary in source locale ${formatLocale(sourceLocale)}. Skipping translation for this dictionary.`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n const result: Dictionary[] = [];\n\n // Determine output locales\n let outputLocalesList: Locales[] = outputLocales;\n\n // If mode is review, translate all locales\n // If mode is complete, translate only the locales that are not the source locale\n if (mode === 'complete') {\n const missingLocales = getMissingLocalesContent(\n mainDictionaryToProcess as unknown as ContentNode,\n outputLocales,\n {\n dictionaryKey: mainDictionaryToProcess.key,\n keyPath: [],\n plugins: [],\n }\n );\n\n outputLocalesList = missingLocales;\n }\n\n if (outputLocalesList.length === 0) {\n appLogger(\n `${dictionaryPreset} No locales to fill - Skipping dictionary`,\n\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n const translationResults = await parallelize(\n outputLocalesList,\n async (targetLocale) => {\n const localePreset = colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n formatLocale(targetLocale),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: maxLocaleLength }\n );\n\n appLogger(\n `${dictionaryPreset}${localePreset} Preparing translation for dictionary from ${formatLocale(sourceLocale)} to ${formatLocale(targetLocale)}`,\n {\n level: 'info',\n }\n );\n\n const presetOutputContent = getLocalisedContent(\n mainDictionaryToProcess as unknown as ContentNode,\n targetLocale,\n { dictionaryKey, keyPath: [] }\n );\n\n try {\n const translationResult = await intlayerAPI.ai.translateJSON({\n entryFileContent: sourceLocaleContent.content, // Should be JSON, ensure getLocalisedContent provides this.\n presetOutputContent: presetOutputContent.content, // Should be JSON\n dictionaryDescription: mainDictionaryToProcess.description ?? '',\n entryLocale: sourceLocale,\n outputLocale: targetLocale,\n mode,\n aiOptions: options.aiOptions,\n });\n\n if (!translationResult.data?.fileContent) {\n appLogger(`${dictionaryPreset}${localePreset} No content result`, {\n level: 'error',\n });\n return null;\n }\n\n const processedPerLocaleDictionary = processPerLocaleDictionary({\n ...mainDictionaryToProcess,\n content: translationResult.data?.fileContent,\n locale: targetLocale,\n });\n\n return processedPerLocaleDictionary;\n } catch (error) {\n appLogger(\n `${dictionaryPreset}${localePreset} ${colorize('Error filling', ANSIColors.RED)}: ` +\n error,\n {\n level: 'error',\n }\n );\n return null;\n }\n },\n options.nbConcurrentTranslations ?? NB_CONCURRENT_TRANSLATIONS\n );\n\n // Filter out null results and add to result array\n translationResults.forEach((translationResult) => {\n if (translationResult) {\n result.push(translationResult);\n }\n });\n\n const dictionaryToMerge =\n mode === 'review'\n ? [...result, mainDictionaryToProcess] // Mode review: generated content will override the base one\n : [mainDictionaryToProcess, ...result]; // Mode complete: base content will override the generated one\n\n const mergedResults = mergeDictionaries(dictionaryToMerge);\n\n let formattedDict = targetUnmergedDictionary;\n\n if (formattedDict.locale) {\n const presetOutputContent = getLocalisedContent(\n mainDictionaryToProcess as unknown as ContentNode,\n formattedDict.locale,\n { dictionaryKey, keyPath: [] }\n );\n\n formattedDict = {\n ...formattedDict,\n content: presetOutputContent.content,\n };\n }\n\n const reducedResult = reduceDictionaryContent(mergedResults, formattedDict);\n\n if (formattedDict.autoFill || configuration.content.autoFill) {\n await autoFill(\n mergedResults,\n targetUnmergedDictionary,\n formattedDict.autoFill ?? configuration.content.autoFill,\n outputLocalesList,\n [sourceLocale],\n configuration\n );\n } else {\n await writeContentDeclaration(\n { ...formattedDict, content: reducedResult.content },\n configuration,\n formattedDict.filePath\n );\n\n if (formattedDict.filePath) {\n appLogger(\n `${dictionaryPreset} Content declaration written to ${formatPath(formattedDict.filePath)}`,\n {\n level: 'info',\n }\n );\n }\n }\n }\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/fill/index.ts"],"sourcesContent":["import { AIOptions, getIntlayerAPIProxy } from '@intlayer/api'; // Importing only getAiAPI for now\nimport {\n formatLocale,\n formatPath,\n ListGitFilesOptions,\n mergeDictionaries,\n parallelize,\n prepareIntlayer,\n processPerLocaleDictionary,\n reduceDictionaryContent,\n writeContentDeclaration,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colon,\n colorize,\n colorizeKey,\n colorizePath,\n getAppLogger,\n getConfiguration,\n Locales,\n} from '@intlayer/config';\nimport {\n type ContentNode,\n type Dictionary,\n getFilterTranslationsOnlyContent,\n getLocalisedContent,\n getMissingLocalesContent,\n} from '@intlayer/core';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport { relative } from 'path';\nimport {\n ensureArray,\n GetTargetDictionaryOptions,\n getTargetUnmergedDictionaries,\n} from '../getTargetDictionary';\nimport { checkAIAccess } from '../utils/checkAccess';\nimport { autoFill } from './autoFill';\n\nconst NB_CONCURRENT_TRANSLATIONS = 8;\n\n// Arguments for the fill function\nexport type FillOptions = {\n sourceLocale?: Locales;\n outputLocales?: Locales | Locales[];\n mode?: 'complete' | 'review';\n gitOptions?: ListGitFilesOptions;\n aiOptions?: AIOptions; // Added aiOptions to be passed to translateJSON\n verbose?: boolean;\n nbConcurrentTranslations?: number;\n build?: boolean;\n} & GetTargetDictionaryOptions;\n\n/**\n * Fill translations based on the provided options.\n */\nexport const fill = async (options: FillOptions): Promise<void> => {\n const configuration = getConfiguration(options.configOptions);\n const appLogger = getAppLogger(configuration, {\n config: {\n prefix: '',\n },\n });\n\n if (options.build) {\n await prepareIntlayer(configuration);\n }\n\n const { defaultLocale, locales } = configuration.internationalization;\n const mode = options.mode ?? 'complete';\n const baseLocale = options.sourceLocale ?? defaultLocale;\n const outputLocales = (\n options.outputLocales ? ensureArray(options.outputLocales) : locales\n ).filter((locale) => locale !== baseLocale);\n\n const hasAIAccess = await checkAIAccess(configuration, options.aiOptions);\n\n if (!hasAIAccess) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, configuration);\n\n const targetUnmergedDictionaries =\n await getTargetUnmergedDictionaries(options);\n\n const affectedDictionaryKeys = new Set<string>();\n targetUnmergedDictionaries.forEach((dict) => {\n affectedDictionaryKeys.add(dict.key);\n });\n\n appLogger([\n 'Affected dictionary keys for processing:',\n Array.from(affectedDictionaryKeys)\n .map((key) => colorizeKey(key))\n .join(', '),\n ]);\n\n const maxKeyLength = Math.max(\n ...targetUnmergedDictionaries.map((dict) => dict.key.length)\n );\n const maxLocaleLength = Math.max(\n ...locales.map((locale) => formatLocale(locale).length)\n );\n const dictionariesRecord = getDictionaries(configuration);\n\n type TranslationTask = {\n dictionaryKey: string;\n sourceLocale: Locales;\n targetLocale: Locales;\n dictionaryPreset: string;\n localePreset: string;\n };\n\n const translationTasks: TranslationTask[] = [];\n\n for (const targetUnmergedDictionary of targetUnmergedDictionaries) {\n const dictionaryPreset = colon(\n [\n colorize(' - [', ANSIColors.GREY_DARK),\n colorizeKey(targetUnmergedDictionary.key),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: maxKeyLength + 6 }\n );\n\n const dictionaryKey = targetUnmergedDictionary.key;\n const mainDictionaryToProcess: Dictionary =\n dictionariesRecord[dictionaryKey];\n\n const sourceLocale: Locales =\n (targetUnmergedDictionary.locale as Locales) ?? baseLocale;\n\n if (!mainDictionaryToProcess) {\n appLogger(\n `${dictionaryPreset} Dictionary not found in dictionariesRecord. Skipping.`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n if (!targetUnmergedDictionary.filePath) {\n appLogger(`${dictionaryPreset} Dictionary has no file path. Skipping.`, {\n level: 'warn',\n });\n continue;\n }\n\n const relativePath = relative(\n configuration.content.baseDir,\n targetUnmergedDictionary.filePath\n );\n\n appLogger(\n `${dictionaryPreset} Processing content declaration: ${colorizePath(relativePath)}`,\n {\n level: 'info',\n }\n );\n\n const sourceLocaleContent = getFilterTranslationsOnlyContent(\n mainDictionaryToProcess as unknown as ContentNode,\n sourceLocale,\n { dictionaryKey, keyPath: [] }\n );\n\n if (Object.keys(sourceLocaleContent).length === 0) {\n appLogger(\n `${dictionaryPreset} No content found for dictionary in source locale ${formatLocale(sourceLocale)}. Skipping translation for this dictionary.`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n let outputLocalesList: Locales[] = outputLocales;\n\n if (mode === 'complete') {\n const missingLocales = getMissingLocalesContent(\n mainDictionaryToProcess as unknown as ContentNode,\n outputLocales,\n {\n dictionaryKey: mainDictionaryToProcess.key,\n keyPath: [],\n plugins: [],\n }\n );\n\n outputLocalesList = missingLocales;\n }\n\n if (outputLocalesList.length === 0) {\n appLogger(\n `${dictionaryPreset} No locales to fill - Skipping dictionary`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n for (const targetLocale of outputLocalesList) {\n const localePreset = colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n formatLocale(targetLocale),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: maxLocaleLength }\n );\n\n translationTasks.push({\n dictionaryKey,\n sourceLocale,\n targetLocale,\n dictionaryPreset,\n localePreset,\n });\n }\n }\n\n const translationResults = await parallelize(\n translationTasks,\n async (task) => {\n const mainDictionaryToProcess: Dictionary =\n dictionariesRecord[task.dictionaryKey];\n\n appLogger(\n `${task.dictionaryPreset}${task.localePreset} Preparing translation for dictionary from ${formatLocale(task.sourceLocale)} to ${formatLocale(task.targetLocale)}`,\n {\n level: 'info',\n }\n );\n\n const sourceLocaleContent = getFilterTranslationsOnlyContent(\n mainDictionaryToProcess as unknown as ContentNode,\n task.sourceLocale,\n { dictionaryKey: task.dictionaryKey, keyPath: [] }\n );\n\n const presetOutputContent = getLocalisedContent(\n mainDictionaryToProcess as unknown as ContentNode,\n task.targetLocale,\n { dictionaryKey: task.dictionaryKey, keyPath: [] }\n );\n\n try {\n const translationResult = await intlayerAPI.ai.translateJSON({\n entryFileContent: sourceLocaleContent.content,\n presetOutputContent: presetOutputContent.content,\n dictionaryDescription: mainDictionaryToProcess.description ?? '',\n entryLocale: task.sourceLocale,\n outputLocale: task.targetLocale,\n mode,\n aiOptions: options.aiOptions,\n });\n\n if (!translationResult.data?.fileContent) {\n appLogger(\n `${task.dictionaryPreset}${task.localePreset} No content result`,\n {\n level: 'error',\n }\n );\n return { key: task.dictionaryKey, result: null } as const;\n }\n\n const processedPerLocaleDictionary = processPerLocaleDictionary({\n ...mainDictionaryToProcess,\n content: translationResult.data?.fileContent,\n locale: task.targetLocale,\n });\n\n return {\n key: task.dictionaryKey,\n result: processedPerLocaleDictionary,\n } as const;\n } catch (error) {\n appLogger(\n `${task.dictionaryPreset}${task.localePreset} ${colorize('Error filling', ANSIColors.RED)}: ` +\n error,\n {\n level: 'error',\n }\n );\n return { key: task.dictionaryKey, result: null } as const;\n }\n },\n options.nbConcurrentTranslations ?? NB_CONCURRENT_TRANSLATIONS\n );\n\n const resultsByDictionary = new Map<string, Dictionary[]>();\n for (const item of translationResults) {\n if (item?.result) {\n const list = resultsByDictionary.get(item.key) ?? [];\n list.push(item.result);\n resultsByDictionary.set(item.key, list);\n }\n }\n\n for (const targetUnmergedDictionary of targetUnmergedDictionaries) {\n const dictionaryKey = targetUnmergedDictionary.key;\n const mainDictionaryToProcess: Dictionary =\n dictionariesRecord[dictionaryKey];\n\n const sourceLocale: Locales =\n (targetUnmergedDictionary.locale as Locales) ?? baseLocale;\n\n if (!mainDictionaryToProcess || !targetUnmergedDictionary.filePath) {\n continue;\n }\n\n let outputLocalesList: Locales[] = outputLocales;\n\n if (mode === 'complete') {\n const missingLocales = getMissingLocalesContent(\n mainDictionaryToProcess as unknown as ContentNode,\n outputLocales,\n {\n dictionaryKey: mainDictionaryToProcess.key,\n keyPath: [],\n plugins: [],\n }\n );\n\n outputLocalesList = missingLocales;\n }\n\n if (outputLocalesList.length === 0) {\n continue;\n }\n\n const perLocaleResults = resultsByDictionary.get(dictionaryKey) ?? [];\n\n const dictionaryToMerge =\n mode === 'review'\n ? [...perLocaleResults, mainDictionaryToProcess]\n : [mainDictionaryToProcess, ...perLocaleResults];\n\n const mergedResults = mergeDictionaries(dictionaryToMerge);\n\n let formattedDict = targetUnmergedDictionary;\n\n if (formattedDict.locale) {\n const presetOutputContent = getLocalisedContent(\n mainDictionaryToProcess as unknown as ContentNode,\n formattedDict.locale,\n { dictionaryKey, keyPath: [] }\n );\n\n formattedDict = {\n ...formattedDict,\n content: presetOutputContent.content,\n };\n }\n\n const reducedResult = reduceDictionaryContent(mergedResults, formattedDict);\n\n if (formattedDict.autoFill || configuration.content.autoFill) {\n await autoFill(\n mergedResults,\n targetUnmergedDictionary,\n formattedDict.autoFill ?? configuration.content.autoFill,\n outputLocalesList,\n [sourceLocale],\n configuration\n );\n } else {\n await writeContentDeclaration(\n { ...formattedDict, content: reducedResult.content },\n configuration,\n formattedDict.filePath\n );\n\n if (formattedDict.filePath) {\n const dictionaryPreset = colon(\n [\n colorize(' - [', ANSIColors.GREY_DARK),\n colorizeKey(targetUnmergedDictionary.key),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: maxKeyLength + 6 }\n );\n appLogger(\n `${dictionaryPreset} Content declaration written to ${formatPath(formattedDict.filePath)}`,\n {\n level: 'info',\n }\n );\n }\n }\n }\n};\n"],"mappings":"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","names":[]}
@@ -20,8 +20,9 @@ __reExport(index_exports, require('./cli.cjs'), module.exports);
20
20
  __reExport(index_exports, require('./editor.cjs'), module.exports);
21
21
  __reExport(index_exports, require('./fill/index.cjs'), module.exports);
22
22
  __reExport(index_exports, require('./listContentDeclaration.cjs'), module.exports);
23
+ __reExport(index_exports, require('./liveSync.cjs'), module.exports);
23
24
  __reExport(index_exports, require('./pull.cjs'), module.exports);
24
- __reExport(index_exports, require('./push.cjs'), module.exports);
25
+ __reExport(index_exports, require('./push/push.cjs'), module.exports);
25
26
  __reExport(index_exports, require('./pushConfig.cjs'), module.exports);
26
27
  __reExport(index_exports, require('./reviewDoc.cjs'), module.exports);
27
28
  __reExport(index_exports, require('./test/index.cjs'), module.exports);
@@ -33,8 +34,9 @@ __reExport(index_exports, require('./translateDoc.cjs'), module.exports);
33
34
  ...require('./editor.cjs'),
34
35
  ...require('./fill/index.cjs'),
35
36
  ...require('./listContentDeclaration.cjs'),
37
+ ...require('./liveSync.cjs'),
36
38
  ...require('./pull.cjs'),
37
- ...require('./push.cjs'),
39
+ ...require('./push/push.cjs'),
38
40
  ...require('./pushConfig.cjs'),
39
41
  ...require('./reviewDoc.cjs'),
40
42
  ...require('./test/index.cjs'),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["export type * from '@intlayer/chokidar';\nexport * from './build';\nexport * from './cli';\nexport * from './editor';\nexport * from './fill';\nexport * from './listContentDeclaration';\nexport * from './pull';\nexport * from './push';\nexport * from './pushConfig';\nexport * from './reviewDoc';\nexport * from './test';\nexport * from './translateDoc';\n"],"mappings":";;;;;;;;;;;;;;;AAAA;AAAA;AACA,0BAAc,oBADd;AAEA,0BAAc,kBAFd;AAGA,0BAAc,qBAHd;AAIA,0BAAc,mBAJd;AAKA,0BAAc,qCALd;AAMA,0BAAc,mBANd;AAOA,0BAAc,mBAPd;AAQA,0BAAc,yBARd;AASA,0BAAc,wBATd;AAUA,0BAAc,mBAVd;AAWA,0BAAc,2BAXd;","names":[]}
1
+ {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["export type * from '@intlayer/chokidar';\nexport * from './build';\nexport * from './cli';\nexport * from './editor';\nexport * from './fill';\nexport * from './listContentDeclaration';\nexport * from './liveSync';\nexport * from './pull';\nexport * from './push/push';\nexport * from './pushConfig';\nexport * from './reviewDoc';\nexport * from './test';\nexport * from './translateDoc';\n"],"mappings":";;;;;;;;;;;;;;;AAAA;AAAA;AACA,0BAAc,oBADd;AAEA,0BAAc,kBAFd;AAGA,0BAAc,qBAHd;AAIA,0BAAc,mBAJd;AAKA,0BAAc,qCALd;AAMA,0BAAc,uBANd;AAOA,0BAAc,mBAPd;AAQA,0BAAc,wBARd;AASA,0BAAc,yBATd;AAUA,0BAAc,wBAVd;AAWA,0BAAc,mBAXd;AAYA,0BAAc,2BAZd;","names":[]}
@@ -38,7 +38,6 @@ var import_package = __toESM(require("@intlayer/config/package.json"));
38
38
  var import_core = require("@intlayer/core");
39
39
  var import_dictionaries_entry = require("@intlayer/dictionaries-entry");
40
40
  var import_unmerged_dictionaries_entry = require("@intlayer/unmerged-dictionaries-entry");
41
- var import_child_process = require("child_process");
42
41
  var import_http = require("http");
43
42
  const writeDictionary = async (dictionary, configuration) => {
44
43
  const appLogger = (0, import_config.getAppLogger)(configuration);
@@ -53,28 +52,12 @@ const liveSync = async (options) => {
53
52
  }
54
53
  });
55
54
  const { liveSyncPort, liveSyncURL } = configuration.editor;
56
- let childProcess = null;
55
+ let parallelProcess = null;
57
56
  let eventListener = null;
58
57
  let isHotReloadConnected = false;
59
58
  let connectionStatus = "disconnected";
60
- if (options?.process) {
61
- const [command, ...args] = options.process.split(" ");
62
- childProcess = (0, import_child_process.spawn)(command, args, {
63
- stdio: "inherit",
64
- shell: true
65
- });
66
- childProcess.on("error", (error) => {
67
- appLogger(`Failed to start process '${options.process}':`, {
68
- level: "error"
69
- });
70
- });
71
- childProcess.on("exit", (code) => {
72
- if (code !== 0) {
73
- appLogger(`Process "${options.process}" exited with code ${code}`);
74
- } else {
75
- appLogger(`Process "${options.process}" exited successfully`);
76
- }
77
- });
59
+ if (options?.with) {
60
+ parallelProcess = (0, import_chokidar.runParallel)(options.with);
78
61
  }
79
62
  if (configuration.editor.liveSync && configuration.editor.backendURL && configuration.editor.clientId && configuration.editor.clientSecret) {
80
63
  eventListener = new import_IntlayerEventListener.IntlayerEventListener(configuration);
@@ -219,7 +202,7 @@ const liveSync = async (options) => {
219
202
  Live server running at: \x1B[90m${liveSyncURL}\x1B[0m
220
203
  - Backend URL: \x1B[90m${configuration.editor.backendURL ?? "-"}\x1B[0m
221
204
  - Live sync: ${getLiveSyncParam()}
222
- - Parallel process: ${options?.process === "" ? "-" : `\x1B[90m${options?.process}\x1B[0m`}
205
+ - Parallel process: ${options?.with ? `\x1B[90m${Array.isArray(options.with) ? options.with.join(" ") : options.with}\x1B[0m` : "-"}
223
206
  - Access key: ${configuration.editor.clientId ?? "-"}
224
207
  `);
225
208
  });
@@ -228,15 +211,8 @@ const liveSync = async (options) => {
228
211
  appLogger("Closing SSE connection...");
229
212
  eventListener.cleanup();
230
213
  }
231
- if (childProcess && !childProcess.killed) {
232
- appLogger("Terminating parallel process...");
233
- childProcess.kill("SIGTERM");
234
- setTimeout(() => {
235
- if (childProcess && !childProcess.killed) {
236
- appLogger("Force killing parallel process...");
237
- childProcess.kill("SIGKILL");
238
- }
239
- }, 5e3);
214
+ if (parallelProcess) {
215
+ parallelProcess.kill();
240
216
  }
241
217
  server.close(() => {
242
218
  appLogger("Live sync server stopped");
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/liveSync.ts"],"sourcesContent":["import { IntlayerEventListener } from './IntlayerEventListener';\n// @ts-ignore: @intlayer/backend is not built yet\nimport type { DictionaryAPI } from '@intlayer/backend';\nimport { buildDictionary } from '@intlayer/chokidar';\nimport type { IntlayerConfig } from '@intlayer/config';\nimport { getAppLogger, getConfiguration } from '@intlayer/config';\nimport packageJson from '@intlayer/config/package.json';\nimport { getLocalisedContent } from '@intlayer/core';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { ChildProcess, spawn } from 'child_process';\nimport { createServer } from 'http';\n\ntype LiveSyncOptions = {\n process?: string;\n};\n\nconst writeDictionary = async (\n dictionary: DictionaryAPI,\n configuration: IntlayerConfig\n) => {\n const appLogger = getAppLogger(configuration);\n appLogger(`Writing dictionary ${dictionary.key}`);\n await buildDictionary([dictionary], configuration);\n};\n\nexport const liveSync = async (options?: LiveSyncOptions) => {\n const configuration = getConfiguration();\n const appLogger = getAppLogger(configuration, {\n config: {\n prefix: '',\n },\n });\n\n const { liveSyncPort, liveSyncURL } = configuration.editor;\n\n let childProcess: ChildProcess | null = null;\n let eventListener: IntlayerEventListener | null = null;\n let isHotReloadConnected = false;\n let connectionStatus = 'disconnected'; // 'connected', 'connecting', 'reconnecting', 'disconnected', 'error'\n\n // Start the parallel process if provided\n if (options?.process) {\n const [command, ...args] = options.process.split(' ');\n\n childProcess = spawn(command, args, {\n stdio: 'inherit',\n shell: true,\n });\n\n childProcess.on('error', (error) => {\n appLogger(`Failed to start process '${options.process}':`, {\n level: 'error',\n });\n });\n\n childProcess.on('exit', (code) => {\n if (code !== 0) {\n appLogger(`Process \"${options.process}\" exited with code ${code}`);\n } else {\n appLogger(`Process \"${options.process}\" exited successfully`);\n }\n });\n }\n\n // Initialize the event listener for hot reload if configured\n if (\n configuration.editor.liveSync &&\n configuration.editor.backendURL &&\n configuration.editor.clientId &&\n configuration.editor.clientSecret\n ) {\n eventListener = new IntlayerEventListener(configuration);\n connectionStatus = 'connecting';\n\n // Set up connection callbacks\n eventListener.onConnectionOpen = () => {\n connectionStatus = 'connected';\n isHotReloadConnected = true;\n appLogger('Live sync connection established');\n };\n\n eventListener.onConnectionError = (error) => {\n connectionStatus = 'error';\n isHotReloadConnected = false;\n const errorEvent = error as any;\n appLogger(\n `Live sync connection error: ${errorEvent.message ?? 'Unknown error'}`,\n {\n level: 'warn',\n }\n );\n\n // If this is a \"terminated: other side closed\" error, it's likely a server restart\n if (\n errorEvent.message?.includes('terminated') ||\n errorEvent.message?.includes('closed')\n ) {\n appLogger(\n 'Server connection was terminated, automatic reconnection will be attempted...',\n {\n level: 'info',\n }\n );\n connectionStatus = 'reconnecting';\n }\n };\n\n // Set up dictionary change callbacks\n eventListener.onDictionaryAdded = (dictionary) =>\n writeDictionary(dictionary, configuration);\n eventListener.onDictionaryChange = (dictionary) =>\n writeDictionary(dictionary, configuration);\n eventListener.onDictionaryDeleted = (dictionary) =>\n writeDictionary(dictionary, configuration);\n\n try {\n await eventListener.initialize();\n } catch (error) {\n connectionStatus = 'error';\n isHotReloadConnected = false;\n appLogger('Failed to initialize IntlayerEventListener:', {\n level: 'error',\n });\n appLogger(\n `Error: ${error instanceof Error ? error.message : String(error)}`,\n {\n level: 'error',\n }\n );\n }\n } else if (!configuration.editor.liveSync) {\n appLogger(\n 'Hot reload is disabled. Please enable it in the configuration (editor.liveSync).'\n );\n } else if (\n !configuration.editor.clientId ||\n !configuration.editor.clientSecret\n ) {\n appLogger(\n 'Missing client credentials for hot reload. Please configure clientId and clientSecret'\n );\n }\n\n const server = createServer(async (req, res) => {\n // Handle CORS preflight requests\n if (req.method === 'OPTIONS') {\n res.writeHead(200, {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n });\n\n res.end();\n return;\n }\n\n if (req.url?.startsWith('/dictionaries')) {\n res.writeHead(200, {\n 'Content-Type': 'application/json; charset=utf-8',\n 'Cache-Control': 'no-store',\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n });\n const dictionaries = getDictionaries(configuration);\n\n const prefix = '/dictionaries/';\n if (req.url.startsWith(prefix)) {\n const [key, locale] = decodeURIComponent(req.url)\n .slice(prefix.length)\n .split('/');\n\n const dictionary = dictionaries[key] ?? null;\n\n if (locale) {\n const sourceLocaleContent = getLocalisedContent(dictionary, locale, {\n dictionaryKey: key,\n keyPath: [],\n });\n\n res.end(JSON.stringify(sourceLocaleContent));\n return;\n }\n\n res.end(JSON.stringify(dictionary));\n return;\n }\n\n res.end(JSON.stringify(dictionaries));\n return;\n }\n\n if (req.url?.startsWith('/unmerged_dictionaries')) {\n res.writeHead(200, {\n 'Content-Type': 'application/json; charset=utf-8',\n 'Cache-Control': 'no-store',\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n });\n const unmergedDictionaries = getUnmergedDictionaries(configuration);\n\n const prefix = '/unmerged_dictionaries/';\n if (req.url.startsWith(prefix)) {\n const key = decodeURIComponent(req.url.slice(prefix.length));\n const one = unmergedDictionaries[key] ?? null;\n\n res.end(JSON.stringify(one));\n return;\n }\n\n res.end(JSON.stringify(unmergedDictionaries));\n return;\n }\n\n if (req.url === '/configuration') {\n res.writeHead(200, {\n 'Content-Type': 'application/json; charset=utf-8',\n 'Cache-Control': 'no-store',\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n });\n res.end(JSON.stringify(configuration));\n return;\n }\n\n if (req.url === '/health') {\n res.writeHead(200, {\n 'Content-Type': 'application/json; charset=utf-8',\n });\n res.end(JSON.stringify({ status: 'ok' }));\n return;\n }\n\n res.end('Not found');\n return;\n });\n\n const getLiveSyncParam = () => {\n if (!configuration.editor.liveSync) return '\\x1b[31m✗ Disabled\\x1b[0m';\n\n return '\\x1b[32m✓ Enabled\\x1b[0m';\n };\n server.listen(liveSyncPort, () => {\n console.log(`\n \\x1b[1;90mINTLAYER v${packageJson.version}\\x1b[0m\n \n Live server running at: \\x1b[90m${liveSyncURL}\\x1b[0m\n - Backend URL: \\x1b[90m${configuration.editor.backendURL ?? '-'}\\x1b[0m\n - Live sync: ${getLiveSyncParam()}\n - Parallel process: ${options?.process === '' ? '-' : `\\x1b[90m${options?.process}\\x1b[0m`}\n - Access key: ${configuration.editor.clientId ?? '-'}\n `);\n });\n\n // Cleanup function to terminate child process and event listener when the main process exits\n const cleanup = () => {\n // Clean up event listener\n if (eventListener) {\n appLogger('Closing SSE connection...');\n eventListener.cleanup();\n }\n\n // Clean up child process\n if (childProcess && !childProcess.killed) {\n appLogger('Terminating parallel process...');\n childProcess.kill('SIGTERM');\n\n // Force kill after 5 seconds if process doesn't terminate gracefully\n setTimeout(() => {\n if (childProcess && !childProcess.killed) {\n appLogger('Force killing parallel process...');\n childProcess.kill('SIGKILL');\n }\n }, 5000);\n }\n\n server.close(() => {\n appLogger('Live sync server stopped');\n process.exit(0);\n });\n };\n\n // Handle process termination signals\n process.on('SIGINT', cleanup);\n process.on('SIGTERM', cleanup);\n process.on('exit', cleanup);\n};\n"],"mappings":"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","names":["packageJson"]}
1
+ {"version":3,"sources":["../../src/liveSync.ts"],"sourcesContent":["import { IntlayerEventListener } from './IntlayerEventListener';\n// @ts-ignore: @intlayer/backend is not built yet\nimport type { DictionaryAPI } from '@intlayer/backend';\nimport {\n buildDictionary,\n runParallel,\n type ParallelHandle,\n} from '@intlayer/chokidar';\nimport type { IntlayerConfig } from '@intlayer/config';\nimport { getAppLogger, getConfiguration } from '@intlayer/config';\nimport packageJson from '@intlayer/config/package.json';\nimport { getLocalisedContent } from '@intlayer/core';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { createServer } from 'http';\n\ntype LiveSyncOptions = {\n with?: string | string[];\n};\n\nconst writeDictionary = async (\n dictionary: DictionaryAPI,\n configuration: IntlayerConfig\n) => {\n const appLogger = getAppLogger(configuration);\n appLogger(`Writing dictionary ${dictionary.key}`);\n await buildDictionary([dictionary], configuration);\n};\n\nexport const liveSync = async (options?: LiveSyncOptions) => {\n const configuration = getConfiguration();\n const appLogger = getAppLogger(configuration, {\n config: {\n prefix: '',\n },\n });\n\n const { liveSyncPort, liveSyncURL } = configuration.editor;\n\n let parallelProcess: ParallelHandle | null = null;\n let eventListener: IntlayerEventListener | null = null;\n let isHotReloadConnected = false;\n let connectionStatus = 'disconnected'; // 'connected', 'connecting', 'reconnecting', 'disconnected', 'error'\n\n // Start the parallel process if provided\n if (options?.with) {\n parallelProcess = runParallel(options.with);\n }\n\n // Initialize the event listener for hot reload if configured\n if (\n configuration.editor.liveSync &&\n configuration.editor.backendURL &&\n configuration.editor.clientId &&\n configuration.editor.clientSecret\n ) {\n eventListener = new IntlayerEventListener(configuration);\n connectionStatus = 'connecting';\n\n // Set up connection callbacks\n eventListener.onConnectionOpen = () => {\n connectionStatus = 'connected';\n isHotReloadConnected = true;\n appLogger('Live sync connection established');\n };\n\n eventListener.onConnectionError = (error) => {\n connectionStatus = 'error';\n isHotReloadConnected = false;\n const errorEvent = error as any;\n appLogger(\n `Live sync connection error: ${errorEvent.message ?? 'Unknown error'}`,\n {\n level: 'warn',\n }\n );\n\n // If this is a \"terminated: other side closed\" error, it's likely a server restart\n if (\n errorEvent.message?.includes('terminated') ||\n errorEvent.message?.includes('closed')\n ) {\n appLogger(\n 'Server connection was terminated, automatic reconnection will be attempted...',\n {\n level: 'info',\n }\n );\n connectionStatus = 'reconnecting';\n }\n };\n\n // Set up dictionary change callbacks\n eventListener.onDictionaryAdded = (dictionary) =>\n writeDictionary(dictionary, configuration);\n eventListener.onDictionaryChange = (dictionary) =>\n writeDictionary(dictionary, configuration);\n eventListener.onDictionaryDeleted = (dictionary) =>\n writeDictionary(dictionary, configuration);\n\n try {\n await eventListener.initialize();\n } catch (error) {\n connectionStatus = 'error';\n isHotReloadConnected = false;\n appLogger('Failed to initialize IntlayerEventListener:', {\n level: 'error',\n });\n appLogger(\n `Error: ${error instanceof Error ? error.message : String(error)}`,\n {\n level: 'error',\n }\n );\n }\n } else if (!configuration.editor.liveSync) {\n appLogger(\n 'Hot reload is disabled. Please enable it in the configuration (editor.liveSync).'\n );\n } else if (\n !configuration.editor.clientId ||\n !configuration.editor.clientSecret\n ) {\n appLogger(\n 'Missing client credentials for hot reload. Please configure clientId and clientSecret'\n );\n }\n\n const server = createServer(async (req, res) => {\n // Handle CORS preflight requests\n if (req.method === 'OPTIONS') {\n res.writeHead(200, {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n });\n\n res.end();\n return;\n }\n\n if (req.url?.startsWith('/dictionaries')) {\n res.writeHead(200, {\n 'Content-Type': 'application/json; charset=utf-8',\n 'Cache-Control': 'no-store',\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n });\n const dictionaries = getDictionaries(configuration);\n\n const prefix = '/dictionaries/';\n if (req.url.startsWith(prefix)) {\n const [key, locale] = decodeURIComponent(req.url)\n .slice(prefix.length)\n .split('/');\n\n const dictionary = dictionaries[key] ?? null;\n\n if (locale) {\n const sourceLocaleContent = getLocalisedContent(dictionary, locale, {\n dictionaryKey: key,\n keyPath: [],\n });\n\n res.end(JSON.stringify(sourceLocaleContent));\n return;\n }\n\n res.end(JSON.stringify(dictionary));\n return;\n }\n\n res.end(JSON.stringify(dictionaries));\n return;\n }\n\n if (req.url?.startsWith('/unmerged_dictionaries')) {\n res.writeHead(200, {\n 'Content-Type': 'application/json; charset=utf-8',\n 'Cache-Control': 'no-store',\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n });\n const unmergedDictionaries = getUnmergedDictionaries(configuration);\n\n const prefix = '/unmerged_dictionaries/';\n if (req.url.startsWith(prefix)) {\n const key = decodeURIComponent(req.url.slice(prefix.length));\n const one = unmergedDictionaries[key] ?? null;\n\n res.end(JSON.stringify(one));\n return;\n }\n\n res.end(JSON.stringify(unmergedDictionaries));\n return;\n }\n\n if (req.url === '/configuration') {\n res.writeHead(200, {\n 'Content-Type': 'application/json; charset=utf-8',\n 'Cache-Control': 'no-store',\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n });\n res.end(JSON.stringify(configuration));\n return;\n }\n\n if (req.url === '/health') {\n res.writeHead(200, {\n 'Content-Type': 'application/json; charset=utf-8',\n });\n res.end(JSON.stringify({ status: 'ok' }));\n return;\n }\n\n res.end('Not found');\n return;\n });\n\n const getLiveSyncParam = () => {\n if (!configuration.editor.liveSync) return '\\x1b[31m✗ Disabled\\x1b[0m';\n\n return '\\x1b[32m✓ Enabled\\x1b[0m';\n };\n server.listen(liveSyncPort, () => {\n console.log(`\n \\x1b[1;90mINTLAYER v${packageJson.version}\\x1b[0m\n \n Live server running at: \\x1b[90m${liveSyncURL}\\x1b[0m\n - Backend URL: \\x1b[90m${configuration.editor.backendURL ?? '-'}\\x1b[0m\n - Live sync: ${getLiveSyncParam()}\n - Parallel process: ${options?.with ? `\\x1b[90m${Array.isArray(options.with) ? options.with.join(' ') : options.with}\\x1b[0m` : '-'}\n - Access key: ${configuration.editor.clientId ?? '-'}\n `);\n });\n\n // Cleanup function to terminate child process and event listener when the main process exits\n const cleanup = () => {\n // Clean up event listener\n if (eventListener) {\n appLogger('Closing SSE connection...');\n eventListener.cleanup();\n }\n\n if (parallelProcess) {\n parallelProcess.kill();\n }\n\n server.close(() => {\n appLogger('Live sync server stopped');\n process.exit(0);\n });\n };\n\n // Handle process termination signals\n process.on('SIGINT', cleanup);\n process.on('SIGTERM', cleanup);\n process.on('exit', cleanup);\n};\n"],"mappings":"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","names":["packageJson"]}
package/dist/cjs/pull.cjs CHANGED
@@ -26,7 +26,7 @@ var import_chokidar = require("@intlayer/chokidar");
26
26
  var import_config = require("@intlayer/config");
27
27
  var import_fs = require("fs");
28
28
  var import_path = require("path");
29
- var import_pullLog = require('./pullLog.cjs');
29
+ var import_pullLog = require('./push/pullLog.cjs');
30
30
  var import_checkAccess = require('./utils/checkAccess.cjs');
31
31
  const pull = async (options) => {
32
32
  const appLogger = (0, import_config.getAppLogger)(options?.configOptions?.override, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pull.ts"],"sourcesContent":["import { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n parallelize,\n writeContentDeclaration,\n type DictionaryStatus,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n ESMxCJSRequire,\n getAppLogger,\n getConfiguration,\n GetConfigurationOptions,\n} from '@intlayer/config';\nimport type { Dictionary } from '@intlayer/core';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { PullLogger, type PullStatus } from './pullLog';\nimport { checkCMSAuth } from './utils/checkAccess';\n\ntype PullOptions = {\n dictionaries?: string[];\n newDictionariesPath?: string;\n configOptions?: GetConfigurationOptions;\n};\n\ntype DictionariesStatus = {\n dictionaryKey: string;\n status: DictionaryStatus | 'pending' | 'fetching' | 'error';\n error?: Error;\n errorMessage?: string;\n};\n\n/**\n * Fetch distant dictionaries and write them locally,\n * with progress indicators and concurrency control.\n */\nexport const pull = async (options?: PullOptions): Promise<void> => {\n const appLogger = getAppLogger(options?.configOptions?.override, {\n config: {\n prefix: '',\n },\n });\n\n try {\n const config = getConfiguration(options?.configOptions);\n\n const hasCMSAuth = await checkCMSAuth(config);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, config);\n\n // Get remote update timestamps map\n const getDictionariesUpdateTimestampResult =\n await intlayerAPI.dictionary.getDictionariesUpdateTimestamp();\n\n if (!getDictionariesUpdateTimestampResult.data) {\n throw new Error('No distant dictionaries found');\n }\n\n let distantDictionariesUpdateTimeStamp: Record<string, number> =\n getDictionariesUpdateTimestampResult.data;\n\n // Optional filtering by requested dictionaries\n if (options?.dictionaries) {\n distantDictionariesUpdateTimeStamp = Object.fromEntries(\n Object.entries(distantDictionariesUpdateTimeStamp).filter(([key]) =>\n options.dictionaries!.includes(key)\n )\n );\n }\n\n // Load local cached remote dictionaries (if any)\n const remoteDictionariesPath = join(\n config.content.mainDir,\n 'remote_dictionaries.cjs'\n );\n const remoteDictionariesRecord: Record<string, any> = existsSync(\n remoteDictionariesPath\n )\n ? (ESMxCJSRequire(remoteDictionariesPath) as any)\n : {};\n\n // Determine which keys need fetching by comparing updatedAt with local cache\n const entries = Object.entries(distantDictionariesUpdateTimeStamp);\n const keysToFetch = entries\n .filter(([key, remoteUpdatedAt]) => {\n if (!remoteUpdatedAt) return true;\n const local = (remoteDictionariesRecord as any)[key];\n if (!local) return true;\n const localUpdatedAtRaw = (local as any)?.updatedAt as\n | number\n | string\n | undefined;\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n if (typeof localUpdatedAt !== 'number') return true;\n return remoteUpdatedAt > localUpdatedAt;\n })\n .map(([key]) => key);\n\n const cachedKeys = entries\n .filter(([key, remoteUpdatedAt]) => {\n const local = (remoteDictionariesRecord as any)[key];\n const localUpdatedAtRaw = (local as any)?.updatedAt as\n | number\n | string\n | undefined;\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n return (\n typeof localUpdatedAt === 'number' &&\n typeof remoteUpdatedAt === 'number' &&\n localUpdatedAt >= remoteUpdatedAt\n );\n })\n .map(([key]) => key);\n\n // Check if dictionaries list is empty\n if (entries.length === 0) {\n appLogger('No dictionaries to fetch', {\n level: 'error',\n });\n return;\n }\n\n appLogger('Fetching dictionaries:');\n\n // Prepare dictionaries statuses\n const dictionariesStatuses: DictionariesStatus[] = [\n ...cachedKeys.map((dictionaryKey) => ({\n dictionaryKey,\n status: 'imported' as DictionaryStatus,\n })),\n ...keysToFetch.map((dictionaryKey) => ({\n dictionaryKey,\n status: 'pending' as const,\n })),\n ];\n\n // Initialize aggregated logger\n const logger = new PullLogger();\n logger.update(\n dictionariesStatuses.map<PullStatus>((s) => ({\n dictionaryKey: s.dictionaryKey,\n status: s.status,\n }))\n );\n\n const successfullyFetchedDictionaries: Dictionary[] = [];\n\n const processDictionary = async (\n statusObj: DictionariesStatus\n ): Promise<void> => {\n const isCached =\n statusObj.status === 'imported' || statusObj.status === 'up-to-date';\n\n if (!isCached) {\n statusObj.status = 'fetching';\n logger.update([\n { dictionaryKey: statusObj.dictionaryKey, status: 'fetching' },\n ]);\n }\n\n try {\n let sourceDictionary: Dictionary | undefined;\n\n if (isCached) {\n sourceDictionary = remoteDictionariesRecord[\n statusObj.dictionaryKey\n ] as Dictionary | undefined;\n }\n\n if (!sourceDictionary) {\n // Fetch the dictionary\n const getDictionaryResult =\n await intlayerAPI.dictionary.getDictionary(statusObj.dictionaryKey);\n\n sourceDictionary = getDictionaryResult.data as Dictionary | undefined;\n }\n\n if (!sourceDictionary) {\n throw new Error(\n `Dictionary ${statusObj.dictionaryKey} not found on remote`\n );\n }\n\n // Now, write the dictionary to local file\n const { status } = await writeContentDeclaration(\n sourceDictionary,\n config,\n options?.newDictionariesPath\n );\n\n statusObj.status = status;\n logger.update([{ dictionaryKey: statusObj.dictionaryKey, status }]);\n\n successfullyFetchedDictionaries.push(sourceDictionary);\n } catch (error) {\n statusObj.status = 'error';\n statusObj.error = error as Error;\n statusObj.errorMessage = `Error fetching dictionary ${statusObj.dictionaryKey}: ${error}`;\n logger.update([\n { dictionaryKey: statusObj.dictionaryKey, status: 'error' },\n ]);\n }\n };\n\n // Process dictionaries in parallel with concurrency limit\n await parallelize(dictionariesStatuses, processDictionary, 5);\n\n // Stop the logger and render final state\n logger.finish();\n\n // Per-dictionary summary\n const iconFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'fetched':\n case 'imported':\n case 'updated':\n case 'up-to-date':\n case 'reimported in JSON':\n case 'new content file':\n return '✔';\n case 'error':\n return '✖';\n default:\n return '⏲';\n }\n };\n\n const colorFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'fetched':\n case 'imported':\n case 'updated':\n case 'up-to-date':\n return ANSIColors.GREEN;\n case 'reimported in JSON':\n case 'new content file':\n return ANSIColors.YELLOW;\n case 'error':\n return ANSIColors.RED;\n default:\n return ANSIColors.BLUE;\n }\n };\n\n for (const s of dictionariesStatuses) {\n const icon = iconFor(s.status);\n const color = colorFor(s.status);\n appLogger(\n ` - ${s.dictionaryKey} ${ANSIColors.GREY}[${color}${icon} ${s.status}${ANSIColors.GREY}]${ANSIColors.RESET}`\n );\n }\n\n // Output any error messages\n for (const statusObj of dictionariesStatuses) {\n if (statusObj.errorMessage) {\n appLogger(statusObj.errorMessage, {\n level: 'error',\n });\n }\n }\n } catch (error) {\n appLogger(error, {\n level: 'error',\n });\n }\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/pull.ts"],"sourcesContent":["import { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n parallelize,\n writeContentDeclaration,\n type DictionaryStatus,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n ESMxCJSRequire,\n getAppLogger,\n getConfiguration,\n GetConfigurationOptions,\n} from '@intlayer/config';\nimport type { Dictionary } from '@intlayer/core';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { PullLogger, type PullStatus } from './push/pullLog';\nimport { checkCMSAuth } from './utils/checkAccess';\n\ntype PullOptions = {\n dictionaries?: string[];\n newDictionariesPath?: string;\n configOptions?: GetConfigurationOptions;\n};\n\ntype DictionariesStatus = {\n dictionaryKey: string;\n status: DictionaryStatus | 'pending' | 'fetching' | 'error';\n error?: Error;\n errorMessage?: string;\n};\n\n/**\n * Fetch distant dictionaries and write them locally,\n * with progress indicators and concurrency control.\n */\nexport const pull = async (options?: PullOptions): Promise<void> => {\n const appLogger = getAppLogger(options?.configOptions?.override, {\n config: {\n prefix: '',\n },\n });\n\n try {\n const config = getConfiguration(options?.configOptions);\n\n const hasCMSAuth = await checkCMSAuth(config);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, config);\n\n // Get remote update timestamps map\n const getDictionariesUpdateTimestampResult =\n await intlayerAPI.dictionary.getDictionariesUpdateTimestamp();\n\n if (!getDictionariesUpdateTimestampResult.data) {\n throw new Error('No distant dictionaries found');\n }\n\n let distantDictionariesUpdateTimeStamp: Record<string, number> =\n getDictionariesUpdateTimestampResult.data;\n\n // Optional filtering by requested dictionaries\n if (options?.dictionaries) {\n distantDictionariesUpdateTimeStamp = Object.fromEntries(\n Object.entries(distantDictionariesUpdateTimeStamp).filter(([key]) =>\n options.dictionaries!.includes(key)\n )\n );\n }\n\n // Load local cached remote dictionaries (if any)\n const remoteDictionariesPath = join(\n config.content.mainDir,\n 'remote_dictionaries.cjs'\n );\n const remoteDictionariesRecord: Record<string, any> = existsSync(\n remoteDictionariesPath\n )\n ? (ESMxCJSRequire(remoteDictionariesPath) as any)\n : {};\n\n // Determine which keys need fetching by comparing updatedAt with local cache\n const entries = Object.entries(distantDictionariesUpdateTimeStamp);\n const keysToFetch = entries\n .filter(([key, remoteUpdatedAt]) => {\n if (!remoteUpdatedAt) return true;\n const local = (remoteDictionariesRecord as any)[key];\n if (!local) return true;\n const localUpdatedAtRaw = (local as any)?.updatedAt as\n | number\n | string\n | undefined;\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n if (typeof localUpdatedAt !== 'number') return true;\n return remoteUpdatedAt > localUpdatedAt;\n })\n .map(([key]) => key);\n\n const cachedKeys = entries\n .filter(([key, remoteUpdatedAt]) => {\n const local = (remoteDictionariesRecord as any)[key];\n const localUpdatedAtRaw = (local as any)?.updatedAt as\n | number\n | string\n | undefined;\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n return (\n typeof localUpdatedAt === 'number' &&\n typeof remoteUpdatedAt === 'number' &&\n localUpdatedAt >= remoteUpdatedAt\n );\n })\n .map(([key]) => key);\n\n // Check if dictionaries list is empty\n if (entries.length === 0) {\n appLogger('No dictionaries to fetch', {\n level: 'error',\n });\n return;\n }\n\n appLogger('Fetching dictionaries:');\n\n // Prepare dictionaries statuses\n const dictionariesStatuses: DictionariesStatus[] = [\n ...cachedKeys.map((dictionaryKey) => ({\n dictionaryKey,\n status: 'imported' as DictionaryStatus,\n })),\n ...keysToFetch.map((dictionaryKey) => ({\n dictionaryKey,\n status: 'pending' as const,\n })),\n ];\n\n // Initialize aggregated logger\n const logger = new PullLogger();\n logger.update(\n dictionariesStatuses.map<PullStatus>((s) => ({\n dictionaryKey: s.dictionaryKey,\n status: s.status,\n }))\n );\n\n const successfullyFetchedDictionaries: Dictionary[] = [];\n\n const processDictionary = async (\n statusObj: DictionariesStatus\n ): Promise<void> => {\n const isCached =\n statusObj.status === 'imported' || statusObj.status === 'up-to-date';\n\n if (!isCached) {\n statusObj.status = 'fetching';\n logger.update([\n { dictionaryKey: statusObj.dictionaryKey, status: 'fetching' },\n ]);\n }\n\n try {\n let sourceDictionary: Dictionary | undefined;\n\n if (isCached) {\n sourceDictionary = remoteDictionariesRecord[\n statusObj.dictionaryKey\n ] as Dictionary | undefined;\n }\n\n if (!sourceDictionary) {\n // Fetch the dictionary\n const getDictionaryResult =\n await intlayerAPI.dictionary.getDictionary(statusObj.dictionaryKey);\n\n sourceDictionary = getDictionaryResult.data as Dictionary | undefined;\n }\n\n if (!sourceDictionary) {\n throw new Error(\n `Dictionary ${statusObj.dictionaryKey} not found on remote`\n );\n }\n\n // Now, write the dictionary to local file\n const { status } = await writeContentDeclaration(\n sourceDictionary,\n config,\n options?.newDictionariesPath\n );\n\n statusObj.status = status;\n logger.update([{ dictionaryKey: statusObj.dictionaryKey, status }]);\n\n successfullyFetchedDictionaries.push(sourceDictionary);\n } catch (error) {\n statusObj.status = 'error';\n statusObj.error = error as Error;\n statusObj.errorMessage = `Error fetching dictionary ${statusObj.dictionaryKey}: ${error}`;\n logger.update([\n { dictionaryKey: statusObj.dictionaryKey, status: 'error' },\n ]);\n }\n };\n\n // Process dictionaries in parallel with concurrency limit\n await parallelize(dictionariesStatuses, processDictionary, 5);\n\n // Stop the logger and render final state\n logger.finish();\n\n // Per-dictionary summary\n const iconFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'fetched':\n case 'imported':\n case 'updated':\n case 'up-to-date':\n case 'reimported in JSON':\n case 'new content file':\n return '✔';\n case 'error':\n return '✖';\n default:\n return '⏲';\n }\n };\n\n const colorFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'fetched':\n case 'imported':\n case 'updated':\n case 'up-to-date':\n return ANSIColors.GREEN;\n case 'reimported in JSON':\n case 'new content file':\n return ANSIColors.YELLOW;\n case 'error':\n return ANSIColors.RED;\n default:\n return ANSIColors.BLUE;\n }\n };\n\n for (const s of dictionariesStatuses) {\n const icon = iconFor(s.status);\n const color = colorFor(s.status);\n appLogger(\n ` - ${s.dictionaryKey} ${ANSIColors.GREY}[${color}${icon} ${s.status}${ANSIColors.GREY}]${ANSIColors.RESET}`\n );\n }\n\n // Output any error messages\n for (const statusObj of dictionariesStatuses) {\n if (statusObj.errorMessage) {\n appLogger(statusObj.errorMessage, {\n level: 'error',\n });\n }\n }\n } catch (error) {\n appLogger(error, {\n level: 'error',\n });\n }\n};\n"],"mappings":"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","names":[]}
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/push/pullLog.ts"],"sourcesContent":["import { type DictionaryStatus } from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colorize,\n getConfiguration,\n spinnerFrames,\n} from '@intlayer/config';\n\nexport type PullStatus = {\n dictionaryKey: string;\n status: DictionaryStatus | 'pending' | 'fetching';\n errorMessage?: string;\n};\n\nexport class PullLogger {\n private statuses: PullStatus[] = [];\n private spinnerTimer: NodeJS.Timeout | null = null;\n private spinnerIndex = 0;\n private renderedLines = 0;\n private readonly spinnerFrames = spinnerFrames;\n private isFinished = false;\n private readonly prefix: string;\n private lastRenderedState: string = '';\n\n constructor() {\n const configuration = getConfiguration();\n this.prefix = configuration.log.prefix;\n }\n\n update(newStatuses: PullStatus[]) {\n if (this.isFinished) return;\n for (const status of newStatuses) {\n const index = this.statuses.findIndex(\n (s) => s.dictionaryKey === status.dictionaryKey\n );\n if (index >= 0) {\n this.statuses[index] = status;\n } else {\n this.statuses.push(status);\n }\n }\n\n this.startSpinner();\n this.render();\n }\n\n finish() {\n this.isFinished = true;\n this.stopSpinner();\n this.render();\n }\n\n private startSpinner() {\n if (this.spinnerTimer || this.isFinished) return;\n this.spinnerTimer = setInterval(() => {\n this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length;\n this.render();\n }, 100);\n }\n\n private stopSpinner() {\n if (!this.spinnerTimer) return;\n clearInterval(this.spinnerTimer);\n this.spinnerTimer = null;\n }\n\n private render() {\n const { total, done, success, errors } = this.computeProgress();\n\n const frame = this.spinnerFrames[this.spinnerIndex];\n const lines: string[] = [];\n\n const isDone = done === total;\n const progressLabel = `dictionaries: ${done}/${total}`;\n const details: string[] = [];\n if (success > 0) details.push(`ok: ${success}`);\n if (errors > 0) details.push(colorize(`errors: ${errors}`, ANSIColors.RED));\n\n const suffix = details.length > 0 ? ` (${details.join(', ')})` : '';\n\n if (isDone) {\n lines.push(\n `${this.prefix} ${colorize('✔', ANSIColors.GREEN)} fetched ${progressLabel}${suffix}`\n );\n } else {\n lines.push(\n `${this.prefix} ${colorize(frame, ANSIColors.BLUE)} fetching ${progressLabel}${suffix}`\n );\n }\n\n const currentState = lines.join('\\n');\n if (currentState === this.lastRenderedState) {\n return;\n }\n this.lastRenderedState = currentState;\n\n if (this.renderedLines > 0) {\n process.stdout.write(`\\x1b[${this.renderedLines}F`);\n }\n\n const totalLinesToClear = Math.max(this.renderedLines, lines.length);\n for (let i = 0; i < totalLinesToClear; i++) {\n process.stdout.write('\\x1b[2K');\n const line = lines[i];\n if (line !== undefined) {\n process.stdout.write(line);\n }\n process.stdout.write('\\n');\n }\n\n this.renderedLines = lines.length;\n }\n\n private computeProgress() {\n const keys = new Set(this.statuses.map((s) => s.dictionaryKey));\n\n const doneSet = new Set<DictionaryStatus | 'error'>([\n 'fetched',\n 'imported',\n 'updated',\n 'up-to-date',\n 'reimported in JSON',\n 'new content file',\n 'error',\n ] as const);\n\n const successesSet = new Set<DictionaryStatus>([\n 'fetched',\n 'imported',\n 'updated',\n 'up-to-date',\n 'reimported in JSON',\n 'new content file',\n ] as const);\n\n const done = this.statuses.filter((s) =>\n doneSet.has(s.status as any)\n ).length;\n const success = this.statuses.filter((s) =>\n successesSet.has(s.status as any)\n ).length;\n const errors = this.statuses.filter((s) => s.status === 'error').length;\n\n return {\n total: keys.size,\n done,\n success,\n errors,\n } as const;\n }\n}\n"],"mappings":"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","names":[]}
@@ -38,8 +38,8 @@ var import_dictionaries_entry = require("@intlayer/dictionaries-entry");
38
38
  var fsPromises = __toESM(require("fs/promises"));
39
39
  var import_path = require("path");
40
40
  var readline = __toESM(require("readline"));
41
- var import_pushLog = require('./pushLog.cjs');
42
- var import_checkAccess = require('./utils/checkAccess.cjs');
41
+ var import_pushLog = require('../pushLog.cjs');
42
+ var import_checkAccess = require('../utils/checkAccess.cjs');
43
43
  const push = async (options) => {
44
44
  const config = (0, import_config.getConfiguration)(options?.configOptions);
45
45
  const appLogger = (0, import_config.getAppLogger)(config, {
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/push/push.ts"],"sourcesContent":["import { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n formatPath,\n listGitFiles,\n ListGitFilesOptions,\n parallelize,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n getAppLogger,\n getConfiguration,\n GetConfigurationOptions,\n} from '@intlayer/config';\nimport type { Dictionary } from '@intlayer/core';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport * as fsPromises from 'fs/promises';\nimport { join } from 'path';\nimport * as readline from 'readline';\nimport { PushLogger, type PushStatus } from '../pushLog';\nimport { checkCMSAuth } from '../utils/checkAccess';\n\ntype PushOptions = {\n deleteLocaleDictionary?: boolean;\n keepLocaleDictionary?: boolean;\n dictionaries?: string[];\n gitOptions?: ListGitFilesOptions;\n configOptions?: GetConfigurationOptions;\n};\n\ntype DictionariesStatus = {\n dictionary: Dictionary;\n status: 'pending' | 'pushing' | 'modified' | 'pushed' | 'unknown' | 'error';\n error?: Error;\n errorMessage?: string;\n};\n\n/**\n * Get all local dictionaries and push them simultaneously.\n */\nexport const push = async (options?: PushOptions): Promise<void> => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config, {\n config: {\n prefix: '',\n },\n });\n\n try {\n const hasCMSAuth = await checkCMSAuth(config);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, config);\n\n const dictionariesRecord = getDictionaries(config);\n let dictionaries: Dictionary[] = Object.values(dictionariesRecord);\n const existingDictionariesKeys: string[] = Object.keys(dictionariesRecord);\n\n if (options?.dictionaries) {\n // Check if the provided dictionaries exist\n const noneExistingDictionariesOption = options.dictionaries.filter(\n (dictionaryId) => !existingDictionariesKeys.includes(dictionaryId)\n );\n\n if (noneExistingDictionariesOption.length > 0) {\n appLogger(\n `The following dictionaries do not exist: ${noneExistingDictionariesOption.join(\n ', '\n )} and have been ignored.`,\n {\n level: 'error',\n }\n );\n }\n\n // Filter the dictionaries from the provided list of IDs\n dictionaries = dictionaries.filter((dictionary) =>\n options.dictionaries!.includes(dictionary.key)\n );\n }\n\n if (options?.gitOptions) {\n const gitFiles = await listGitFiles(options.gitOptions);\n\n dictionaries = dictionaries.filter((dictionary) =>\n gitFiles.includes(\n join(config.content.baseDir, dictionary.filePath ?? '')\n )\n );\n }\n\n // Check if the dictionaries list is empty\n if (dictionaries.length === 0) {\n appLogger('No local dictionaries found', {\n level: 'error',\n });\n return;\n }\n\n appLogger('Pushing dictionaries:', {});\n\n // Prepare dictionaries statuses\n const dictionariesStatuses: DictionariesStatus[] = dictionaries.map(\n (dictionary) => ({\n dictionary,\n status: 'pending',\n })\n );\n\n // Initialize aggregated logger similar to loadDictionaries\n const logger = new PushLogger();\n logger.update(\n dictionariesStatuses.map<PushStatus>((s) => ({\n dictionaryKey: s.dictionary.key,\n status: 'pending',\n }))\n );\n\n const successfullyPushedDictionaries: Dictionary[] = [];\n\n const processDictionary = async (\n statusObj: DictionariesStatus\n ): Promise<void> => {\n statusObj.status = 'pushing';\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'pushing' },\n ]);\n\n try {\n const pushResult = await intlayerAPI.dictionary.pushDictionaries([\n statusObj.dictionary,\n ]);\n\n const updatedDictionaries = pushResult.data?.updatedDictionaries || [];\n const newDictionaries = pushResult.data?.newDictionaries || [];\n\n if (updatedDictionaries.includes(statusObj.dictionary.key)) {\n statusObj.status = 'modified';\n successfullyPushedDictionaries.push(statusObj.dictionary);\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'modified' },\n ]);\n } else if (newDictionaries.includes(statusObj.dictionary.key)) {\n statusObj.status = 'pushed';\n successfullyPushedDictionaries.push(statusObj.dictionary);\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'pushed' },\n ]);\n } else {\n statusObj.status = 'unknown';\n }\n } catch (error) {\n statusObj.status = 'error';\n statusObj.error = error as Error;\n statusObj.errorMessage = `Error pushing dictionary ${statusObj.dictionary.key}: ${error}`;\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'error' },\n ]);\n }\n };\n\n // Process dictionaries in parallel with a concurrency limit (reuse parallelize)\n await parallelize(dictionariesStatuses, processDictionary, 5);\n\n // Stop the logger and render final state\n logger.finish();\n\n // Print per-dictionary summary similar to loadDictionaries\n const iconFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'pushed':\n case 'modified':\n return '✔';\n case 'error':\n return '✖';\n default:\n return '⏲';\n }\n };\n\n const colorFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'pushed':\n case 'modified':\n return ANSIColors.GREEN;\n case 'error':\n return ANSIColors.RED;\n default:\n return ANSIColors.BLUE;\n }\n };\n\n for (const s of dictionariesStatuses) {\n const icon = iconFor(s.status);\n const color = colorFor(s.status);\n appLogger(\n ` - ${s.dictionary.key} ${ANSIColors.GREY}[${color}${icon} ${s.status}${ANSIColors.GREY}]${ANSIColors.RESET}`\n );\n }\n\n // Output any error messages\n for (const statusObj of dictionariesStatuses) {\n if (statusObj.errorMessage) {\n appLogger(statusObj.errorMessage, {\n level: 'error',\n });\n }\n }\n\n // Handle delete or keep options\n const deleteOption = options?.deleteLocaleDictionary;\n const keepOption = options?.keepLocaleDictionary;\n\n if (deleteOption && keepOption) {\n throw new Error(\n 'Cannot specify both --deleteLocaleDictionary and --keepLocaleDictionary options.'\n );\n }\n\n if (deleteOption) {\n // Delete only the successfully pushed dictionaries\n await deleteLocalDictionaries(successfullyPushedDictionaries, options);\n } else if (keepOption) {\n // Do nothing, keep the local dictionaries\n } else {\n // Ask the user\n const answer = await askUser(\n 'Do you want to delete the local dictionaries that were successfully pushed? (yes/no): '\n );\n if (answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y') {\n await deleteLocalDictionaries(successfullyPushedDictionaries, options);\n }\n }\n } catch (error) {\n appLogger(error, {\n level: 'error',\n });\n }\n};\n\nconst askUser = (question: string): Promise<string> => {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n return new Promise((resolve) => {\n rl.question(question, (answer: string) => {\n rl.close();\n resolve(answer);\n });\n });\n};\n\nconst deleteLocalDictionaries = async (\n dictionariesToDelete: Dictionary[],\n options?: PushOptions\n): Promise<void> => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config, {\n config: {\n prefix: '',\n },\n });\n\n // Use a Set to collect all unique file paths\n const filePathsSet: Set<string> = new Set();\n\n for (const dictionary of dictionariesToDelete) {\n const { filePath } = dictionary;\n\n if (!filePath) {\n appLogger(`Dictionary ${dictionary.key} does not have a file path`, {\n level: 'error',\n });\n continue;\n }\n\n filePathsSet.add(filePath);\n }\n\n for (const filePath of filePathsSet) {\n try {\n const stats = await fsPromises.lstat(filePath);\n\n if (stats.isFile()) {\n await fsPromises.unlink(filePath);\n appLogger(`Deleted file ${formatPath(filePath)}`, {});\n } else if (stats.isDirectory()) {\n appLogger(`Path is a directory ${formatPath(filePath)}, skipping.`, {});\n } else {\n appLogger(\n `Unknown file type for ${formatPath(filePath)}, skipping.`,\n {}\n );\n }\n } catch (err) {\n appLogger(`Error deleting ${formatPath(filePath)}: ${err}`, {\n level: 'error',\n });\n }\n }\n};\n"],"mappings":"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","names":[]}