@intlayer/cli 6.0.0 → 6.0.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 (73) hide show
  1. package/dist/cjs/fill/index.cjs +76 -84
  2. package/dist/cjs/fill/index.cjs.map +1 -1
  3. package/dist/cjs/{fill/getTargetDictionary.cjs → getTargetDictionary.cjs} +6 -15
  4. package/dist/cjs/getTargetDictionary.cjs.map +1 -0
  5. package/dist/cjs/listContentDeclaration.cjs +3 -12
  6. package/dist/cjs/listContentDeclaration.cjs.map +1 -1
  7. package/dist/cjs/liveSync.cjs +2 -2
  8. package/dist/cjs/liveSync.cjs.map +1 -1
  9. package/dist/cjs/pull.cjs +3 -6
  10. package/dist/cjs/pull.cjs.map +1 -1
  11. package/dist/cjs/push.cjs +13 -9
  12. package/dist/cjs/push.cjs.map +1 -1
  13. package/dist/cjs/pushConfig.cjs +3 -10
  14. package/dist/cjs/pushConfig.cjs.map +1 -1
  15. package/dist/cjs/reviewDoc.cjs +3 -2
  16. package/dist/cjs/reviewDoc.cjs.map +1 -1
  17. package/dist/cjs/test/index.cjs +1 -1
  18. package/dist/cjs/test/index.cjs.map +1 -1
  19. package/dist/cjs/test/listMissingTranslations.cjs +8 -16
  20. package/dist/cjs/test/listMissingTranslations.cjs.map +1 -1
  21. package/dist/cjs/translateDoc.cjs +3 -2
  22. package/dist/cjs/translateDoc.cjs.map +1 -1
  23. package/dist/cjs/utils/checkAccess.cjs +79 -0
  24. package/dist/cjs/utils/checkAccess.cjs.map +1 -0
  25. package/dist/esm/fill/index.mjs +83 -74
  26. package/dist/esm/fill/index.mjs.map +1 -1
  27. package/dist/esm/{fill/getTargetDictionary.mjs → getTargetDictionary.mjs} +4 -3
  28. package/dist/esm/getTargetDictionary.mjs.map +1 -0
  29. package/dist/esm/listContentDeclaration.mjs +2 -1
  30. package/dist/esm/listContentDeclaration.mjs.map +1 -1
  31. package/dist/esm/liveSync.mjs +2 -2
  32. package/dist/esm/liveSync.mjs.map +1 -1
  33. package/dist/esm/pull.mjs +3 -6
  34. package/dist/esm/pull.mjs.map +1 -1
  35. package/dist/esm/push.mjs +11 -7
  36. package/dist/esm/push.mjs.map +1 -1
  37. package/dist/esm/pushConfig.mjs +3 -10
  38. package/dist/esm/pushConfig.mjs.map +1 -1
  39. package/dist/esm/reviewDoc.mjs +3 -2
  40. package/dist/esm/reviewDoc.mjs.map +1 -1
  41. package/dist/esm/test/index.mjs +1 -1
  42. package/dist/esm/test/index.mjs.map +1 -1
  43. package/dist/esm/test/listMissingTranslations.mjs +8 -4
  44. package/dist/esm/test/listMissingTranslations.mjs.map +1 -1
  45. package/dist/esm/translateDoc.mjs +3 -2
  46. package/dist/esm/translateDoc.mjs.map +1 -1
  47. package/dist/esm/utils/checkAccess.mjs +54 -0
  48. package/dist/esm/utils/checkAccess.mjs.map +1 -0
  49. package/dist/types/fill/index.d.ts +3 -9
  50. package/dist/types/fill/index.d.ts.map +1 -1
  51. package/dist/types/getTargetDictionary.d.ts +15 -0
  52. package/dist/types/getTargetDictionary.d.ts.map +1 -0
  53. package/dist/types/listContentDeclaration.d.ts.map +1 -1
  54. package/dist/types/pull.d.ts.map +1 -1
  55. package/dist/types/push.d.ts.map +1 -1
  56. package/dist/types/pushConfig.d.ts.map +1 -1
  57. package/dist/types/reviewDoc.d.ts.map +1 -1
  58. package/dist/types/test/listMissingTranslations.d.ts +4 -4
  59. package/dist/types/test/listMissingTranslations.d.ts.map +1 -1
  60. package/dist/types/translateDoc.d.ts.map +1 -1
  61. package/dist/types/utils/checkAccess.d.ts +5 -0
  62. package/dist/types/utils/checkAccess.d.ts.map +1 -0
  63. package/package.json +16 -16
  64. package/dist/cjs/fill/getTargetDictionary.cjs.map +0 -1
  65. package/dist/cjs/utils/checkAIAccess.cjs +0 -44
  66. package/dist/cjs/utils/checkAIAccess.cjs.map +0 -1
  67. package/dist/esm/fill/getTargetDictionary.mjs.map +0 -1
  68. package/dist/esm/utils/checkAIAccess.mjs +0 -20
  69. package/dist/esm/utils/checkAIAccess.mjs.map +0 -1
  70. package/dist/types/fill/getTargetDictionary.d.ts +0 -4
  71. package/dist/types/fill/getTargetDictionary.d.ts.map +0 -1
  72. package/dist/types/utils/checkAIAccess.d.ts +0 -4
  73. package/dist/types/utils/checkAIAccess.d.ts.map +0 -1
@@ -1,14 +1,18 @@
1
- import { getAiAPI, getOAuthAPI } from "@intlayer/api";
1
+ import { getIntlayerAPIProxy } from "@intlayer/api";
2
2
  import {
3
3
  formatLocale,
4
4
  formatPath,
5
5
  mergeDictionaries,
6
+ parallelize,
6
7
  prepareIntlayer,
7
8
  processPerLocaleDictionary,
8
9
  reduceDictionaryContent,
9
10
  writeContentDeclaration
10
11
  } from "@intlayer/chokidar";
11
12
  import {
13
+ ANSIColors,
14
+ colon,
15
+ colorize,
12
16
  colorizeKey,
13
17
  colorizePath,
14
18
  getAppLogger,
@@ -19,12 +23,14 @@ import {
19
23
  getLocalisedContent,
20
24
  getMissingLocalesContent
21
25
  } from "@intlayer/core";
22
- import dictionariesRecord from "@intlayer/dictionaries-entry";
23
- import pLimit from "p-limit";
26
+ import { getDictionaries } from "@intlayer/dictionaries-entry";
24
27
  import { relative } from "path";
25
- import { checkAIAccess } from "../utils/checkAIAccess.mjs";
28
+ import {
29
+ ensureArray,
30
+ getTargetUnmergedDictionaries
31
+ } from "../getTargetDictionary.mjs";
32
+ import { checkAIAccess } from "../utils/checkAccess.mjs";
26
33
  import { autoFill } from "./autoFill.mjs";
27
- import { ensureArray, getTargetDictionary } from "./getTargetDictionary.mjs";
28
34
  const NB_CONCURRENT_TRANSLATIONS = 8;
29
35
  const fill = async (options) => {
30
36
  const configuration = getConfiguration(options.configOptions);
@@ -40,37 +46,40 @@ const fill = async (options) => {
40
46
  const mode = options.mode ?? "complete";
41
47
  const baseLocale = options.sourceLocale ?? defaultLocale;
42
48
  const outputLocales = (options.outputLocales ? ensureArray(options.outputLocales) : locales).filter((locale) => locale !== baseLocale);
43
- checkAIAccess(configuration, options.aiOptions);
44
- let oAuth2AccessToken;
45
- if (configuration.editor.clientId) {
46
- const intlayerAuthAPI = getOAuthAPI(configuration);
47
- const oAuth2TokenResult = await intlayerAuthAPI.getOAuth2AccessToken();
48
- oAuth2AccessToken = oAuth2TokenResult.data?.accessToken;
49
- }
50
- appLogger("Starting fill function", {
51
- level: "info"
52
- });
53
- const targetUnmergedDictionaries = await getTargetDictionary(options);
49
+ const hasAIAccess = await checkAIAccess(configuration, options.aiOptions);
50
+ if (!hasAIAccess) return;
51
+ const intlayerAPI = getIntlayerAPIProxy(void 0, configuration);
52
+ const targetUnmergedDictionaries = await getTargetUnmergedDictionaries(options);
54
53
  const affectedDictionaryKeys = /* @__PURE__ */ new Set();
55
54
  targetUnmergedDictionaries.forEach((dict) => {
56
55
  affectedDictionaryKeys.add(dict.key);
57
56
  });
58
- appLogger(
59
- [
60
- "Affected dictionary keys for processing:",
61
- Array.from(affectedDictionaryKeys).map((key) => colorizeKey(key)).join(", ")
62
- ],
63
- {
64
- isVerbose: true
65
- }
57
+ appLogger([
58
+ "Affected dictionary keys for processing:",
59
+ Array.from(affectedDictionaryKeys).map((key) => colorizeKey(key)).join(", ")
60
+ ]);
61
+ const maxKeyLength = Math.max(
62
+ ...targetUnmergedDictionaries.map((dict) => dict.key.length)
63
+ );
64
+ const maxLocaleLength = Math.max(
65
+ ...locales.map((locale) => formatLocale(locale).length)
66
66
  );
67
67
  for (const targetUnmergedDictionary of targetUnmergedDictionaries) {
68
+ const dictionaryPreset = colon(
69
+ [
70
+ colorize(" - [", ANSIColors.GREY_DARK),
71
+ colorizeKey(targetUnmergedDictionary.key),
72
+ colorize("]", ANSIColors.GREY_DARK)
73
+ ].join(""),
74
+ { colSize: maxKeyLength + 6 }
75
+ );
68
76
  const dictionaryKey = targetUnmergedDictionary.key;
77
+ const dictionariesRecord = getDictionaries(configuration);
69
78
  const mainDictionaryToProcess = dictionariesRecord[dictionaryKey];
70
79
  const sourceLocale = targetUnmergedDictionary.locale ?? baseLocale;
71
80
  if (!mainDictionaryToProcess) {
72
81
  appLogger(
73
- `Dictionary with key '${colorizeKey(dictionaryKey)}' not found in dictionariesRecord. Skipping.`,
82
+ `${dictionaryPreset} Dictionary not found in dictionariesRecord. Skipping.`,
74
83
  {
75
84
  level: "warn"
76
85
  }
@@ -78,21 +87,21 @@ const fill = async (options) => {
78
87
  continue;
79
88
  }
80
89
  if (!targetUnmergedDictionary.filePath) {
81
- appLogger(
82
- `Dictionary with key '${colorizeKey(dictionaryKey)}' has no file path. Skipping.`,
83
- {
84
- level: "warn"
85
- }
86
- );
90
+ appLogger(`${dictionaryPreset} Dictionary has no file path. Skipping.`, {
91
+ level: "warn"
92
+ });
87
93
  continue;
88
94
  }
89
95
  const relativePath = relative(
90
96
  configuration.content.baseDir,
91
97
  targetUnmergedDictionary.filePath
92
98
  );
93
- appLogger(`Processing content declaration: ${colorizePath(relativePath)}`, {
94
- level: "info"
95
- });
99
+ appLogger(
100
+ `${dictionaryPreset} Processing content declaration: ${colorizePath(relativePath)}`,
101
+ {
102
+ level: "info"
103
+ }
104
+ );
96
105
  const sourceLocaleContent = getFilterTranslationsOnlyContent(
97
106
  mainDictionaryToProcess,
98
107
  sourceLocale,
@@ -100,7 +109,7 @@ const fill = async (options) => {
100
109
  );
101
110
  if (Object.keys(sourceLocaleContent).length === 0) {
102
111
  appLogger(
103
- `No content found for dictionary '${colorizeKey(dictionaryKey)}' in source locale ${formatLocale(sourceLocale)}. Skipping translation for this dictionary.`,
112
+ `${dictionaryPreset} No content found for dictionary in source locale ${formatLocale(sourceLocale)}. Skipping translation for this dictionary.`,
104
113
  {
105
114
  level: "warn"
106
115
  }
@@ -108,9 +117,6 @@ const fill = async (options) => {
108
117
  continue;
109
118
  }
110
119
  const result = [];
111
- const limit = pLimit(
112
- options.nbConcurrentTranslations ?? NB_CONCURRENT_TRANSLATIONS
113
- );
114
120
  let outputLocalesList = outputLocales;
115
121
  if (mode === "complete") {
116
122
  const missingLocales = getMissingLocalesContent(
@@ -124,10 +130,28 @@ const fill = async (options) => {
124
130
  );
125
131
  outputLocalesList = missingLocales;
126
132
  }
127
- const translationPromises = outputLocalesList.map(
128
- (targetLocale) => limit(async () => {
133
+ if (outputLocalesList.length === 0) {
134
+ appLogger(
135
+ `${dictionaryPreset} No locales to fill - Skipping dictionary`,
136
+ {
137
+ level: "warn"
138
+ }
139
+ );
140
+ continue;
141
+ }
142
+ const translationResults = await parallelize(
143
+ outputLocalesList,
144
+ async (targetLocale) => {
145
+ const localePreset = colon(
146
+ [
147
+ colorize("[", ANSIColors.GREY_DARK),
148
+ formatLocale(targetLocale),
149
+ colorize("]", ANSIColors.GREY_DARK)
150
+ ].join(""),
151
+ { colSize: maxLocaleLength }
152
+ );
129
153
  appLogger(
130
- `Preparing translation for '${colorizeKey(dictionaryKey)}' dictionary from ${formatLocale(sourceLocale)} to ${formatLocale(targetLocale)}`,
154
+ `${dictionaryPreset}${localePreset} Preparing translation for dictionary from ${formatLocale(sourceLocale)} to ${formatLocale(targetLocale)}`,
131
155
  {
132
156
  level: "info"
133
157
  }
@@ -138,36 +162,21 @@ const fill = async (options) => {
138
162
  { dictionaryKey, keyPath: [] }
139
163
  );
140
164
  try {
141
- const translationResult = await getAiAPI(
142
- void 0,
143
- configuration
144
- ).translateJSON(
145
- {
146
- entryFileContent: sourceLocaleContent.content,
147
- // Should be JSON, ensure getLocalisedContent provides this.
148
- presetOutputContent: presetOutputContent.content,
149
- // Should be JSON
150
- dictionaryDescription: mainDictionaryToProcess.description,
151
- entryLocale: sourceLocale,
152
- outputLocale: targetLocale,
153
- mode,
154
- aiOptions: options.aiOptions
155
- },
156
- {
157
- ...oAuth2AccessToken && {
158
- headers: {
159
- Authorization: `Bearer ${oAuth2AccessToken}`
160
- }
161
- }
162
- }
163
- );
165
+ const translationResult = await intlayerAPI.ai.translateJSON({
166
+ entryFileContent: sourceLocaleContent.content,
167
+ // Should be JSON, ensure getLocalisedContent provides this.
168
+ presetOutputContent: presetOutputContent.content,
169
+ // Should be JSON
170
+ dictionaryDescription: mainDictionaryToProcess.description ?? "",
171
+ entryLocale: sourceLocale,
172
+ outputLocale: targetLocale,
173
+ mode,
174
+ aiOptions: options.aiOptions
175
+ });
164
176
  if (!translationResult.data?.fileContent) {
165
- appLogger(
166
- `No content result found for '${colorizeKey(dictionaryKey)}' to ${formatLocale(targetLocale)}`,
167
- {
168
- level: "error"
169
- }
170
- );
177
+ appLogger(`${dictionaryPreset}${localePreset} No content result`, {
178
+ level: "error"
179
+ });
171
180
  return null;
172
181
  }
173
182
  const processedPerLocaleDictionary = processPerLocaleDictionary({
@@ -178,16 +187,16 @@ const fill = async (options) => {
178
187
  return processedPerLocaleDictionary;
179
188
  } catch (error) {
180
189
  appLogger(
181
- `Error filling '${colorizeKey(dictionaryKey)}' to ${formatLocale(targetLocale)}:` + error,
190
+ `${dictionaryPreset}${localePreset} ${colorize("Error filling", ANSIColors.RED)}: ` + error,
182
191
  {
183
192
  level: "error"
184
193
  }
185
194
  );
186
195
  return null;
187
196
  }
188
- })
197
+ },
198
+ options.nbConcurrentTranslations ?? NB_CONCURRENT_TRANSLATIONS
189
199
  );
190
- const translationResults = await Promise.all(translationPromises);
191
200
  translationResults.forEach((translationResult) => {
192
201
  if (translationResult) {
193
202
  result.push(translationResult);
@@ -225,7 +234,7 @@ const fill = async (options) => {
225
234
  );
226
235
  if (formattedDict.filePath) {
227
236
  appLogger(
228
- `Content declaration for '${colorizeKey(dictionaryKey)}' written to ${formatPath(formattedDict.filePath)}`,
237
+ `${dictionaryPreset} Content declaration written to ${formatPath(formattedDict.filePath)}`,
229
238
  {
230
239
  level: "info"
231
240
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/fill/index.ts"],"sourcesContent":["import { AIOptions, getAiAPI, getOAuthAPI } from '@intlayer/api'; // Importing only getAiAPI for now\nimport {\n formatLocale,\n formatPath,\n ListGitFilesOptions,\n mergeDictionaries,\n prepareIntlayer,\n processPerLocaleDictionary,\n reduceDictionaryContent,\n writeContentDeclaration,\n} from '@intlayer/chokidar';\nimport {\n colorizeKey,\n colorizePath,\n getAppLogger,\n getConfiguration,\n GetConfigurationOptions,\n Locales,\n} from '@intlayer/config';\nimport {\n type ContentNode,\n type Dictionary,\n getFilterTranslationsOnlyContent,\n getLocalisedContent,\n getMissingLocalesContent,\n} from '@intlayer/core';\nimport dictionariesRecord from '@intlayer/dictionaries-entry';\nimport pLimit from 'p-limit';\nimport { relative } from 'path';\nimport { checkAIAccess } from '../utils/checkAIAccess';\nimport { autoFill } from './autoFill';\nimport { ensureArray, getTargetDictionary } from './getTargetDictionary';\n\nconst NB_CONCURRENT_TRANSLATIONS = 8;\n\n// Arguments for the fill function\nexport type FillOptions = {\n sourceLocale?: Locales;\n outputLocales?: Locales | Locales[];\n file?: string | string[];\n mode?: 'complete' | 'review';\n keys?: string | string[];\n excludedKeys?: string | string[];\n filter?: (entry: Dictionary) => boolean; // DictionaryEntry needs to be defined\n pathFilter?: string | string[];\n gitOptions?: ListGitFilesOptions;\n configOptions?: GetConfigurationOptions;\n aiOptions?: AIOptions; // Added aiOptions to be passed to translateJSON\n verbose?: boolean;\n nbConcurrentTranslations?: number;\n build?: boolean;\n};\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 checkAIAccess(configuration, options.aiOptions);\n\n let oAuth2AccessToken: string | undefined;\n if (configuration.editor.clientId) {\n const intlayerAuthAPI = getOAuthAPI(configuration);\n const oAuth2TokenResult = await intlayerAuthAPI.getOAuth2AccessToken();\n\n oAuth2AccessToken = oAuth2TokenResult.data?.accessToken;\n }\n\n appLogger('Starting fill function', {\n level: 'info',\n });\n\n const targetUnmergedDictionaries = await getTargetDictionary(options);\n\n const affectedDictionaryKeys = new Set<string>();\n targetUnmergedDictionaries.forEach((dict) => {\n affectedDictionaryKeys.add(dict.key);\n });\n\n appLogger(\n [\n 'Affected dictionary keys for processing:',\n Array.from(affectedDictionaryKeys)\n .map((key) => colorizeKey(key))\n .join(', '),\n ],\n {\n isVerbose: true,\n }\n );\n\n for (const targetUnmergedDictionary of targetUnmergedDictionaries) {\n const dictionaryKey = targetUnmergedDictionary.key;\n const mainDictionaryToProcess = dictionariesRecord[dictionaryKey];\n const sourceLocale: Locales =\n (targetUnmergedDictionary.locale as Locales) ?? baseLocale;\n\n if (!mainDictionaryToProcess) {\n appLogger(\n `Dictionary with key '${colorizeKey(dictionaryKey)}' not found in dictionariesRecord. Skipping.`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n if (!targetUnmergedDictionary.filePath) {\n appLogger(\n `Dictionary with key '${colorizeKey(dictionaryKey)}' has no file path. Skipping.`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n const relativePath = relative(\n configuration.content.baseDir,\n targetUnmergedDictionary.filePath\n );\n\n appLogger(`Processing content declaration: ${colorizePath(relativePath)}`, {\n level: 'info',\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 `No content found for dictionary '${colorizeKey(dictionaryKey)}' 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 // 5. for each locale to translate (exclude base locale) generate json translations\n // Limit concurrent translations to 5 at a time\n const limit = pLimit(\n options.nbConcurrentTranslations ?? NB_CONCURRENT_TRANSLATIONS\n );\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 const translationPromises = outputLocalesList.map((targetLocale) =>\n limit(async () => {\n appLogger(\n `Preparing translation for '${colorizeKey(dictionaryKey)}' 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 getAiAPI(\n undefined,\n configuration\n ).translateJSON(\n {\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 ...(oAuth2AccessToken && {\n headers: {\n Authorization: `Bearer ${oAuth2AccessToken}`,\n },\n }),\n }\n );\n\n if (!translationResult.data?.fileContent) {\n appLogger(\n `No content result found for '${colorizeKey(dictionaryKey)}' to ${formatLocale(targetLocale)}`,\n {\n level: 'error',\n }\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 `Error filling '${colorizeKey(dictionaryKey)}' to ${formatLocale(targetLocale)}:` +\n error,\n {\n level: 'error',\n }\n );\n return null;\n }\n })\n );\n\n // Wait for all translations to complete\n const translationResults = await Promise.all(translationPromises);\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 `Content declaration for '${colorizeKey(dictionaryKey)}' 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\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,11 +1,12 @@
1
1
  import { listGitFiles } from "@intlayer/chokidar";
2
2
  import { getConfiguration } from "@intlayer/config";
3
- import unmergedDictionariesRecord from "@intlayer/unmerged-dictionaries-entry";
3
+ import { getUnmergedDictionaries } from "@intlayer/unmerged-dictionaries-entry";
4
4
  import { join } from "path";
5
5
  const ensureArray = (value) => [value].flat();
6
- const getTargetDictionary = async (options) => {
6
+ const getTargetUnmergedDictionaries = async (options) => {
7
7
  const configuration = getConfiguration(options.configOptions);
8
8
  const { baseDir } = configuration.content;
9
+ const unmergedDictionariesRecord = getUnmergedDictionaries(configuration);
9
10
  let result = Object.values(unmergedDictionariesRecord).flat();
10
11
  if (typeof options.file !== "undefined") {
11
12
  const fileArray = ensureArray(options.file);
@@ -46,6 +47,6 @@ const getTargetDictionary = async (options) => {
46
47
  };
47
48
  export {
48
49
  ensureArray,
49
- getTargetDictionary
50
+ getTargetUnmergedDictionaries
50
51
  };
51
52
  //# sourceMappingURL=getTargetDictionary.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/getTargetDictionary.ts"],"sourcesContent":["import { listGitFiles, ListGitFilesOptions } from '@intlayer/chokidar';\nimport { getConfiguration, GetConfigurationOptions } from '@intlayer/config';\nimport { Dictionary } from '@intlayer/core';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { join } from 'path';\n\nexport const ensureArray = <T>(value: T | T[]): T[] => [value].flat() as T[];\n\n// Arguments for the fill function\nexport type GetTargetDictionaryOptions = {\n file?: string | string[];\n keys?: string | string[];\n excludedKeys?: string | string[];\n filter?: (entry: Dictionary) => boolean; // DictionaryEntry needs to be defined\n pathFilter?: string | string[];\n gitOptions?: ListGitFilesOptions;\n configOptions?: GetConfigurationOptions;\n};\n\nexport const getTargetUnmergedDictionaries = async (\n options: GetTargetDictionaryOptions\n): Promise<Dictionary[]> => {\n const configuration = getConfiguration(options.configOptions);\n\n const { baseDir } = configuration.content;\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(configuration);\n let result = Object.values(unmergedDictionariesRecord).flat();\n\n // 1. if filePath not defined, list all content declaration files based on unmerged dictionaries list\n if (typeof options.file !== 'undefined') {\n const fileArray = ensureArray(options.file);\n const absoluteFilePaths = fileArray.map((file) => join(baseDir, file));\n\n result = result.filter(\n (dict) =>\n dict.filePath &&\n (absoluteFilePaths.includes(dict.filePath) ||\n absoluteFilePaths.includes(join(baseDir, dict.filePath)))\n );\n }\n\n if (typeof options.keys !== 'undefined') {\n result = result.filter((dict) =>\n ensureArray(options.keys)?.includes(dict.key)\n );\n }\n\n if (typeof options.excludedKeys !== 'undefined') {\n result = result.filter(\n (dict) => !ensureArray(options.excludedKeys)?.includes(dict.key)\n );\n }\n\n if (typeof options.pathFilter !== 'undefined') {\n result = result.filter((dict) =>\n ensureArray(options.pathFilter)?.includes(dict.filePath ?? '')\n );\n }\n\n if (typeof options.filter !== 'undefined') {\n result = result.filter(options.filter);\n }\n\n const gitOptions = options.gitOptions;\n if (gitOptions) {\n const gitChangedFiles = await listGitFiles(gitOptions);\n\n if (gitChangedFiles) {\n // Convert dictionary file paths to be relative to git root for comparison\n\n // Filter dictionaries based on git changed files\n result = result.filter((dict) => {\n if (!dict.filePath) return false;\n\n return gitChangedFiles.some((gitFile) => dict.filePath === gitFile);\n });\n }\n }\n\n return result.filter((dict) => !dict.autoFilled);\n};\n"],"mappings":"AAAA,SAAS,oBAAyC;AAClD,SAAS,wBAAiD;AAE1D,SAAS,+BAA+B;AACxC,SAAS,YAAY;AAEd,MAAM,cAAc,CAAI,UAAwB,CAAC,KAAK,EAAE,KAAK;AAa7D,MAAM,gCAAgC,OAC3C,YAC0B;AAC1B,QAAM,gBAAgB,iBAAiB,QAAQ,aAAa;AAE5D,QAAM,EAAE,QAAQ,IAAI,cAAc;AAElC,QAAM,6BAA6B,wBAAwB,aAAa;AACxE,MAAI,SAAS,OAAO,OAAO,0BAA0B,EAAE,KAAK;AAG5D,MAAI,OAAO,QAAQ,SAAS,aAAa;AACvC,UAAM,YAAY,YAAY,QAAQ,IAAI;AAC1C,UAAM,oBAAoB,UAAU,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC;AAErE,aAAS,OAAO;AAAA,MACd,CAAC,SACC,KAAK,aACJ,kBAAkB,SAAS,KAAK,QAAQ,KACvC,kBAAkB,SAAS,KAAK,SAAS,KAAK,QAAQ,CAAC;AAAA,IAC7D;AAAA,EACF;AAEA,MAAI,OAAO,QAAQ,SAAS,aAAa;AACvC,aAAS,OAAO;AAAA,MAAO,CAAC,SACtB,YAAY,QAAQ,IAAI,GAAG,SAAS,KAAK,GAAG;AAAA,IAC9C;AAAA,EACF;AAEA,MAAI,OAAO,QAAQ,iBAAiB,aAAa;AAC/C,aAAS,OAAO;AAAA,MACd,CAAC,SAAS,CAAC,YAAY,QAAQ,YAAY,GAAG,SAAS,KAAK,GAAG;AAAA,IACjE;AAAA,EACF;AAEA,MAAI,OAAO,QAAQ,eAAe,aAAa;AAC7C,aAAS,OAAO;AAAA,MAAO,CAAC,SACtB,YAAY,QAAQ,UAAU,GAAG,SAAS,KAAK,YAAY,EAAE;AAAA,IAC/D;AAAA,EACF;AAEA,MAAI,OAAO,QAAQ,WAAW,aAAa;AACzC,aAAS,OAAO,OAAO,QAAQ,MAAM;AAAA,EACvC;AAEA,QAAM,aAAa,QAAQ;AAC3B,MAAI,YAAY;AACd,UAAM,kBAAkB,MAAM,aAAa,UAAU;AAErD,QAAI,iBAAiB;AAInB,eAAS,OAAO,OAAO,CAAC,SAAS;AAC/B,YAAI,CAAC,KAAK,SAAU,QAAO;AAE3B,eAAO,gBAAgB,KAAK,CAAC,YAAY,KAAK,aAAa,OAAO;AAAA,MACpE,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,OAAO,OAAO,CAAC,SAAS,CAAC,KAAK,UAAU;AACjD;","names":[]}
@@ -6,10 +6,11 @@ import {
6
6
  getAppLogger,
7
7
  getConfiguration
8
8
  } from "@intlayer/config";
9
- import unmergedDictionariesRecord from "@intlayer/unmerged-dictionaries-entry";
9
+ import { getUnmergedDictionaries } from "@intlayer/unmerged-dictionaries-entry";
10
10
  import { relative } from "path";
11
11
  const listContentDeclarationRows = (options) => {
12
12
  const config = getConfiguration(options?.configOptions);
13
+ const unmergedDictionariesRecord = getUnmergedDictionaries(config);
13
14
  const rows = Object.values(unmergedDictionariesRecord).flat().map((dictionary) => ({
14
15
  key: dictionary.key ?? "",
15
16
  path: relative(config.content.baseDir, dictionary.filePath ?? "Remote")
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/listContentDeclaration.ts"],"sourcesContent":["import { formatPath } from '@intlayer/chokidar';\nimport {\n colon,\n colorizeKey,\n colorizeNumber,\n getAppLogger,\n getConfiguration,\n type GetConfigurationOptions,\n} from '@intlayer/config';\nimport unmergedDictionariesRecord from '@intlayer/unmerged-dictionaries-entry';\nimport { relative } from 'path';\n\ntype ListContentDeclarationOptions = {\n configOptions?: GetConfigurationOptions;\n};\n\nexport const listContentDeclarationRows = (\n options?: ListContentDeclarationOptions\n) => {\n const config = getConfiguration(options?.configOptions);\n\n const rows = Object.values(unmergedDictionariesRecord)\n .flat()\n .map((dictionary) => ({\n key: dictionary.key ?? '',\n path: relative(config.content.baseDir, dictionary.filePath ?? 'Remote'),\n }));\n return rows;\n};\n\nexport const listContentDeclaration = (\n options?: ListContentDeclarationOptions\n) => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config, {\n config: {\n prefix: '',\n },\n });\n\n const rows = listContentDeclarationRows(options);\n\n const lines = rows.map((r) =>\n [\n colon(` - ${colorizeKey(r.key)}`, {\n colSize: rows.map((r) => r.key.length),\n maxSize: 60,\n }),\n ' - ',\n formatPath(r.path),\n ].join('')\n );\n\n appLogger(`Content declaration files:`);\n\n lines.forEach((l) => {\n appLogger(l, {\n level: 'info',\n });\n });\n\n appLogger(`Total content declaration files: ${colorizeNumber(rows.length)}`);\n};\n"],"mappings":"AAAA,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,OAAO,gCAAgC;AACvC,SAAS,gBAAgB;AAMlB,MAAM,6BAA6B,CACxC,YACG;AACH,QAAM,SAAS,iBAAiB,SAAS,aAAa;AAEtD,QAAM,OAAO,OAAO,OAAO,0BAA0B,EAClD,KAAK,EACL,IAAI,CAAC,gBAAgB;AAAA,IACpB,KAAK,WAAW,OAAO;AAAA,IACvB,MAAM,SAAS,OAAO,QAAQ,SAAS,WAAW,YAAY,QAAQ;AAAA,EACxE,EAAE;AACJ,SAAO;AACT;AAEO,MAAM,yBAAyB,CACpC,YACG;AACH,QAAM,SAAS,iBAAiB,SAAS,aAAa;AACtD,QAAM,YAAY,aAAa,QAAQ;AAAA,IACrC,QAAQ;AAAA,MACN,QAAQ;AAAA,IACV;AAAA,EACF,CAAC;AAED,QAAM,OAAO,2BAA2B,OAAO;AAE/C,QAAM,QAAQ,KAAK;AAAA,IAAI,CAAC,MACtB;AAAA,MACE,MAAM,MAAM,YAAY,EAAE,GAAG,CAAC,IAAI;AAAA,QAChC,SAAS,KAAK,IAAI,CAACA,OAAMA,GAAE,IAAI,MAAM;AAAA,QACrC,SAAS;AAAA,MACX,CAAC;AAAA,MACD;AAAA,MACA,WAAW,EAAE,IAAI;AAAA,IACnB,EAAE,KAAK,EAAE;AAAA,EACX;AAEA,YAAU,4BAA4B;AAEtC,QAAM,QAAQ,CAAC,MAAM;AACnB,cAAU,GAAG;AAAA,MACX,OAAO;AAAA,IACT,CAAC;AAAA,EACH,CAAC;AAED,YAAU,oCAAoC,eAAe,KAAK,MAAM,CAAC,EAAE;AAC7E;","names":["r"]}
1
+ {"version":3,"sources":["../../src/listContentDeclaration.ts"],"sourcesContent":["import { formatPath } from '@intlayer/chokidar';\nimport {\n colon,\n colorizeKey,\n colorizeNumber,\n getAppLogger,\n getConfiguration,\n type GetConfigurationOptions,\n} from '@intlayer/config';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { relative } from 'path';\n\ntype ListContentDeclarationOptions = {\n configOptions?: GetConfigurationOptions;\n};\n\nexport const listContentDeclarationRows = (\n options?: ListContentDeclarationOptions\n) => {\n const config = getConfiguration(options?.configOptions);\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(config);\n\n const rows = Object.values(unmergedDictionariesRecord)\n .flat()\n .map((dictionary) => ({\n key: dictionary.key ?? '',\n path: relative(config.content.baseDir, dictionary.filePath ?? 'Remote'),\n }));\n return rows;\n};\n\nexport const listContentDeclaration = (\n options?: ListContentDeclarationOptions\n) => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config, {\n config: {\n prefix: '',\n },\n });\n\n const rows = listContentDeclarationRows(options);\n\n const lines = rows.map((r) =>\n [\n colon(` - ${colorizeKey(r.key)}`, {\n colSize: rows.map((r) => r.key.length),\n maxSize: 60,\n }),\n ' - ',\n formatPath(r.path),\n ].join('')\n );\n\n appLogger(`Content declaration files:`);\n\n lines.forEach((l) => {\n appLogger(l, {\n level: 'info',\n });\n });\n\n appLogger(`Total content declaration files: ${colorizeNumber(rows.length)}`);\n};\n"],"mappings":"AAAA,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,+BAA+B;AACxC,SAAS,gBAAgB;AAMlB,MAAM,6BAA6B,CACxC,YACG;AACH,QAAM,SAAS,iBAAiB,SAAS,aAAa;AAEtD,QAAM,6BAA6B,wBAAwB,MAAM;AAEjE,QAAM,OAAO,OAAO,OAAO,0BAA0B,EAClD,KAAK,EACL,IAAI,CAAC,gBAAgB;AAAA,IACpB,KAAK,WAAW,OAAO;AAAA,IACvB,MAAM,SAAS,OAAO,QAAQ,SAAS,WAAW,YAAY,QAAQ;AAAA,EACxE,EAAE;AACJ,SAAO;AACT;AAEO,MAAM,yBAAyB,CACpC,YACG;AACH,QAAM,SAAS,iBAAiB,SAAS,aAAa;AACtD,QAAM,YAAY,aAAa,QAAQ;AAAA,IACrC,QAAQ;AAAA,MACN,QAAQ;AAAA,IACV;AAAA,EACF,CAAC;AAED,QAAM,OAAO,2BAA2B,OAAO;AAE/C,QAAM,QAAQ,KAAK;AAAA,IAAI,CAAC,MACtB;AAAA,MACE,MAAM,MAAM,YAAY,EAAE,GAAG,CAAC,IAAI;AAAA,QAChC,SAAS,KAAK,IAAI,CAACA,OAAMA,GAAE,IAAI,MAAM;AAAA,QACrC,SAAS;AAAA,MACX,CAAC;AAAA,MACD;AAAA,MACA,WAAW,EAAE,IAAI;AAAA,IACnB,EAAE,KAAK,EAAE;AAAA,EACX;AAEA,YAAU,4BAA4B;AAEtC,QAAM,QAAQ,CAAC,MAAM;AACnB,cAAU,GAAG;AAAA,MACX,OAAO;AAAA,IACT,CAAC;AAAA,EACH,CAAC;AAED,YAAU,oCAAoC,eAAe,KAAK,MAAM,CAAC,EAAE;AAC7E;","names":["r"]}
@@ -116,7 +116,7 @@ const liveSync = async (options) => {
116
116
  "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
117
117
  "Access-Control-Allow-Headers": "Content-Type, Authorization"
118
118
  });
119
- const dictionaries = getDictionaries();
119
+ const dictionaries = getDictionaries(configuration);
120
120
  const prefix = "/dictionaries/";
121
121
  if (req.url.startsWith(prefix)) {
122
122
  const [key, locale] = decodeURIComponent(req.url).slice(prefix.length).split("/");
@@ -143,7 +143,7 @@ const liveSync = async (options) => {
143
143
  "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
144
144
  "Access-Control-Allow-Headers": "Content-Type, Authorization"
145
145
  });
146
- const unmergedDictionaries = getUnmergedDictionaries();
146
+ const unmergedDictionaries = getUnmergedDictionaries(configuration);
147
147
  const prefix = "/unmerged_dictionaries/";
148
148
  if (req.url.startsWith(prefix)) {
149
149
  const key = decodeURIComponent(req.url.slice(prefix.length));
@@ -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();\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();\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":[]}
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":[]}
package/dist/esm/pull.mjs CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  import { existsSync } from "fs";
13
13
  import { join } from "path";
14
14
  import { PullLogger } from "./pullLog.mjs";
15
+ import { checkCMSAuth } from "./utils/checkAccess.mjs";
15
16
  const pull = async (options) => {
16
17
  const appLogger = getAppLogger(options?.configOptions?.override, {
17
18
  config: {
@@ -20,12 +21,8 @@ const pull = async (options) => {
20
21
  });
21
22
  try {
22
23
  const config = getConfiguration(options?.configOptions);
23
- const { clientId, clientSecret } = config.editor;
24
- if (!clientId || !clientSecret) {
25
- throw new Error(
26
- "Missing OAuth2 client ID or client secret. To get access token go to https://intlayer.org/dashboard/project."
27
- );
28
- }
24
+ const hasCMSAuth = await checkCMSAuth(config);
25
+ if (!hasCMSAuth) return;
29
26
  const intlayerAPI = getIntlayerAPIProxy(void 0, config);
30
27
  const getDictionariesUpdateTimestampResult = await intlayerAPI.dictionary.getDictionariesUpdateTimestamp();
31
28
  if (!getDictionariesUpdateTimestampResult.data) {
@@ -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';\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 const { clientId, clientSecret } = config.editor;\n\n if (!clientId || !clientSecret) {\n throw new Error(\n 'Missing OAuth2 client ID or client secret. To get access token go to https://intlayer.org/dashboard/project.'\n );\n }\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 './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":[]}
package/dist/esm/push.mjs CHANGED
@@ -9,10 +9,12 @@ import {
9
9
  getAppLogger,
10
10
  getConfiguration
11
11
  } from "@intlayer/config";
12
- import dictionariesRecord from "@intlayer/dictionaries-entry";
12
+ import { getDictionaries } from "@intlayer/dictionaries-entry";
13
13
  import * as fsPromises from "fs/promises";
14
+ import { join } from "path";
14
15
  import * as readline from "readline";
15
16
  import { PushLogger } from "./pushLog.mjs";
17
+ import { checkCMSAuth } from "./utils/checkAccess.mjs";
16
18
  const push = async (options) => {
17
19
  const config = getConfiguration(options?.configOptions);
18
20
  const appLogger = getAppLogger(config, {
@@ -20,14 +22,11 @@ const push = async (options) => {
20
22
  prefix: ""
21
23
  }
22
24
  });
23
- const { clientId, clientSecret } = config.editor;
24
25
  try {
25
- if (!clientId || !clientSecret) {
26
- throw new Error(
27
- "Missing OAuth2 client ID or client secret. To get access token go to https://intlayer.org/dashboard/project."
28
- );
29
- }
26
+ const hasCMSAuth = await checkCMSAuth(config);
27
+ if (!hasCMSAuth) return;
30
28
  const intlayerAPI = getIntlayerAPIProxy(void 0, config);
29
+ const dictionariesRecord = getDictionaries(config);
31
30
  let dictionaries = Object.values(dictionariesRecord);
32
31
  const existingDictionariesKeys = Object.keys(dictionariesRecord);
33
32
  if (options?.dictionaries) {
@@ -50,6 +49,11 @@ const push = async (options) => {
50
49
  }
51
50
  if (options?.gitOptions) {
52
51
  const gitFiles = await listGitFiles(options.gitOptions);
52
+ dictionaries = dictionaries.filter(
53
+ (dictionary) => gitFiles.includes(
54
+ join(config.content.baseDir, dictionary.filePath ?? "")
55
+ )
56
+ );
53
57
  }
54
58
  if (dictionaries.length === 0) {
55
59
  appLogger("No local dictionaries found", {