@intlayer/cli 8.2.1 → 8.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/IntlayerEventListener.cjs +1 -1
- package/dist/cjs/IntlayerEventListener.cjs.map +1 -1
- package/dist/cjs/auth/login.cjs +2 -2
- package/dist/cjs/auth/login.cjs.map +1 -1
- package/dist/cjs/build.cjs +1 -1
- package/dist/cjs/build.cjs.map +1 -1
- package/dist/cjs/ci.cjs +1 -1
- package/dist/cjs/ci.cjs.map +1 -1
- package/dist/cjs/cli.cjs +1 -1
- package/dist/cjs/cli.cjs.map +1 -1
- package/dist/cjs/config.cjs +1 -1
- package/dist/cjs/config.cjs.map +1 -1
- package/dist/cjs/extract.cjs +1 -1
- package/dist/cjs/extract.cjs.map +1 -1
- package/dist/cjs/fill/deepMergeContent.cjs +1 -1
- package/dist/cjs/fill/deepMergeContent.cjs.map +1 -1
- package/dist/cjs/fill/fill.cjs +1 -1
- package/dist/cjs/fill/fill.cjs.map +1 -1
- package/dist/cjs/fill/getFilterMissingContentPerLocale.cjs +1 -1
- package/dist/cjs/fill/getFilterMissingContentPerLocale.cjs.map +1 -1
- package/dist/cjs/fill/listTranslationsTasks.cjs +1 -1
- package/dist/cjs/fill/translateDictionary.cjs +1 -1
- package/dist/cjs/fill/translateDictionary.cjs.map +1 -1
- package/dist/cjs/fill/writeFill.cjs +1 -1
- package/dist/cjs/fill/writeFill.cjs.map +1 -1
- package/dist/cjs/getTargetDictionary.cjs +1 -1
- package/dist/cjs/init.cjs +1 -1
- package/dist/cjs/initMCP.cjs +1 -1
- package/dist/cjs/initSkills.cjs +1 -1
- package/dist/cjs/listContentDeclaration.cjs +1 -1
- package/dist/cjs/listProjects.cjs +1 -1
- package/dist/cjs/liveSync.cjs +1 -1
- package/dist/cjs/liveSync.cjs.map +1 -1
- package/dist/cjs/pull.cjs +1 -1
- package/dist/cjs/pull.cjs.map +1 -1
- package/dist/cjs/push/pullLog.cjs +1 -1
- package/dist/cjs/push/push.cjs +1 -1
- package/dist/cjs/push/push.cjs.map +1 -1
- package/dist/cjs/pushConfig.cjs +1 -1
- package/dist/cjs/pushConfig.cjs.map +1 -1
- package/dist/cjs/reviewDoc/reviewDoc.cjs +1 -1
- package/dist/cjs/reviewDoc/reviewDoc.cjs.map +1 -1
- package/dist/cjs/reviewDoc/reviewDocBlockAware.cjs +1 -1
- package/dist/cjs/searchDoc.cjs +1 -1
- package/dist/cjs/searchDoc.cjs.map +1 -1
- package/dist/cjs/test/listMissingTranslations.cjs +1 -1
- package/dist/cjs/test/listMissingTranslations.cjs.map +1 -1
- package/dist/cjs/test/test.cjs +1 -1
- package/dist/cjs/translateDoc/translateDoc.cjs +1 -1
- package/dist/cjs/translateDoc/translateDoc.cjs.map +1 -1
- package/dist/cjs/translateDoc/translateFile.cjs +2 -2
- package/dist/cjs/watch.cjs +1 -1
- package/dist/cjs/watch.cjs.map +1 -1
- package/dist/esm/IntlayerEventListener.mjs +1 -1
- package/dist/esm/IntlayerEventListener.mjs.map +1 -1
- package/dist/esm/auth/login.mjs +2 -2
- package/dist/esm/auth/login.mjs.map +1 -1
- package/dist/esm/build.mjs +1 -1
- package/dist/esm/build.mjs.map +1 -1
- package/dist/esm/ci.mjs +1 -1
- package/dist/esm/ci.mjs.map +1 -1
- package/dist/esm/cli.mjs +1 -1
- package/dist/esm/cli.mjs.map +1 -1
- package/dist/esm/config.mjs +1 -1
- package/dist/esm/config.mjs.map +1 -1
- package/dist/esm/extract.mjs +1 -1
- package/dist/esm/extract.mjs.map +1 -1
- package/dist/esm/fill/deepMergeContent.mjs +1 -1
- package/dist/esm/fill/deepMergeContent.mjs.map +1 -1
- package/dist/esm/fill/fill.mjs +1 -1
- package/dist/esm/fill/fill.mjs.map +1 -1
- package/dist/esm/fill/getFilterMissingContentPerLocale.mjs +1 -1
- package/dist/esm/fill/getFilterMissingContentPerLocale.mjs.map +1 -1
- package/dist/esm/fill/listTranslationsTasks.mjs +1 -1
- package/dist/esm/fill/translateDictionary.mjs +1 -1
- package/dist/esm/fill/translateDictionary.mjs.map +1 -1
- package/dist/esm/fill/writeFill.mjs +1 -1
- package/dist/esm/fill/writeFill.mjs.map +1 -1
- package/dist/esm/getTargetDictionary.mjs +1 -1
- package/dist/esm/init.mjs +1 -1
- package/dist/esm/initMCP.mjs +1 -1
- package/dist/esm/initSkills.mjs +1 -1
- package/dist/esm/listContentDeclaration.mjs +1 -1
- package/dist/esm/listProjects.mjs +1 -1
- package/dist/esm/liveSync.mjs +1 -1
- package/dist/esm/liveSync.mjs.map +1 -1
- package/dist/esm/pull.mjs +1 -1
- package/dist/esm/pull.mjs.map +1 -1
- package/dist/esm/push/pullLog.mjs +1 -1
- package/dist/esm/push/push.mjs +1 -1
- package/dist/esm/push/push.mjs.map +1 -1
- package/dist/esm/pushConfig.mjs +1 -1
- package/dist/esm/pushConfig.mjs.map +1 -1
- package/dist/esm/reviewDoc/reviewDoc.mjs +1 -1
- package/dist/esm/reviewDoc/reviewDoc.mjs.map +1 -1
- package/dist/esm/reviewDoc/reviewDocBlockAware.mjs +1 -1
- package/dist/esm/searchDoc.mjs +1 -1
- package/dist/esm/searchDoc.mjs.map +1 -1
- package/dist/esm/test/listMissingTranslations.mjs +1 -1
- package/dist/esm/test/listMissingTranslations.mjs.map +1 -1
- package/dist/esm/test/test.mjs +1 -1
- package/dist/esm/translateDoc/translateDoc.mjs +1 -1
- package/dist/esm/translateDoc/translateDoc.mjs.map +1 -1
- package/dist/esm/translateDoc/translateFile.mjs +2 -2
- package/dist/esm/watch.mjs +1 -1
- package/dist/esm/watch.mjs.map +1 -1
- package/dist/types/auth/login.d.ts.map +1 -1
- package/dist/types/build.d.ts.map +1 -1
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/extract.d.ts.map +1 -1
- package/dist/types/fill/fill.d.ts.map +1 -1
- package/dist/types/fill/getFilterMissingContentPerLocale.d.ts.map +1 -1
- package/dist/types/fill/translateDictionary.d.ts.map +1 -1
- package/dist/types/pull.d.ts.map +1 -1
- package/dist/types/push/push.d.ts.map +1 -1
- package/dist/types/pushConfig.d.ts.map +1 -1
- package/dist/types/reviewDoc/reviewDoc.d.ts.map +1 -1
- package/dist/types/searchDoc.d.ts.map +1 -1
- package/dist/types/test/listMissingTranslations.d.ts.map +1 -1
- package/dist/types/watch.d.ts.map +1 -1
- package/package.json +14 -14
package/dist/cjs/pull.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull.cjs","names":["checkCMSAuth","PullLogger","ANSIColors"],"sources":["../../src/pull.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n type DictionaryStatus,\n writeContentDeclaration,\n} from '@intlayer/chokidar/build';\nimport { parallelize } from '@intlayer/chokidar/utils';\nimport { ANSIColors, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { getProjectRequire } from '@intlayer/config/utils';\nimport type { Dictionary } from '@intlayer/types';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\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\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 const unmergedDictionariesRecord = getUnmergedDictionaries(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, any> =\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 // Filter by location\n distantDictionariesUpdateTimeStamp = Object.fromEntries(\n Object.entries(distantDictionariesUpdateTimeStamp).filter(([key]) => {\n const localDictionaries = unmergedDictionariesRecord[key];\n const location =\n localDictionaries?.[0]?.location ??\n config.dictionary?.location ??\n 'remote';\n\n return location === 'remote' || location === 'hybrid';\n })\n );\n\n // Load local cached remote dictionaries (if any)\n const remoteDictionariesPath = join(\n config.system.mainDir,\n 'remote_dictionaries.cjs'\n );\n const requireFunction = config.build?.require ?? getProjectRequire();\n const remoteDictionariesRecord: Record<string, any> = existsSync(\n remoteDictionariesPath\n )\n ? (requireFunction(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, remoteUpdatedAtValue]) => {\n if (!remoteUpdatedAtValue) return true;\n\n const remoteUpdatedAt =\n typeof remoteUpdatedAtValue === 'object'\n ? (remoteUpdatedAtValue as any).updatedAt\n : remoteUpdatedAtValue;\n\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, remoteUpdatedAtValue]) => {\n const remoteUpdatedAt =\n typeof remoteUpdatedAtValue === 'object'\n ? (remoteUpdatedAtValue as any).updatedAt\n : remoteUpdatedAtValue;\n\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 // Check if there is a local version of this dictionary that is hybrid\n const localDictionaries =\n unmergedDictionariesRecord[statusObj.dictionaryKey];\n const localAndRemoteDictionary = localDictionaries?.find(\n (d) => d.location === 'hybrid'\n );\n\n if (localAndRemoteDictionary) {\n // We want to preserve the local properties but use the remote content\n sourceDictionary = {\n ...sourceDictionary,\n location: 'hybrid',\n filePath: localAndRemoteDictionary.filePath,\n localId: localAndRemoteDictionary.localId,\n };\n }\n\n // Now, write the dictionary to local file\n const { status } = await writeContentDeclaration(\n sourceDictionary,\n config,\n options\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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"}
|
|
1
|
+
{"version":3,"file":"pull.cjs","names":["checkCMSAuth","PullLogger","ANSIColors"],"sources":["../../src/pull.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n type DictionaryStatus,\n writeContentDeclaration,\n} from '@intlayer/chokidar/build';\nimport { logConfigDetails } from '@intlayer/chokidar/cli';\nimport { parallelize } from '@intlayer/chokidar/utils';\nimport { ANSIColors, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { getProjectRequire } from '@intlayer/config/utils';\nimport type { Dictionary } from '@intlayer/types';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\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\n try {\n const config = getConfiguration(options?.configOptions);\n logConfigDetails(options?.configOptions);\n\n const hasCMSAuth = await checkCMSAuth(config);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, config);\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(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, any> =\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 // Filter by location\n distantDictionariesUpdateTimeStamp = Object.fromEntries(\n Object.entries(distantDictionariesUpdateTimeStamp).filter(([key]) => {\n const localDictionaries = unmergedDictionariesRecord[key];\n const location =\n localDictionaries?.[0]?.location ??\n config.dictionary?.location ??\n 'remote';\n\n return location === 'remote' || location === 'hybrid';\n })\n );\n\n // Load local cached remote dictionaries (if any)\n const remoteDictionariesPath = join(\n config.system.mainDir,\n 'remote_dictionaries.cjs'\n );\n const requireFunction = config.build?.require ?? getProjectRequire();\n const remoteDictionariesRecord: Record<string, any> = existsSync(\n remoteDictionariesPath\n )\n ? (requireFunction(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, remoteUpdatedAtValue]) => {\n if (!remoteUpdatedAtValue) return true;\n\n const remoteUpdatedAt =\n typeof remoteUpdatedAtValue === 'object'\n ? (remoteUpdatedAtValue as any).updatedAt\n : remoteUpdatedAtValue;\n\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, remoteUpdatedAtValue]) => {\n const remoteUpdatedAt =\n typeof remoteUpdatedAtValue === 'object'\n ? (remoteUpdatedAtValue as any).updatedAt\n : remoteUpdatedAtValue;\n\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 // Check if there is a local version of this dictionary that is hybrid\n const localDictionaries =\n unmergedDictionariesRecord[statusObj.dictionaryKey];\n const localAndRemoteDictionary = localDictionaries?.find(\n (d) => d.location === 'hybrid'\n );\n\n if (localAndRemoteDictionary) {\n // We want to preserve the local properties but use the remote content\n sourceDictionary = {\n ...sourceDictionary,\n location: 'hybrid',\n filePath: localAndRemoteDictionary.filePath,\n localId: localAndRemoteDictionary.localId,\n };\n }\n\n // Now, write the dictionary to local file\n const { status } = await writeContentDeclaration(\n sourceDictionary,\n config,\n options\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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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`../_virtual/_rolldown/runtime.cjs`);let e=require(`@intlayer/config/
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`../_virtual/_rolldown/runtime.cjs`);let e=require(`@intlayer/config/logger`),t=require(`@intlayer/config/node`);var n=class{statuses=[];spinnerTimer=null;spinnerIndex=0;renderedLines=0;spinnerFrames=e.spinnerFrames;isFinished=!1;prefix;lastRenderedState=``;constructor(){this.prefix=(0,t.getConfiguration)().log.prefix}update(e){if(!this.isFinished){for(let t of e){let e=this.statuses.findIndex(e=>e.dictionaryKey===t.dictionaryKey);e>=0?this.statuses[e]=t:this.statuses.push(t)}this.startSpinner(),this.render()}}finish(){this.isFinished=!0,this.stopSpinner(),this.render()}startSpinner(){this.spinnerTimer||this.isFinished||(this.spinnerTimer=setInterval(()=>{this.spinnerIndex=(this.spinnerIndex+1)%this.spinnerFrames.length,this.render()},100))}stopSpinner(){this.spinnerTimer&&=(clearInterval(this.spinnerTimer),null)}render(){let{total:t,done:n,success:r,errors:i}=this.computeProgress(),a=this.spinnerFrames[this.spinnerIndex],o=[],s=n===t,c=`dictionaries: ${n}/${t}`,l=[];r>0&&l.push(`ok: ${r}`),i>0&&l.push((0,e.colorize)(`errors: ${i}`,e.ANSIColors.RED));let u=l.length>0?` (${l.join(`, `)})`:``;s?o.push(`${this.prefix} ${(0,e.colorize)(`✔`,e.ANSIColors.GREEN)} fetched ${c}${u}`):o.push(`${this.prefix} ${(0,e.colorize)(a,e.ANSIColors.BLUE)} fetching ${c}${u}`);let d=o.join(`
|
|
2
2
|
`);if(d===this.lastRenderedState)return;this.lastRenderedState=d,this.renderedLines>0&&process.stdout.write(`\x1b[${this.renderedLines}F`);let f=Math.max(this.renderedLines,o.length);for(let e=0;e<f;e++){process.stdout.write(`\x1B[2K`);let t=o[e];t!==void 0&&process.stdout.write(t),process.stdout.write(`
|
|
3
3
|
`)}this.renderedLines=o.length}computeProgress(){let e=new Set(this.statuses.map(e=>e.dictionaryKey)),t=new Set([`fetched`,`imported`,`updated`,`up-to-date`,`reimported in JSON`,`new content file`,`error`]),n=new Set([`fetched`,`imported`,`updated`,`up-to-date`,`reimported in JSON`,`new content file`]),r=this.statuses.filter(e=>t.has(e.status)).length,i=this.statuses.filter(e=>n.has(e.status)).length,a=this.statuses.filter(e=>e.status===`error`).length;return{total:e.size,done:r,success:i,errors:a}}};exports.PullLogger=n;
|
|
4
4
|
//# sourceMappingURL=pullLog.cjs.map
|
package/dist/cjs/push/push.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`../utils/checkAccess.cjs`),n=require(`../pushLog.cjs`);let r=require(`@intlayer/api`),i=require(`@intlayer/
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`../utils/checkAccess.cjs`),n=require(`../pushLog.cjs`);let r=require(`@intlayer/api`),i=require(`@intlayer/config/logger`),a=require(`@intlayer/chokidar/cli`),o=require(`@intlayer/chokidar/utils`),s=require(`@intlayer/config/node`),c=require(`node:path`),l=require(`node:fs/promises`);l=e.__toESM(l);let u=require(`@intlayer/unmerged-dictionaries-entry`),d=require(`@intlayer/chokidar/build`);const f={pushed:{icon:`✔`,color:i.ANSIColors.GREEN},modified:{icon:`✔`,color:i.ANSIColors.GREEN},error:{icon:`✖`,color:i.ANSIColors.RED},default:{icon:`⏲`,color:i.ANSIColors.BLUE}},p=e=>f[e]??f.default,m=async e=>{let l=(0,s.getConfiguration)(e?.configOptions);(0,a.logConfigDetails)(e?.configOptions);let f=(0,i.getAppLogger)(l);e?.build===!0?await(0,d.prepareIntlayer)(l,{forceRun:!0}):e?.build===void 0&&await(0,d.prepareIntlayer)(l);try{if(!await t.checkCMSAuth(l))return;let s=(0,r.getIntlayerAPIProxy)(void 0,l),m=(0,u.getUnmergedDictionaries)(l),g=Object.values(m).flat(),_=Array.from(new Set(g.map(e=>e.location).filter(e=>e&&![`remote`,`local`,`hybrid`].includes(e)))),v=[];if(_.length>0){let{multiselect:e,confirm:t,isCancel:n}=await import(`@clack/prompts`);if(_.length===1){let e=await t({message:`Do you want to push dictionaries with custom location ${(0,i.colorize)(_[0],i.ANSIColors.BLUE,i.ANSIColors.RESET)}?`,initialValue:!1});if(n(e))return;e&&(v=[_[0]])}else{let t=await e({message:`Select custom locations to push:`,options:_.map(e=>({value:e,label:e})),required:!1});if(n(t))return;v=t}}let y=g.filter(e=>{let t=e.location??l.dictionary?.location??`local`;return t===`remote`||t===`hybrid`||v.includes(t)});if(y.length===0){f(`No dictionaries found to push. Only dictionaries with location ${(0,i.colorize)(`remote`,i.ANSIColors.BLUE,i.ANSIColors.RESET)}, ${(0,i.colorize)(`hybrid`,i.ANSIColors.BLUE,i.ANSIColors.RESET)} or selected custom locations are pushed.`,{level:`warn`}),f(`You can set the location in your dictionary file (e.g. ${(0,i.colorize)(`{ key: 'my-key', location: 'hybrid', ... }`,i.ANSIColors.BLUE,i.ANSIColors.RESET)} or globally in your intlayer.config.ts file (e.g. ${(0,i.colorize)(`{ dictionary: { location: 'hybrid' } }`,i.ANSIColors.BLUE,i.ANSIColors.RESET)}).`,{level:`info`});return}let b=Object.keys(m);if(e?.dictionaries){let t=e.dictionaries.filter(e=>!b.includes(e));t.length>0&&f(`The following dictionaries do not exist: ${t.join(`, `)} and have been ignored.`,{level:`error`}),y=y.filter(t=>e.dictionaries?.includes(t.key))}if(e?.gitOptions){let t=await(0,a.listGitFiles)(e.gitOptions);y=y.filter(e=>t.includes((0,c.join)(l.content.baseDir,e.filePath??``)))}if(y.length===0){f(`No local dictionaries found`,{level:`error`});return}f(`Pushing dictionaries:`);let x=y.map(e=>({dictionary:e,status:`pending`})),S=new n.PushLogger;S.update(x.map(e=>({dictionaryKey:e.dictionary.key,status:`pending`})));let C=[];await(0,o.parallelize)(x,async e=>{e.status=`pushing`,S.update([{dictionaryKey:e.dictionary.key,status:`pushing`}]);try{let t=await s.dictionary.pushDictionaries([e.dictionary]),n=t.data?.updatedDictionaries??[],r=t.data?.newDictionaries??[],i=[...n,...r];for(let e of i){let t=m[e.key]?.find(t=>t.localId===e.localId);t&&await(0,d.writeContentDeclaration)({...t,id:e.id},l)}n.some(t=>t.key===e.dictionary.key)?(e.status=`modified`,C.push(e.dictionary),S.update([{dictionaryKey:e.dictionary.key,status:`modified`}])):r.some(t=>t.key===e.dictionary.key)?(e.status=`pushed`,C.push(e.dictionary),S.update([{dictionaryKey:e.dictionary.key,status:`pushed`}])):e.status=`unknown`}catch(t){e.status=`error`,e.error=t,e.errorMessage=`Error pushing dictionary ${e.dictionary.key}: ${t}`,S.update([{dictionaryKey:e.dictionary.key,status:`error`}])}},5),S.finish();for(let e of x){let{icon:t,color:n}=p(e.status);f(` - ${(0,i.colorizeKey)(e.dictionary.key)} ${i.ANSIColors.GREY}[${n}${t} ${e.status}${i.ANSIColors.GREY}]${i.ANSIColors.RESET}`)}for(let e of x)e.errorMessage&&f(e.errorMessage,{level:`error`});let w=e?.deleteLocaleDictionary,T=e?.keepLocaleDictionary;if(w&&T)throw Error(`Cannot specify both --deleteLocaleDictionary and --keepLocaleDictionary options.`);if(w)await h(C,e);else if(!T){let t=C.filter(e=>e.location===`remote`),n=t.map(e=>e.key);if(t.length>0){let{confirm:r,isCancel:a}=await import(`@clack/prompts`),o=await r({message:`Do you want to delete the local dictionaries that were successfully pushed? ${(0,i.colorize)(`(Dictionaries:`,i.ANSIColors.GREY,i.ANSIColors.RESET)} ${(0,i.colorizeKey)(n)}${(0,i.colorize)(`)`,i.ANSIColors.GREY,i.ANSIColors.RESET)}`,initialValue:!1});if(a(o))return;o&&await h(t,e)}}}catch(e){f(e,{level:`error`})}},h=async(e,t)=>{let n=(0,i.getAppLogger)((0,s.getConfiguration)(t?.configOptions)),r=new Set;for(let t of e){let{filePath:e}=t;if(!e){n(`Dictionary ${(0,i.colorizeKey)(t.key)} does not have a file path`,{level:`error`});continue}r.add(e)}for(let e of r)try{let t=await l.lstat(e);t.isFile()?(await l.unlink(e),n(`Deleted file ${(0,o.formatPath)(e)}`,{})):t.isDirectory()?n(`Path is a directory ${(0,o.formatPath)(e)}, skipping.`,{}):n(`Unknown file type for ${(0,o.formatPath)(e)}, skipping.`,{})}catch(t){n(`Error deleting ${(0,o.formatPath)(e)}: ${t}`,{level:`error`})}};exports.push=m;
|
|
2
2
|
//# sourceMappingURL=push.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push.cjs","names":["ANSIColors","checkCMSAuth","PushLogger","fsPromises"],"sources":["../../../src/push/push.ts"],"sourcesContent":["import * as fsPromises from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n prepareIntlayer,\n writeContentDeclaration,\n} from '@intlayer/chokidar/build';\nimport { type ListGitFilesOptions, listGitFiles } from '@intlayer/chokidar/cli';\nimport { formatPath, parallelize } from '@intlayer/chokidar/utils';\nimport {\n ANSIColors,\n colorize,\n colorizeKey,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport type { Dictionary } from '@intlayer/types';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\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 build?: boolean;\n};\n\ntype DictionariesStatus = {\n dictionary: Dictionary;\n status: 'pending' | 'pushing' | 'modified' | 'pushed' | 'unknown' | 'error';\n error?: Error;\n errorMessage?: string;\n};\n\n// Print per-dictionary summary similar to loadDictionaries\nconst statusIconsAndColors = {\n pushed: { icon: '✔', color: ANSIColors.GREEN },\n modified: { icon: '✔', color: ANSIColors.GREEN },\n error: { icon: '✖', color: ANSIColors.RED },\n default: { icon: '⏲', color: ANSIColors.BLUE },\n};\n\nconst getIconAndColor = (status: DictionariesStatus['status']) => {\n return (\n statusIconsAndColors[status as keyof typeof statusIconsAndColors] ??\n statusIconsAndColors.default\n );\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\n if (options?.build === true) {\n await prepareIntlayer(config, { forceRun: true });\n } else if (typeof options?.build === 'undefined') {\n await prepareIntlayer(config);\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 unmergedDictionariesRecord = getUnmergedDictionaries(config);\n const allDictionaries = Object.values(unmergedDictionariesRecord).flat();\n\n const customLocations = Array.from(\n new Set(\n allDictionaries\n .map((dictionary) => dictionary.location)\n .filter(\n (location) =>\n location && !['remote', 'local', 'hybrid'].includes(location)\n )\n )\n ) as string[];\n\n let selectedCustomLocations: string[] = [];\n\n if (customLocations.length > 0) {\n const { multiselect, confirm, isCancel } = await import('@clack/prompts');\n\n if (customLocations.length === 1) {\n const shouldPush = await confirm({\n message: `Do you want to push dictionaries with custom location ${colorize(customLocations[0], ANSIColors.BLUE, ANSIColors.RESET)}?`,\n initialValue: false,\n });\n\n if (isCancel(shouldPush)) {\n return;\n }\n\n if (shouldPush) {\n selectedCustomLocations = [customLocations[0]];\n }\n } else {\n const selected = await multiselect({\n message: 'Select custom locations to push:',\n options: customLocations.map((location) => ({\n value: location,\n label: location,\n })),\n required: false,\n });\n\n if (isCancel(selected)) {\n return;\n }\n\n selectedCustomLocations = selected as string[];\n }\n }\n\n let dictionaries: Dictionary[] = allDictionaries.filter((dictionary) => {\n const location =\n dictionary.location ?? config.dictionary?.location ?? 'local';\n\n return (\n location === 'remote' ||\n location === 'hybrid' ||\n selectedCustomLocations.includes(location)\n );\n });\n\n // Check if the dictionaries list is empty after filtering by location\n if (dictionaries.length === 0) {\n appLogger(\n `No dictionaries found to push. Only dictionaries with location ${colorize('remote', ANSIColors.BLUE, ANSIColors.RESET)}, ${colorize('hybrid', ANSIColors.BLUE, ANSIColors.RESET)} or selected custom locations are pushed.`,\n { level: 'warn' }\n );\n appLogger(\n `You can set the location in your dictionary file (e.g. ${colorize(\"{ key: 'my-key', location: 'hybrid', ... }\", ANSIColors.BLUE, ANSIColors.RESET)} or globally in your intlayer.config.ts file (e.g. ${colorize(\"{ dictionary: { location: 'hybrid' } }\", ANSIColors.BLUE, ANSIColors.RESET)}).`,\n { level: 'info' }\n );\n return;\n }\n\n const existingDictionariesKeys: string[] = Object.keys(\n unmergedDictionariesRecord\n );\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 const allDictionaries = [...updatedDictionaries, ...newDictionaries];\n\n for (const remoteDictionaryData of allDictionaries) {\n const localDictionary = unmergedDictionariesRecord[\n remoteDictionaryData.key\n ]?.find(\n (dictionary) => dictionary.localId === remoteDictionaryData.localId\n );\n\n if (!localDictionary) continue;\n\n await writeContentDeclaration(\n { ...localDictionary, id: remoteDictionaryData.id },\n config\n );\n }\n\n if (\n updatedDictionaries.some(\n (dictionary) => dictionary.key === statusObj.dictionary.key\n )\n ) {\n statusObj.status = 'modified';\n successfullyPushedDictionaries.push(statusObj.dictionary);\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'modified' },\n ]);\n } else if (\n newDictionaries.some(\n (dictionary) => dictionary.key === statusObj.dictionary.key\n )\n ) {\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 for (const dictionaryStatus of dictionariesStatuses) {\n const { icon, color } = getIconAndColor(dictionaryStatus.status);\n appLogger(\n ` - ${colorizeKey(dictionaryStatus.dictionary.key)} ${ANSIColors.GREY}[${color}${icon} ${dictionaryStatus.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 remoteDictionaries = successfullyPushedDictionaries.filter(\n (dictionary) => dictionary.location === 'remote'\n );\n const remoteDictionariesKeys = remoteDictionaries.map(\n (dictionary) => dictionary.key\n );\n\n if (remoteDictionaries.length > 0) {\n const { confirm, isCancel } = await import('@clack/prompts');\n\n const shouldDelete = await confirm({\n message: `Do you want to delete the local dictionaries that were successfully pushed? ${colorize('(Dictionaries:', ANSIColors.GREY, ANSIColors.RESET)} ${colorizeKey(remoteDictionariesKeys)}${colorize(')', ANSIColors.GREY, ANSIColors.RESET)}`,\n initialValue: false,\n });\n\n if (isCancel(shouldDelete)) {\n return;\n }\n\n if (shouldDelete) {\n await deleteLocalDictionaries(remoteDictionaries, options);\n }\n }\n }\n } catch (error) {\n appLogger(error, {\n level: 'error',\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\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(\n `Dictionary ${colorizeKey(dictionary.key)} does not have a file path`,\n {\n level: 'error',\n }\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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"}
|
|
1
|
+
{"version":3,"file":"push.cjs","names":["ANSIColors","checkCMSAuth","PushLogger","fsPromises"],"sources":["../../../src/push/push.ts"],"sourcesContent":["import * as fsPromises from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n prepareIntlayer,\n writeContentDeclaration,\n} from '@intlayer/chokidar/build';\nimport {\n type ListGitFilesOptions,\n listGitFiles,\n logConfigDetails,\n} from '@intlayer/chokidar/cli';\nimport { formatPath, parallelize } from '@intlayer/chokidar/utils';\nimport {\n ANSIColors,\n colorize,\n colorizeKey,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport type { Dictionary } from '@intlayer/types';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\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 build?: boolean;\n};\n\ntype DictionariesStatus = {\n dictionary: Dictionary;\n status: 'pending' | 'pushing' | 'modified' | 'pushed' | 'unknown' | 'error';\n error?: Error;\n errorMessage?: string;\n};\n\n// Print per-dictionary summary similar to loadDictionaries\nconst statusIconsAndColors = {\n pushed: { icon: '✔', color: ANSIColors.GREEN },\n modified: { icon: '✔', color: ANSIColors.GREEN },\n error: { icon: '✖', color: ANSIColors.RED },\n default: { icon: '⏲', color: ANSIColors.BLUE },\n};\n\nconst getIconAndColor = (status: DictionariesStatus['status']) => {\n return (\n statusIconsAndColors[status as keyof typeof statusIconsAndColors] ??\n statusIconsAndColors.default\n );\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 logConfigDetails(options?.configOptions);\n\n const appLogger = getAppLogger(config);\n\n if (options?.build === true) {\n await prepareIntlayer(config, { forceRun: true });\n } else if (typeof options?.build === 'undefined') {\n await prepareIntlayer(config);\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 unmergedDictionariesRecord = getUnmergedDictionaries(config);\n const allDictionaries = Object.values(unmergedDictionariesRecord).flat();\n\n const customLocations = Array.from(\n new Set(\n allDictionaries\n .map((dictionary) => dictionary.location)\n .filter(\n (location) =>\n location && !['remote', 'local', 'hybrid'].includes(location)\n )\n )\n ) as string[];\n\n let selectedCustomLocations: string[] = [];\n\n if (customLocations.length > 0) {\n const { multiselect, confirm, isCancel } = await import('@clack/prompts');\n\n if (customLocations.length === 1) {\n const shouldPush = await confirm({\n message: `Do you want to push dictionaries with custom location ${colorize(customLocations[0], ANSIColors.BLUE, ANSIColors.RESET)}?`,\n initialValue: false,\n });\n\n if (isCancel(shouldPush)) {\n return;\n }\n\n if (shouldPush) {\n selectedCustomLocations = [customLocations[0]];\n }\n } else {\n const selected = await multiselect({\n message: 'Select custom locations to push:',\n options: customLocations.map((location) => ({\n value: location,\n label: location,\n })),\n required: false,\n });\n\n if (isCancel(selected)) {\n return;\n }\n\n selectedCustomLocations = selected as string[];\n }\n }\n\n let dictionaries: Dictionary[] = allDictionaries.filter((dictionary) => {\n const location =\n dictionary.location ?? config.dictionary?.location ?? 'local';\n\n return (\n location === 'remote' ||\n location === 'hybrid' ||\n selectedCustomLocations.includes(location)\n );\n });\n\n // Check if the dictionaries list is empty after filtering by location\n if (dictionaries.length === 0) {\n appLogger(\n `No dictionaries found to push. Only dictionaries with location ${colorize('remote', ANSIColors.BLUE, ANSIColors.RESET)}, ${colorize('hybrid', ANSIColors.BLUE, ANSIColors.RESET)} or selected custom locations are pushed.`,\n { level: 'warn' }\n );\n appLogger(\n `You can set the location in your dictionary file (e.g. ${colorize(\"{ key: 'my-key', location: 'hybrid', ... }\", ANSIColors.BLUE, ANSIColors.RESET)} or globally in your intlayer.config.ts file (e.g. ${colorize(\"{ dictionary: { location: 'hybrid' } }\", ANSIColors.BLUE, ANSIColors.RESET)}).`,\n { level: 'info' }\n );\n return;\n }\n\n const existingDictionariesKeys: string[] = Object.keys(\n unmergedDictionariesRecord\n );\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 const allDictionaries = [...updatedDictionaries, ...newDictionaries];\n\n for (const remoteDictionaryData of allDictionaries) {\n const localDictionary = unmergedDictionariesRecord[\n remoteDictionaryData.key\n ]?.find(\n (dictionary) => dictionary.localId === remoteDictionaryData.localId\n );\n\n if (!localDictionary) continue;\n\n await writeContentDeclaration(\n { ...localDictionary, id: remoteDictionaryData.id },\n config\n );\n }\n\n if (\n updatedDictionaries.some(\n (dictionary) => dictionary.key === statusObj.dictionary.key\n )\n ) {\n statusObj.status = 'modified';\n successfullyPushedDictionaries.push(statusObj.dictionary);\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'modified' },\n ]);\n } else if (\n newDictionaries.some(\n (dictionary) => dictionary.key === statusObj.dictionary.key\n )\n ) {\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 for (const dictionaryStatus of dictionariesStatuses) {\n const { icon, color } = getIconAndColor(dictionaryStatus.status);\n appLogger(\n ` - ${colorizeKey(dictionaryStatus.dictionary.key)} ${ANSIColors.GREY}[${color}${icon} ${dictionaryStatus.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 remoteDictionaries = successfullyPushedDictionaries.filter(\n (dictionary) => dictionary.location === 'remote'\n );\n const remoteDictionariesKeys = remoteDictionaries.map(\n (dictionary) => dictionary.key\n );\n\n if (remoteDictionaries.length > 0) {\n const { confirm, isCancel } = await import('@clack/prompts');\n\n const shouldDelete = await confirm({\n message: `Do you want to delete the local dictionaries that were successfully pushed? ${colorize('(Dictionaries:', ANSIColors.GREY, ANSIColors.RESET)} ${colorizeKey(remoteDictionariesKeys)}${colorize(')', ANSIColors.GREY, ANSIColors.RESET)}`,\n initialValue: false,\n });\n\n if (isCancel(shouldDelete)) {\n return;\n }\n\n if (shouldDelete) {\n await deleteLocalDictionaries(remoteDictionaries, options);\n }\n }\n }\n } catch (error) {\n appLogger(error, {\n level: 'error',\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\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(\n `Dictionary ${colorizeKey(dictionary.key)} does not have a file path`,\n {\n level: 'error',\n }\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":"4gBA6CA,MAAM,EAAuB,CAC3B,OAAQ,CAAE,KAAM,IAAK,MAAOA,EAAAA,WAAW,MAAO,CAC9C,SAAU,CAAE,KAAM,IAAK,MAAOA,EAAAA,WAAW,MAAO,CAChD,MAAO,CAAE,KAAM,IAAK,MAAOA,EAAAA,WAAW,IAAK,CAC3C,QAAS,CAAE,KAAM,IAAK,MAAOA,EAAAA,WAAW,KAAM,CAC/C,CAEK,EAAmB,GAErB,EAAqB,IACrB,EAAqB,QAOZ,EAAO,KAAO,IAAyC,CAClE,IAAM,GAAA,EAAA,EAAA,kBAA0B,GAAS,cAAc,EACvD,EAAA,EAAA,kBAAiB,GAAS,cAAc,CAExC,IAAM,GAAA,EAAA,EAAA,cAAyB,EAAO,CAElC,GAAS,QAAU,GACrB,MAAA,EAAA,EAAA,iBAAsB,EAAQ,CAAE,SAAU,GAAM,CAAC,CACjC,GAAS,QAAU,QACnC,MAAA,EAAA,EAAA,iBAAsB,EAAO,CAG/B,GAAI,CAGF,GAAI,CAFe,MAAMC,EAAAA,aAAa,EAAO,CAE5B,OAEjB,IAAM,GAAA,EAAA,EAAA,qBAAkC,IAAA,GAAW,EAAO,CAEpD,GAAA,EAAA,EAAA,yBAAqD,EAAO,CAC5D,EAAkB,OAAO,OAAO,EAA2B,CAAC,MAAM,CAElE,EAAkB,MAAM,KAC5B,IAAI,IACF,EACG,IAAK,GAAe,EAAW,SAAS,CACxC,OACE,GACC,GAAY,CAAC,CAAC,SAAU,QAAS,SAAS,CAAC,SAAS,EAAS,CAChE,CACJ,CACF,CAEG,EAAoC,EAAE,CAE1C,GAAI,EAAgB,OAAS,EAAG,CAC9B,GAAM,CAAE,cAAa,UAAS,YAAa,MAAM,OAAO,kBAExD,GAAI,EAAgB,SAAW,EAAG,CAChC,IAAM,EAAa,MAAM,EAAQ,CAC/B,QAAS,0DAAA,EAAA,EAAA,UAAkE,EAAgB,GAAID,EAAAA,WAAW,KAAMA,EAAAA,WAAW,MAAM,CAAC,GAClI,aAAc,GACf,CAAC,CAEF,GAAI,EAAS,EAAW,CACtB,OAGE,IACF,EAA0B,CAAC,EAAgB,GAAG,MAE3C,CACL,IAAM,EAAW,MAAM,EAAY,CACjC,QAAS,mCACT,QAAS,EAAgB,IAAK,IAAc,CAC1C,MAAO,EACP,MAAO,EACR,EAAE,CACH,SAAU,GACX,CAAC,CAEF,GAAI,EAAS,EAAS,CACpB,OAGF,EAA0B,GAI9B,IAAI,EAA6B,EAAgB,OAAQ,GAAe,CACtE,IAAM,EACJ,EAAW,UAAY,EAAO,YAAY,UAAY,QAExD,OACE,IAAa,UACb,IAAa,UACb,EAAwB,SAAS,EAAS,EAE5C,CAGF,GAAI,EAAa,SAAW,EAAG,CAC7B,EACE,mEAAA,EAAA,EAAA,UAA2E,SAAUA,EAAAA,WAAW,KAAMA,EAAAA,WAAW,MAAM,CAAC,KAAA,EAAA,EAAA,UAAa,SAAUA,EAAAA,WAAW,KAAMA,EAAAA,WAAW,MAAM,CAAC,2CAClL,CAAE,MAAO,OAAQ,CAClB,CACD,EACE,2DAAA,EAAA,EAAA,UAAmE,6CAA8CA,EAAAA,WAAW,KAAMA,EAAAA,WAAW,MAAM,CAAC,sDAAA,EAAA,EAAA,UAA8D,yCAA0CA,EAAAA,WAAW,KAAMA,EAAAA,WAAW,MAAM,CAAC,IAC/R,CAAE,MAAO,OAAQ,CAClB,CACD,OAGF,IAAM,EAAqC,OAAO,KAChD,EACD,CAED,GAAI,GAAS,aAAc,CAEzB,IAAM,EAAiC,EAAQ,aAAa,OACzD,GAAiB,CAAC,EAAyB,SAAS,EAAa,CACnE,CAEG,EAA+B,OAAS,GAC1C,EACE,4CAA4C,EAA+B,KACzE,KACD,CAAC,yBACF,CACE,MAAO,QACR,CACF,CAIH,EAAe,EAAa,OAAQ,GAClC,EAAQ,cAAc,SAAS,EAAW,IAAI,CAC/C,CAGH,GAAI,GAAS,WAAY,CACvB,IAAM,EAAW,MAAA,EAAA,EAAA,cAAmB,EAAQ,WAAW,CAEvD,EAAe,EAAa,OAAQ,GAClC,EAAS,UAAA,EAAA,EAAA,MACF,EAAO,QAAQ,QAAS,EAAW,UAAY,GAAG,CACxD,CACF,CAIH,GAAI,EAAa,SAAW,EAAG,CAC7B,EAAU,8BAA+B,CACvC,MAAO,QACR,CAAC,CACF,OAGF,EAAU,wBAAwB,CAGlC,IAAM,EAA6C,EAAa,IAC7D,IAAgB,CACf,aACA,OAAQ,UACT,EACF,CAGK,EAAS,IAAIE,EAAAA,WACnB,EAAO,OACL,EAAqB,IAAiB,IAAO,CAC3C,cAAe,EAAE,WAAW,IAC5B,OAAQ,UACT,EAAE,CACJ,CAED,IAAM,EAA+C,EAAE,CAqEvD,MAAA,EAAA,EAAA,aAAkB,EAnEQ,KACxB,IACkB,CAClB,EAAU,OAAS,UACnB,EAAO,OAAO,CACZ,CAAE,cAAe,EAAU,WAAW,IAAK,OAAQ,UAAW,CAC/D,CAAC,CAEF,GAAI,CACF,IAAM,EAAa,MAAM,EAAY,WAAW,iBAAiB,CAC/D,EAAU,WACX,CAAC,CAEI,EAAsB,EAAW,MAAM,qBAAuB,EAAE,CAChE,EAAkB,EAAW,MAAM,iBAAmB,EAAE,CAExD,EAAkB,CAAC,GAAG,EAAqB,GAAG,EAAgB,CAEpE,IAAK,IAAM,KAAwB,EAAiB,CAClD,IAAM,EAAkB,EACtB,EAAqB,MACpB,KACA,GAAe,EAAW,UAAY,EAAqB,QAC7D,CAEI,GAEL,MAAA,EAAA,EAAA,yBACE,CAAE,GAAG,EAAiB,GAAI,EAAqB,GAAI,CACnD,EACD,CAID,EAAoB,KACjB,GAAe,EAAW,MAAQ,EAAU,WAAW,IACzD,EAED,EAAU,OAAS,WACnB,EAA+B,KAAK,EAAU,WAAW,CACzD,EAAO,OAAO,CACZ,CAAE,cAAe,EAAU,WAAW,IAAK,OAAQ,WAAY,CAChE,CAAC,EAEF,EAAgB,KACb,GAAe,EAAW,MAAQ,EAAU,WAAW,IACzD,EAED,EAAU,OAAS,SACnB,EAA+B,KAAK,EAAU,WAAW,CACzD,EAAO,OAAO,CACZ,CAAE,cAAe,EAAU,WAAW,IAAK,OAAQ,SAAU,CAC9D,CAAC,EAEF,EAAU,OAAS,gBAEd,EAAO,CACd,EAAU,OAAS,QACnB,EAAU,MAAQ,EAClB,EAAU,aAAe,4BAA4B,EAAU,WAAW,IAAI,IAAI,IAClF,EAAO,OAAO,CACZ,CAAE,cAAe,EAAU,WAAW,IAAK,OAAQ,QAAS,CAC7D,CAAC,GAKqD,EAAE,CAG7D,EAAO,QAAQ,CAEf,IAAK,IAAM,KAAoB,EAAsB,CACnD,GAAM,CAAE,OAAM,SAAU,EAAgB,EAAiB,OAAO,CAChE,EACE,OAAA,EAAA,EAAA,aAAkB,EAAiB,WAAW,IAAI,CAAC,GAAGF,EAAAA,WAAW,KAAK,GAAG,IAAQ,EAAK,GAAG,EAAiB,SAASA,EAAAA,WAAW,KAAK,GAAGA,EAAAA,WAAW,QAClJ,CAIH,IAAK,IAAM,KAAa,EAClB,EAAU,cACZ,EAAU,EAAU,aAAc,CAChC,MAAO,QACR,CAAC,CAKN,IAAM,EAAe,GAAS,uBACxB,EAAa,GAAS,qBAE5B,GAAI,GAAgB,EAClB,MAAU,MACR,mFACD,CAGH,GAAI,EAEF,MAAM,EAAwB,EAAgC,EAAQ,SAC7D,GAEJ,CAEL,IAAM,EAAqB,EAA+B,OACvD,GAAe,EAAW,WAAa,SACzC,CACK,EAAyB,EAAmB,IAC/C,GAAe,EAAW,IAC5B,CAED,GAAI,EAAmB,OAAS,EAAG,CACjC,GAAM,CAAE,UAAS,YAAa,MAAM,OAAO,kBAErC,EAAe,MAAM,EAAQ,CACjC,QAAS,gFAAA,EAAA,EAAA,UAAwF,iBAAkBA,EAAAA,WAAW,KAAMA,EAAAA,WAAW,MAAM,CAAC,IAAA,EAAA,EAAA,aAAe,EAAuB,IAAA,EAAA,EAAA,UAAY,IAAKA,EAAAA,WAAW,KAAMA,EAAAA,WAAW,MAAM,GAC/O,aAAc,GACf,CAAC,CAEF,GAAI,EAAS,EAAa,CACxB,OAGE,GACF,MAAM,EAAwB,EAAoB,EAAQ,SAIzD,EAAO,CACd,EAAU,EAAO,CACf,MAAO,QACR,CAAC,GAIA,EAA0B,MAC9B,EACA,IACkB,CAElB,IAAM,GAAA,EAAA,EAAA,eAAA,EAAA,EAAA,kBAD0B,GAAS,cAAc,CACjB,CAGhC,EAA4B,IAAI,IAEtC,IAAK,IAAM,KAAc,EAAsB,CAC7C,GAAM,CAAE,YAAa,EAErB,GAAI,CAAC,EAAU,CACb,EACE,eAAA,EAAA,EAAA,aAA0B,EAAW,IAAI,CAAC,4BAC1C,CACE,MAAO,QACR,CACF,CACD,SAGF,EAAa,IAAI,EAAS,CAG5B,IAAK,IAAM,KAAY,EACrB,GAAI,CACF,IAAM,EAAQ,MAAMG,EAAW,MAAM,EAAS,CAE1C,EAAM,QAAQ,EAChB,MAAMA,EAAW,OAAO,EAAS,CACjC,EAAU,iBAAA,EAAA,EAAA,YAA2B,EAAS,GAAI,EAAE,CAAC,EAC5C,EAAM,aAAa,CAC5B,EAAU,wBAAA,EAAA,EAAA,YAAkC,EAAS,CAAC,aAAc,EAAE,CAAC,CAEvE,EACE,0BAAA,EAAA,EAAA,YAAoC,EAAS,CAAC,aAC9C,EAAE,CACH,OAEI,EAAK,CACZ,EAAU,mBAAA,EAAA,EAAA,YAA6B,EAAS,CAAC,IAAI,IAAO,CAC1D,MAAO,QACR,CAAC"}
|
package/dist/cjs/pushConfig.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./_virtual/_rolldown/runtime.cjs`);const e=require(`./utils/checkAccess.cjs`);let t=require(`@intlayer/api`),n=require(`@intlayer/config/
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./_virtual/_rolldown/runtime.cjs`);const e=require(`./utils/checkAccess.cjs`);let t=require(`@intlayer/api`),n=require(`@intlayer/config/logger`),r=require(`@intlayer/chokidar/cli`),i=require(`@intlayer/config/node`);const a=async a=>{let o=(0,i.getConfiguration)(a?.configOptions);(0,r.logConfigDetails)(a?.configOptions);let s=(0,n.getAppLogger)(o);if(!await e.checkCMSAuth(o,!1))return;let c=await(0,t.getIntlayerAPIProxy)(void 0,o).project.pushProjectConfiguration(o);if(!c.data)throw Error(`Error pushing project configuration`);s(`Project configuration pushed successfully`),s(JSON.stringify(c.data,null,2))};exports.pushConfig=a;
|
|
2
2
|
//# sourceMappingURL=pushConfig.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pushConfig.cjs","names":["checkCMSAuth"],"sources":["../../src/pushConfig.ts"],"sourcesContent":["import { getIntlayerAPIProxy } from '@intlayer/api';\nimport { getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { checkCMSAuth } from './utils/checkAccess';\n\ntype PushOptions = {\n configOptions?: GetConfigurationOptions;\n};\n\nexport const pushConfig = async (options?: PushOptions) => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config);\n\n const hasCMSAuth = await checkCMSAuth(config, false);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, config);\n\n // Push the project configuration\n const getDictionariesKeysResult =\n await intlayerAPI.project.pushProjectConfiguration(config);\n\n if (!getDictionariesKeysResult.data) {\n throw new Error('Error pushing project configuration');\n }\n\n appLogger('Project configuration pushed successfully');\n\n appLogger(JSON.stringify(getDictionariesKeysResult.data, null, 2));\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"pushConfig.cjs","names":["checkCMSAuth"],"sources":["../../src/pushConfig.ts"],"sourcesContent":["import { getIntlayerAPIProxy } from '@intlayer/api';\nimport { logConfigDetails } from '@intlayer/chokidar/cli';\nimport { getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { checkCMSAuth } from './utils/checkAccess';\n\ntype PushOptions = {\n configOptions?: GetConfigurationOptions;\n};\n\nexport const pushConfig = async (options?: PushOptions) => {\n const config = getConfiguration(options?.configOptions);\n logConfigDetails(options?.configOptions);\n\n const appLogger = getAppLogger(config);\n\n const hasCMSAuth = await checkCMSAuth(config, false);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, config);\n\n // Push the project configuration\n const getDictionariesKeysResult =\n await intlayerAPI.project.pushProjectConfiguration(config);\n\n if (!getDictionariesKeysResult.data) {\n throw new Error('Error pushing project configuration');\n }\n\n appLogger('Project configuration pushed successfully');\n\n appLogger(JSON.stringify(getDictionariesKeysResult.data, null, 2));\n};\n"],"mappings":"qSAaA,MAAa,EAAa,KAAO,IAA0B,CACzD,IAAM,GAAA,EAAA,EAAA,kBAA0B,GAAS,cAAc,EACvD,EAAA,EAAA,kBAAiB,GAAS,cAAc,CAExC,IAAM,GAAA,EAAA,EAAA,cAAyB,EAAO,CAItC,GAAI,CAFe,MAAMA,EAAAA,aAAa,EAAQ,GAAM,CAEnC,OAKjB,IAAM,EACJ,MAAA,EAAA,EAAA,qBAJsC,IAAA,GAAW,EAAO,CAItC,QAAQ,yBAAyB,EAAO,CAE5D,GAAI,CAAC,EAA0B,KAC7B,MAAU,MAAM,sCAAsC,CAGxD,EAAU,4CAA4C,CAEtD,EAAU,KAAK,UAAU,EAA0B,KAAM,KAAM,EAAE,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`../utils/setupAI.cjs`),n=require(`../utils/checkFileModifiedRange.cjs`),r=require(`../utils/getOutputFilePath.cjs`),i=require(`./reviewDocBlockAware.cjs`);let a=require(`@intlayer/
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`../utils/setupAI.cjs`),n=require(`../utils/checkFileModifiedRange.cjs`),r=require(`../utils/getOutputFilePath.cjs`),i=require(`./reviewDocBlockAware.cjs`);let a=require(`@intlayer/config/logger`),o=require(`@intlayer/chokidar/cli`),s=require(`@intlayer/chokidar/utils`),c=require(`@intlayer/config/node`),l=require(`node:path`),u=require(`node:fs`),d=require(`fast-glob`);d=e.__toESM(d);const f=async({docPattern:e,locales:f,excludedGlobPattern:p,baseLocale:m,aiOptions:h,nbSimultaneousFileProcessed:g,configOptions:_,customInstructions:v,skipIfModifiedBefore:y,skipIfModifiedAfter:b,skipIfExists:x,gitOptions:S})=>{let C=(0,c.getConfiguration)(_);(0,o.logConfigDetails)(_);let w=(0,a.getAppLogger)(C),T=await t.setupAI(C,h);if(!T?.hasAIAccess)return;let{aiClient:E,aiConfig:D}=T;g&&g>10&&(w(`Warning: nbSimultaneousFileProcessed is set to ${g}, which is greater than 10. Setting it to 10.`),g=10);let O=await(0,d.default)(e,{ignore:p});if(S){let e=await(0,o.listGitFiles)(S);e&&(O=O.filter(t=>e.some(e=>(0,l.join)(process.cwd(),t)===e)))}w(`Base locale is ${(0,s.formatLocale)(m)}`),w(`Reviewing ${(0,a.colorizeNumber)(f.length)} locales: [ ${(0,s.formatLocale)(f)} ]`),w(`Reviewing ${(0,a.colorizeNumber)(O.length)} files:`),w(O.map(e=>` - ${(0,s.formatPath)(e)}\n`)),await(0,s.parallelize)(O.flatMap(e=>f.map(t=>async()=>{w(`Reviewing file: ${(0,s.formatPath)(e)} to ${(0,s.formatLocale)(t)}`);let c=(0,l.join)(C.content.baseDir,e),d=r.getOutputFilePath(c,t,m);if(x&&(0,u.existsSync)(d)){let e=(0,l.relative)(C.content.baseDir,d);w(`${(0,a.colorize)(`⊘`,a.ANSIColors.YELLOW)} File ${(0,s.formatPath)(e)} already exists, skipping.`);return}if((0,u.existsSync)(d)){let e=n.checkFileModifiedRange(d,{skipIfModifiedBefore:y,skipIfModifiedAfter:b});if(e.isSkipped){w(e.message);return}}else (y||b)&&w(`${(0,a.colorize)(`!`,a.ANSIColors.YELLOW)} File ${(0,s.formatPath)(d)} does not exist, skipping modification date check.`);let f;if(S){let e=await(0,o.listGitLines)(c,S);w(`Git changed lines: ${e.join(`, `)}`),f=e}await i.reviewFileBlockAware(c,d,t,m,h,_,v,f,E,D)})),e=>e(),g??3)};exports.reviewDoc=f;
|
|
2
2
|
//# sourceMappingURL=reviewDoc.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reviewDoc.cjs","names":["setupAI","getOutputFilePath","ANSIColors","checkFileModifiedRange","reviewFileBlockAware"],"sources":["../../../src/reviewDoc/reviewDoc.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport type { AIOptions } from '@intlayer/api';\nimport {\n type ListGitFilesOptions,\n listGitFiles,\n listGitLines,\n} from '@intlayer/chokidar/cli';\nimport {\n formatLocale,\n formatPath,\n parallelize,\n} from '@intlayer/chokidar/utils';\nimport {\n ANSIColors,\n colorize,\n colorizeNumber,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport type { Locale } from '@intlayer/types';\nimport fg from 'fast-glob';\nimport { checkFileModifiedRange } from '../utils/checkFileModifiedRange';\nimport { getOutputFilePath } from '../utils/getOutputFilePath';\nimport { setupAI } from '../utils/setupAI';\nimport { reviewFileBlockAware } from './reviewDocBlockAware';\n\ntype ReviewDocOptions = {\n docPattern: string[];\n locales: Locale[];\n excludedGlobPattern: string[];\n baseLocale: Locale;\n aiOptions?: AIOptions;\n nbSimultaneousFileProcessed?: number;\n configOptions?: GetConfigurationOptions;\n customInstructions?: string;\n skipIfModifiedBefore?: number | string | Date;\n skipIfModifiedAfter?: number | string | Date;\n skipIfExists?: boolean;\n gitOptions?: ListGitFilesOptions;\n};\n\n/**\n * Main audit function: scans all .md files in \"en/\" (unless you specified DOC_LIST),\n * then audits them to each locale in LOCALE_LIST.\n */\nexport const reviewDoc = async ({\n docPattern,\n locales,\n excludedGlobPattern,\n baseLocale,\n aiOptions,\n nbSimultaneousFileProcessed,\n configOptions,\n customInstructions,\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n skipIfExists,\n gitOptions,\n}: ReviewDocOptions) => {\n const configuration = getConfiguration(configOptions);\n const appLogger = getAppLogger(configuration);\n\n const aiResult = await setupAI(configuration, aiOptions);\n\n if (!aiResult?.hasAIAccess) return;\n\n const { aiClient, aiConfig } = aiResult;\n\n if (nbSimultaneousFileProcessed && nbSimultaneousFileProcessed > 10) {\n appLogger(\n `Warning: nbSimultaneousFileProcessed is set to ${nbSimultaneousFileProcessed}, which is greater than 10. Setting it to 10.`\n );\n nbSimultaneousFileProcessed = 10; // Limit the number of simultaneous file processed to 10\n }\n\n let docList: string[] = await fg(docPattern, {\n ignore: excludedGlobPattern,\n });\n\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 docList = docList.filter((path) =>\n gitChangedFiles.some((gitFile) => join(process.cwd(), path) === gitFile)\n );\n }\n }\n\n // OAuth handled by API proxy internally\n\n appLogger(`Base locale is ${formatLocale(baseLocale)}`);\n appLogger(\n `Reviewing ${colorizeNumber(locales.length)} locales: [ ${formatLocale(locales)} ]`\n );\n\n appLogger(`Reviewing ${colorizeNumber(docList.length)} files:`);\n appLogger(docList.map((path) => ` - ${formatPath(path)}\\n`));\n\n // Create all tasks to be processed\n const allTasks = docList.flatMap((docPath) =>\n locales.map((locale) => async () => {\n appLogger(\n `Reviewing file: ${formatPath(docPath)} to ${formatLocale(locale)}`\n );\n\n const absoluteBaseFilePath = join(configuration.content.baseDir, docPath);\n const outputFilePath = getOutputFilePath(\n absoluteBaseFilePath,\n locale,\n baseLocale\n );\n\n // Skip if file exists and skipIfExists option is enabled\n if (skipIfExists && existsSync(outputFilePath)) {\n const relativePath = relative(\n configuration.content.baseDir,\n outputFilePath\n );\n appLogger(\n `${colorize('⊘', ANSIColors.YELLOW)} File ${formatPath(relativePath)} already exists, skipping.`\n );\n return;\n }\n\n // Check modification range only if the file exists\n if (existsSync(outputFilePath)) {\n const fileModificationData = checkFileModifiedRange(outputFilePath, {\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n });\n\n if (fileModificationData.isSkipped) {\n appLogger(fileModificationData.message);\n return;\n }\n } else if (skipIfModifiedBefore || skipIfModifiedAfter) {\n // Log if we intended to check modification time but couldn't because the file doesn't exist\n appLogger(\n `${colorize('!', ANSIColors.YELLOW)} File ${formatPath(outputFilePath)} does not exist, skipping modification date check.`\n );\n }\n\n let changedLines: number[] | undefined;\n // FIXED: Enable git optimization that was previously commented out\n if (gitOptions) {\n const gitChangedLines = await listGitLines(\n absoluteBaseFilePath,\n gitOptions\n );\n\n appLogger(`Git changed lines: ${gitChangedLines.join(', ')}`);\n changedLines = gitChangedLines;\n }\n\n await reviewFileBlockAware(\n absoluteBaseFilePath,\n outputFilePath,\n locale as Locale,\n baseLocale,\n aiOptions,\n configOptions,\n customInstructions,\n changedLines,\n aiClient,\n aiConfig\n );\n })\n );\n\n await parallelize(\n allTasks,\n (task) => task(),\n nbSimultaneousFileProcessed ?? 3\n );\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"reviewDoc.cjs","names":["setupAI","getOutputFilePath","ANSIColors","checkFileModifiedRange","reviewFileBlockAware"],"sources":["../../../src/reviewDoc/reviewDoc.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport type { AIOptions } from '@intlayer/api';\nimport {\n type ListGitFilesOptions,\n listGitFiles,\n listGitLines,\n logConfigDetails,\n} from '@intlayer/chokidar/cli';\nimport {\n formatLocale,\n formatPath,\n parallelize,\n} from '@intlayer/chokidar/utils';\nimport {\n ANSIColors,\n colorize,\n colorizeNumber,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport type { Locale } from '@intlayer/types';\nimport fg from 'fast-glob';\nimport { checkFileModifiedRange } from '../utils/checkFileModifiedRange';\nimport { getOutputFilePath } from '../utils/getOutputFilePath';\nimport { setupAI } from '../utils/setupAI';\nimport { reviewFileBlockAware } from './reviewDocBlockAware';\n\ntype ReviewDocOptions = {\n docPattern: string[];\n locales: Locale[];\n excludedGlobPattern: string[];\n baseLocale: Locale;\n aiOptions?: AIOptions;\n nbSimultaneousFileProcessed?: number;\n configOptions?: GetConfigurationOptions;\n customInstructions?: string;\n skipIfModifiedBefore?: number | string | Date;\n skipIfModifiedAfter?: number | string | Date;\n skipIfExists?: boolean;\n gitOptions?: ListGitFilesOptions;\n};\n\n/**\n * Main audit function: scans all .md files in \"en/\" (unless you specified DOC_LIST),\n * then audits them to each locale in LOCALE_LIST.\n */\nexport const reviewDoc = async ({\n docPattern,\n locales,\n excludedGlobPattern,\n baseLocale,\n aiOptions,\n nbSimultaneousFileProcessed,\n configOptions,\n customInstructions,\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n skipIfExists,\n gitOptions,\n}: ReviewDocOptions) => {\n const configuration = getConfiguration(configOptions);\n logConfigDetails(configOptions);\n\n const appLogger = getAppLogger(configuration);\n\n const aiResult = await setupAI(configuration, aiOptions);\n\n if (!aiResult?.hasAIAccess) return;\n\n const { aiClient, aiConfig } = aiResult;\n\n if (nbSimultaneousFileProcessed && nbSimultaneousFileProcessed > 10) {\n appLogger(\n `Warning: nbSimultaneousFileProcessed is set to ${nbSimultaneousFileProcessed}, which is greater than 10. Setting it to 10.`\n );\n nbSimultaneousFileProcessed = 10; // Limit the number of simultaneous file processed to 10\n }\n\n let docList: string[] = await fg(docPattern, {\n ignore: excludedGlobPattern,\n });\n\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 docList = docList.filter((path) =>\n gitChangedFiles.some((gitFile) => join(process.cwd(), path) === gitFile)\n );\n }\n }\n\n // OAuth handled by API proxy internally\n\n appLogger(`Base locale is ${formatLocale(baseLocale)}`);\n appLogger(\n `Reviewing ${colorizeNumber(locales.length)} locales: [ ${formatLocale(locales)} ]`\n );\n\n appLogger(`Reviewing ${colorizeNumber(docList.length)} files:`);\n appLogger(docList.map((path) => ` - ${formatPath(path)}\\n`));\n\n // Create all tasks to be processed\n const allTasks = docList.flatMap((docPath) =>\n locales.map((locale) => async () => {\n appLogger(\n `Reviewing file: ${formatPath(docPath)} to ${formatLocale(locale)}`\n );\n\n const absoluteBaseFilePath = join(configuration.content.baseDir, docPath);\n const outputFilePath = getOutputFilePath(\n absoluteBaseFilePath,\n locale,\n baseLocale\n );\n\n // Skip if file exists and skipIfExists option is enabled\n if (skipIfExists && existsSync(outputFilePath)) {\n const relativePath = relative(\n configuration.content.baseDir,\n outputFilePath\n );\n appLogger(\n `${colorize('⊘', ANSIColors.YELLOW)} File ${formatPath(relativePath)} already exists, skipping.`\n );\n return;\n }\n\n // Check modification range only if the file exists\n if (existsSync(outputFilePath)) {\n const fileModificationData = checkFileModifiedRange(outputFilePath, {\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n });\n\n if (fileModificationData.isSkipped) {\n appLogger(fileModificationData.message);\n return;\n }\n } else if (skipIfModifiedBefore || skipIfModifiedAfter) {\n // Log if we intended to check modification time but couldn't because the file doesn't exist\n appLogger(\n `${colorize('!', ANSIColors.YELLOW)} File ${formatPath(outputFilePath)} does not exist, skipping modification date check.`\n );\n }\n\n let changedLines: number[] | undefined;\n // FIXED: Enable git optimization that was previously commented out\n if (gitOptions) {\n const gitChangedLines = await listGitLines(\n absoluteBaseFilePath,\n gitOptions\n );\n\n appLogger(`Git changed lines: ${gitChangedLines.join(', ')}`);\n changedLines = gitChangedLines;\n }\n\n await reviewFileBlockAware(\n absoluteBaseFilePath,\n outputFilePath,\n locale as Locale,\n baseLocale,\n aiOptions,\n configOptions,\n customInstructions,\n changedLines,\n aiClient,\n aiConfig\n );\n })\n );\n\n await parallelize(\n allTasks,\n (task) => task(),\n nbSimultaneousFileProcessed ?? 3\n );\n};\n"],"mappings":"sgBAkDA,MAAa,EAAY,MAAO,CAC9B,aACA,UACA,sBACA,aACA,YACA,8BACA,gBACA,qBACA,uBACA,sBACA,eACA,gBACsB,CACtB,IAAM,GAAA,EAAA,EAAA,kBAAiC,EAAc,EACrD,EAAA,EAAA,kBAAiB,EAAc,CAE/B,IAAM,GAAA,EAAA,EAAA,cAAyB,EAAc,CAEvC,EAAW,MAAMA,EAAAA,QAAQ,EAAe,EAAU,CAExD,GAAI,CAAC,GAAU,YAAa,OAE5B,GAAM,CAAE,WAAU,YAAa,EAE3B,GAA+B,EAA8B,KAC/D,EACE,kDAAkD,EAA4B,+CAC/E,CACD,EAA8B,IAGhC,IAAI,EAAoB,MAAA,EAAA,EAAA,SAAS,EAAY,CAC3C,OAAQ,EACT,CAAC,CAEF,GAAI,EAAY,CACd,IAAM,EAAkB,MAAA,EAAA,EAAA,cAAmB,EAAW,CAElD,IAIF,EAAU,EAAQ,OAAQ,GACxB,EAAgB,KAAM,IAAA,EAAA,EAAA,MAAiB,QAAQ,KAAK,CAAE,EAAK,GAAK,EAAQ,CACzE,EAML,EAAU,mBAAA,EAAA,EAAA,cAA+B,EAAW,GAAG,CACvD,EACE,cAAA,EAAA,EAAA,gBAA4B,EAAQ,OAAO,CAAC,eAAA,EAAA,EAAA,cAA2B,EAAQ,CAAC,IACjF,CAED,EAAU,cAAA,EAAA,EAAA,gBAA4B,EAAQ,OAAO,CAAC,SAAS,CAC/D,EAAU,EAAQ,IAAK,GAAS,OAAA,EAAA,EAAA,YAAiB,EAAK,CAAC,IAAI,CAAC,CAyE5D,MAAA,EAAA,EAAA,aAtEiB,EAAQ,QAAS,GAChC,EAAQ,IAAK,GAAW,SAAY,CAClC,EACE,oBAAA,EAAA,EAAA,YAA8B,EAAQ,CAAC,OAAA,EAAA,EAAA,cAAmB,EAAO,GAClE,CAED,IAAM,GAAA,EAAA,EAAA,MAA4B,EAAc,QAAQ,QAAS,EAAQ,CACnE,EAAiBC,EAAAA,kBACrB,EACA,EACA,EACD,CAGD,GAAI,IAAA,EAAA,EAAA,YAA2B,EAAe,CAAE,CAC9C,IAAM,GAAA,EAAA,EAAA,UACJ,EAAc,QAAQ,QACtB,EACD,CACD,EACE,IAAA,EAAA,EAAA,UAAY,IAAKC,EAAAA,WAAW,OAAO,CAAC,SAAA,EAAA,EAAA,YAAmB,EAAa,CAAC,4BACtE,CACD,OAIF,IAAA,EAAA,EAAA,YAAe,EAAe,CAAE,CAC9B,IAAM,EAAuBC,EAAAA,uBAAuB,EAAgB,CAClE,uBACA,sBACD,CAAC,CAEF,GAAI,EAAqB,UAAW,CAClC,EAAU,EAAqB,QAAQ,CACvC,cAEO,GAAwB,IAEjC,EACE,IAAA,EAAA,EAAA,UAAY,IAAKD,EAAAA,WAAW,OAAO,CAAC,SAAA,EAAA,EAAA,YAAmB,EAAe,CAAC,oDACxE,CAGH,IAAI,EAEJ,GAAI,EAAY,CACd,IAAM,EAAkB,MAAA,EAAA,EAAA,cACtB,EACA,EACD,CAED,EAAU,sBAAsB,EAAgB,KAAK,KAAK,GAAG,CAC7D,EAAe,EAGjB,MAAME,EAAAA,qBACJ,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACD,EACD,CACH,CAIE,GAAS,GAAM,CAChB,GAA+B,EAChC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`../_virtual/_utils_asset.cjs`),t=require(`../translateDoc/validation.cjs`),n=require(`../translation-alignment/rebuildDocument.cjs`),r=require(`../translation-alignment/pipeline.cjs`),i=require(`../utils/chunkInference.cjs`),a=require(`../utils/fixChunkStartEndChars.cjs`);let o=require(`@intlayer/
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`../_virtual/_utils_asset.cjs`),t=require(`../translateDoc/validation.cjs`),n=require(`../translation-alignment/rebuildDocument.cjs`),r=require(`../translation-alignment/pipeline.cjs`),i=require(`../utils/chunkInference.cjs`),a=require(`../utils/fixChunkStartEndChars.cjs`);let o=require(`@intlayer/config/logger`),s=require(`@intlayer/chokidar/utils`),c=require(`@intlayer/config/node`),l=require(`node:path`),u=require(`@intlayer/config/utils`),d=require(`node:fs`),f=require(`node:fs/promises`),p=require(`@intlayer/core/localization`),m=require(`@intlayer/types`);const h=async(h,g,_,v,y,b,x,S,C,w)=>{let T=(0,c.getConfiguration)(b),E=(0,o.getAppLogger)(T),D=await(0,f.readFile)(h,`utf-8`),O=await(0,f.readFile)(g,`utf-8`).catch(()=>``),k=e.readAsset(`./prompts/REVIEW_PROMPT.md`,`utf-8`).replaceAll(`{{localeName}}`,`${(0,s.formatLocale)(_,!1)}`).replaceAll(`{{baseLocaleName}}`,`${(0,s.formatLocale)(v,!1)}`).replace(`{{applicationContext}}`,y?.applicationContext??`-`).replace(`{{customInstructions}}`,x??`-`),A=[(0,o.colon)(`${o.ANSIColors.GREY_DARK}[${(0,s.formatPath)(h)}${o.ANSIColors.GREY_DARK}] `,{colSize:40}),`→ ${o.ANSIColors.RESET}`].join(``),j=[(0,o.colon)(`${o.ANSIColors.GREY_DARK}[${(0,s.formatPath)(h)}${o.ANSIColors.GREY_DARK}][${(0,s.formatLocale)(_)}${o.ANSIColors.GREY_DARK}] `,{colSize:40}),`→ ${o.ANSIColors.RESET}`].join(``),{englishBlocks:M,frenchBlocks:N,plan:P,segmentsToReview:F}=r.buildAlignmentPlan({englishText:D,frenchText:O,changedLines:S});if(E(`${A}Block-aware alignment complete. Total blocks: EN=${(0,o.colorizeNumber)(M.length)}, FR=${(0,o.colorizeNumber)(N.length)}`),E(`${A}Actions: reuse=${(0,o.colorizeNumber)(P.actions.filter(e=>e.kind===`reuse`).length)}, review=${(0,o.colorizeNumber)(P.actions.filter(e=>e.kind===`review`).length)}, new=${(0,o.colorizeNumber)(P.actions.filter(e=>e.kind===`insert_new`).length)}, delete=${(0,o.colorizeNumber)(P.actions.filter(e=>e.kind===`delete`).length)}`),F.length===0){E(`${A}No segments need review, reusing existing translation`),(0,d.mkdirSync)((0,l.dirname)(g),{recursive:!0}),(0,d.writeFileSync)(g,n.mergeReviewedSegments(P,N,new Map)),E(`${(0,o.colorize)(`✔`,o.ANSIColors.GREEN)} File ${(0,s.formatPath)(g)} updated successfully (no changes needed).`);return}E(`${A}Segments to review: ${(0,o.colorizeNumber)(F.length)}`);let I=new Map;for(let e of F){let n=F.indexOf(e)+1,r=e.englishBlock,c=()=>`**BLOCK ${n} of ${F.length}** is the base block in ${(0,s.formatLocale)(v,!1)} as reference.\n///chunksStart///\n`+r.content+`///chunksEnd///`,l=()=>`**BLOCK ${n} of ${F.length}** is the current block to review in ${(0,s.formatLocale)(_,!1)}.\n///chunksStart///\n`+(e.frenchBlockText??``)+`///chunksEnd///`,d=await(0,u.retryManager)(async()=>{let e=await i.chunkInference([{role:`system`,content:k},{role:`system`,content:c()},{role:`system`,content:l()},{role:`system`,content:`The next user message will be the **BLOCK ${(0,o.colorizeNumber)(n)} of ${(0,o.colorizeNumber)(F.length)}** that should be translated in ${(0,p.getLocaleName)(_,m.Locales.ENGLISH)} (${_}).`},{role:`user`,content:r.content}],y,T,C,w);E(`${j}${(0,o.colorizeNumber)(e.tokenUsed)} tokens used - Block ${(0,o.colorizeNumber)(n)} of ${(0,o.colorizeNumber)(F.length)}`);let s=t.sanitizeChunk(e?.fileContent,r.content);if(s=a.fixChunkStartEndChars(s,r.content),!t.validateTranslation(r.content,s,E))throw Error(`Validation failed for chunk (structure or length mismatch). Retrying...`);return s})();I.set(e.actionIndex,d)}let L=n.mergeReviewedSegments(P,N,I);(0,d.mkdirSync)((0,l.dirname)(g),{recursive:!0}),(0,d.writeFileSync)(g,L),E(`${(0,o.colorize)(`✔`,o.ANSIColors.GREEN)} File ${(0,s.formatPath)(g)} created/updated successfully.`)};exports.reviewFileBlockAware=h;
|
|
2
2
|
//# sourceMappingURL=reviewDocBlockAware.cjs.map
|
package/dist/cjs/searchDoc.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./_virtual/_rolldown/runtime.cjs`);let e=require(`@intlayer/api`),t=require(`@intlayer/config/
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./_virtual/_rolldown/runtime.cjs`);let e=require(`@intlayer/api`),t=require(`@intlayer/config/logger`),n=require(`@intlayer/chokidar/cli`),r=require(`@intlayer/config/node`);const i=async({query:i,limit:a=10,configOptions:o})=>{let s=(0,r.getConfiguration)(o);(0,n.logConfigDetails)(o);let c=(0,t.getAppLogger)(s);try{let{searchDoc:n}=(0,e.getSearchAPI)(void 0,s),r=await n({input:i,limit:a.toString(),returnContent:`true`});if(!r.data||!Array.isArray(r.data)){c(`No relevant chunks found.`);return}let o=r.data;c(`Found ${(0,t.colorizeNumber)(o.length)} relevant chunks:`),o.forEach(e=>{c(`---`),c(`${(0,t.colorizeKey)(`File`)}: ${e.fileKey}`),c(`${(0,t.colorizeKey)(`Title`)}: ${e.docName}`),c(`${(0,t.colorizeKey)(`URL`)}: ${e.docUrl}`),c(`${(0,t.colorizeKey)(`Chunk`)}: ${e.chunkNumber}`),c(`${(0,t.colorizeKey)(`Content`)}:`),c(e.content)})}catch(e){c(`Search failed: ${e instanceof Error?e.message:`An unknown error occurred`}`,{level:`error`})}};exports.searchDoc=i;
|
|
2
2
|
//# sourceMappingURL=searchDoc.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"searchDoc.cjs","names":[],"sources":["../../src/searchDoc.ts"],"sourcesContent":["import { getSearchAPI } from '@intlayer/api';\nimport {\n colorizeKey,\n colorizeNumber,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\n\ninterface SearchDocOptions {\n query: string;\n limit?: number;\n configOptions?: GetConfigurationOptions;\n}\n\nexport const searchDoc = async ({\n query,\n limit = 10,\n configOptions,\n}: SearchDocOptions) => {\n const config = getConfiguration(configOptions);\n const appLogger = getAppLogger(config);\n\n try {\n const { searchDoc } = getSearchAPI(undefined, config);\n const response = await searchDoc({\n input: query,\n limit: limit.toString(),\n returnContent: 'true',\n });\n\n if (!response.data || !Array.isArray(response.data)) {\n appLogger('No relevant chunks found.');\n return;\n }\n\n const chunks = response.data;\n\n appLogger(`Found ${colorizeNumber(chunks.length)} relevant chunks:`);\n\n chunks.forEach((chunk: any) => {\n appLogger('---');\n appLogger(`${colorizeKey('File')}: ${chunk.fileKey}`);\n appLogger(`${colorizeKey('Title')}: ${chunk.docName}`);\n appLogger(`${colorizeKey('URL')}: ${chunk.docUrl}`);\n appLogger(`${colorizeKey('Chunk')}: ${chunk.chunkNumber}`);\n appLogger(`${colorizeKey('Content')}:`);\n appLogger(chunk.content);\n });\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : 'An unknown error occurred';\n appLogger(`Search failed: ${errorMessage}`, { level: 'error' });\n }\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"searchDoc.cjs","names":[],"sources":["../../src/searchDoc.ts"],"sourcesContent":["import { getSearchAPI } from '@intlayer/api';\nimport { logConfigDetails } from '@intlayer/chokidar/cli';\nimport {\n colorizeKey,\n colorizeNumber,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\n\ninterface SearchDocOptions {\n query: string;\n limit?: number;\n configOptions?: GetConfigurationOptions;\n}\n\nexport const searchDoc = async ({\n query,\n limit = 10,\n configOptions,\n}: SearchDocOptions) => {\n const config = getConfiguration(configOptions);\n logConfigDetails(configOptions);\n\n const appLogger = getAppLogger(config);\n\n try {\n const { searchDoc } = getSearchAPI(undefined, config);\n const response = await searchDoc({\n input: query,\n limit: limit.toString(),\n returnContent: 'true',\n });\n\n if (!response.data || !Array.isArray(response.data)) {\n appLogger('No relevant chunks found.');\n return;\n }\n\n const chunks = response.data;\n\n appLogger(`Found ${colorizeNumber(chunks.length)} relevant chunks:`);\n\n chunks.forEach((chunk: any) => {\n appLogger('---');\n appLogger(`${colorizeKey('File')}: ${chunk.fileKey}`);\n appLogger(`${colorizeKey('Title')}: ${chunk.docName}`);\n appLogger(`${colorizeKey('URL')}: ${chunk.docUrl}`);\n appLogger(`${colorizeKey('Chunk')}: ${chunk.chunkNumber}`);\n appLogger(`${colorizeKey('Content')}:`);\n appLogger(chunk.content);\n });\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : 'An unknown error occurred';\n appLogger(`Search failed: ${errorMessage}`, { level: 'error' });\n }\n};\n"],"mappings":"0PAkBA,MAAa,EAAY,MAAO,CAC9B,QACA,QAAQ,GACR,mBACsB,CACtB,IAAM,GAAA,EAAA,EAAA,kBAA0B,EAAc,EAC9C,EAAA,EAAA,kBAAiB,EAAc,CAE/B,IAAM,GAAA,EAAA,EAAA,cAAyB,EAAO,CAEtC,GAAI,CACF,GAAM,CAAE,cAAA,EAAA,EAAA,cAA2B,IAAA,GAAW,EAAO,CAC/C,EAAW,MAAM,EAAU,CAC/B,MAAO,EACP,MAAO,EAAM,UAAU,CACvB,cAAe,OAChB,CAAC,CAEF,GAAI,CAAC,EAAS,MAAQ,CAAC,MAAM,QAAQ,EAAS,KAAK,CAAE,CACnD,EAAU,4BAA4B,CACtC,OAGF,IAAM,EAAS,EAAS,KAExB,EAAU,UAAA,EAAA,EAAA,gBAAwB,EAAO,OAAO,CAAC,mBAAmB,CAEpE,EAAO,QAAS,GAAe,CAC7B,EAAU,MAAM,CAChB,EAAU,IAAA,EAAA,EAAA,aAAe,OAAO,CAAC,IAAI,EAAM,UAAU,CACrD,EAAU,IAAA,EAAA,EAAA,aAAe,QAAQ,CAAC,IAAI,EAAM,UAAU,CACtD,EAAU,IAAA,EAAA,EAAA,aAAe,MAAM,CAAC,IAAI,EAAM,SAAS,CACnD,EAAU,IAAA,EAAA,EAAA,aAAe,QAAQ,CAAC,IAAI,EAAM,cAAc,CAC1D,EAAU,IAAA,EAAA,EAAA,aAAe,UAAU,CAAC,GAAG,CACvC,EAAU,EAAM,QAAQ,EACxB,OACK,EAAO,CAGd,EAAU,kBADR,aAAiB,MAAQ,EAAM,QAAU,8BACC,CAAE,MAAO,QAAS,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`../_virtual/_rolldown/runtime.cjs`);let e=require(`@intlayer/config/node`),
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`../_virtual/_rolldown/runtime.cjs`);let e=require(`@intlayer/chokidar/cli`),t=require(`@intlayer/config/node`),n=require(`@intlayer/unmerged-dictionaries-entry`),r=require(`@intlayer/core/plugins`),i=require(`@intlayer/dictionaries-entry`);const a=e=>{let t=(0,n.getUnmergedDictionaries)(e),a=(0,i.getDictionaries)(e),o=[],{locales:s,requiredLocales:c}=e.internationalization,l=Object.keys(t);for(let e of l){let n=t[e],i=n.filter(e=>!e.locale);for(let t of i){let n=(0,r.getMissingLocalesContentFromDictionary)(t,s);n.length>0&&o.push({key:e,id:t.id,filePath:t.filePath,locales:n})}if(n.filter(e=>e.locale).length===0)continue;let c=a[e],l=(0,r.getMissingLocalesContentFromDictionary)(c,s);l.length>0&&o.push({key:e,locales:l})}let u=new Set(o.flatMap(e=>e.locales)),d=Array.from(u);return{missingTranslations:o,missingLocales:d,missingRequiredLocales:d.filter(e=>(c??s).includes(e))}},o=n=>{let r=(0,t.getConfiguration)(n);return(0,e.logConfigDetails)(n),a(r)};exports.listMissingTranslations=o,exports.listMissingTranslationsWithConfig=a;
|
|
2
2
|
//# sourceMappingURL=listMissingTranslations.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listMissingTranslations.cjs","names":[],"sources":["../../../src/test/listMissingTranslations.ts"],"sourcesContent":["import {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { getMissingLocalesContentFromDictionary } from '@intlayer/core/plugins';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport type { Dictionary, IntlayerConfig, Locale } from '@intlayer/types';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\n\nexport const listMissingTranslationsWithConfig = (\n configuration: IntlayerConfig\n) => {\n const unmergedDictionariesRecord = getUnmergedDictionaries(configuration);\n const mergedDictionaries = getDictionaries(configuration);\n\n const missingTranslations: {\n key: string;\n filePath?: string;\n id?: string;\n locales: Locale[];\n }[] = [];\n\n const { locales, requiredLocales } = configuration.internationalization;\n\n const dictionariesKeys = Object.keys(unmergedDictionariesRecord);\n\n for (const dictionaryKey of dictionariesKeys) {\n const dictionaries: Dictionary[] =\n unmergedDictionariesRecord[dictionaryKey];\n\n const multilingualDictionary: Dictionary[] = dictionaries.filter(\n (dictionary) => !dictionary.locale\n );\n\n // Test all by merging all dictionaries to ensure no per-locale dictionary is missing\n for (const dictionary of multilingualDictionary) {\n const missingLocales = getMissingLocalesContentFromDictionary(\n dictionary,\n locales\n );\n\n if (missingLocales.length > 0) {\n missingTranslations.push({\n key: dictionaryKey,\n id: dictionary.id,\n filePath: dictionary.filePath,\n locales: missingLocales,\n });\n }\n }\n\n const perLocaleDictionary: Dictionary[] = dictionaries.filter(\n (dictionary) => dictionary.locale\n );\n\n if (perLocaleDictionary.length === 0) {\n continue;\n }\n\n const mergedDictionary = mergedDictionaries[dictionaryKey];\n\n const missingLocales = getMissingLocalesContentFromDictionary(\n mergedDictionary,\n locales\n );\n\n if (missingLocales.length > 0) {\n missingTranslations.push({\n key: dictionaryKey,\n locales: missingLocales,\n });\n }\n }\n\n const missingLocalesSet = new Set(\n missingTranslations.flatMap((t) => t.locales)\n );\n const missingLocales = Array.from(missingLocalesSet);\n\n const missingRequiredLocales = missingLocales.filter((locale) =>\n (requiredLocales ?? locales).includes(locale)\n );\n\n return { missingTranslations, missingLocales, missingRequiredLocales };\n};\n\nexport const listMissingTranslations = (\n configurationOptions?: GetConfigurationOptions\n) => {\n const configuration = getConfiguration(configurationOptions);\n\n return listMissingTranslationsWithConfig(configuration);\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"listMissingTranslations.cjs","names":[],"sources":["../../../src/test/listMissingTranslations.ts"],"sourcesContent":["import { logConfigDetails } from '@intlayer/chokidar/cli';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { getMissingLocalesContentFromDictionary } from '@intlayer/core/plugins';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport type { Dictionary, IntlayerConfig, Locale } from '@intlayer/types';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\n\nexport const listMissingTranslationsWithConfig = (\n configuration: IntlayerConfig\n) => {\n const unmergedDictionariesRecord = getUnmergedDictionaries(configuration);\n const mergedDictionaries = getDictionaries(configuration);\n\n const missingTranslations: {\n key: string;\n filePath?: string;\n id?: string;\n locales: Locale[];\n }[] = [];\n\n const { locales, requiredLocales } = configuration.internationalization;\n\n const dictionariesKeys = Object.keys(unmergedDictionariesRecord);\n\n for (const dictionaryKey of dictionariesKeys) {\n const dictionaries: Dictionary[] =\n unmergedDictionariesRecord[dictionaryKey];\n\n const multilingualDictionary: Dictionary[] = dictionaries.filter(\n (dictionary) => !dictionary.locale\n );\n\n // Test all by merging all dictionaries to ensure no per-locale dictionary is missing\n for (const dictionary of multilingualDictionary) {\n const missingLocales = getMissingLocalesContentFromDictionary(\n dictionary,\n locales\n );\n\n if (missingLocales.length > 0) {\n missingTranslations.push({\n key: dictionaryKey,\n id: dictionary.id,\n filePath: dictionary.filePath,\n locales: missingLocales,\n });\n }\n }\n\n const perLocaleDictionary: Dictionary[] = dictionaries.filter(\n (dictionary) => dictionary.locale\n );\n\n if (perLocaleDictionary.length === 0) {\n continue;\n }\n\n const mergedDictionary = mergedDictionaries[dictionaryKey];\n\n const missingLocales = getMissingLocalesContentFromDictionary(\n mergedDictionary,\n locales\n );\n\n if (missingLocales.length > 0) {\n missingTranslations.push({\n key: dictionaryKey,\n locales: missingLocales,\n });\n }\n }\n\n const missingLocalesSet = new Set(\n missingTranslations.flatMap((t) => t.locales)\n );\n const missingLocales = Array.from(missingLocalesSet);\n\n const missingRequiredLocales = missingLocales.filter((locale) =>\n (requiredLocales ?? locales).includes(locale)\n );\n\n return { missingTranslations, missingLocales, missingRequiredLocales };\n};\n\nexport const listMissingTranslations = (\n configurationOptions?: GetConfigurationOptions\n) => {\n const configuration = getConfiguration(configurationOptions);\n logConfigDetails(configurationOptions);\n\n return listMissingTranslationsWithConfig(configuration);\n};\n"],"mappings":"4TAUA,MAAa,EACX,GACG,CACH,IAAM,GAAA,EAAA,EAAA,yBAAqD,EAAc,CACnE,GAAA,EAAA,EAAA,iBAAqC,EAAc,CAEnD,EAKA,EAAE,CAEF,CAAE,UAAS,mBAAoB,EAAc,qBAE7C,EAAmB,OAAO,KAAK,EAA2B,CAEhE,IAAK,IAAM,KAAiB,EAAkB,CAC5C,IAAM,EACJ,EAA2B,GAEvB,EAAuC,EAAa,OACvD,GAAe,CAAC,EAAW,OAC7B,CAGD,IAAK,IAAM,KAAc,EAAwB,CAC/C,IAAM,GAAA,EAAA,EAAA,wCACJ,EACA,EACD,CAEG,EAAe,OAAS,GAC1B,EAAoB,KAAK,CACvB,IAAK,EACL,GAAI,EAAW,GACf,SAAU,EAAW,SACrB,QAAS,EACV,CAAC,CAQN,GAJ0C,EAAa,OACpD,GAAe,EAAW,OAC5B,CAEuB,SAAW,EACjC,SAGF,IAAM,EAAmB,EAAmB,GAEtC,GAAA,EAAA,EAAA,wCACJ,EACA,EACD,CAEG,EAAe,OAAS,GAC1B,EAAoB,KAAK,CACvB,IAAK,EACL,QAAS,EACV,CAAC,CAIN,IAAM,EAAoB,IAAI,IAC5B,EAAoB,QAAS,GAAM,EAAE,QAAQ,CAC9C,CACK,EAAiB,MAAM,KAAK,EAAkB,CAMpD,MAAO,CAAE,sBAAqB,iBAAgB,uBAJf,EAAe,OAAQ,IACnD,GAAmB,GAAS,SAAS,EAAO,CAC9C,CAEqE,EAG3D,EACX,GACG,CACH,IAAM,GAAA,EAAA,EAAA,kBAAiC,EAAqB,CAG5D,OAFA,EAAA,EAAA,kBAAiB,EAAqB,CAE/B,EAAkC,EAAc"}
|
package/dist/cjs/test/test.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`./listMissingTranslations.cjs`);let t=require(`@intlayer/
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`./listMissingTranslations.cjs`);let t=require(`@intlayer/config/logger`),n=require(`@intlayer/chokidar/utils`),r=require(`@intlayer/config/node`),i=require(`@intlayer/chokidar/build`);const a=async a=>{let o=(0,r.getConfiguration)(a?.configOptions),{locales:s,requiredLocales:c}=o.internationalization,l=(0,t.getAppLogger)(o);a?.build===!0?await(0,i.prepareIntlayer)(o,{forceRun:!0}):a?.build===void 0&&await(0,i.prepareIntlayer)(o);let u=e.listMissingTranslations(a?.configOptions),d=u.missingTranslations.map(e=>` - ${e.key}`).reduce((e,t)=>Math.max(e,t.length),0),f=u.missingTranslations.map(e=>(0,n.formatLocale)(e.locales,!1)).reduce((e,t)=>Math.max(e,t.length),0),p=u.missingTranslations.map(e=>[(0,t.colon)(` - ${(0,t.colorizeKey)(e.key)}`,{colSize:d,maxSize:40}),` - `,(0,t.colon)((0,n.formatLocale)(e.locales,t.ANSIColors.RED),{colSize:f,maxSize:40}),e.filePath?` - ${(0,n.formatPath)(e.filePath)}`:``,e.id?` - remote`:``].join(``));l(`Missing translations:`,{level:`info`}),p.forEach(e=>{l(e,{level:`info`})}),l(`Locales: ${(0,n.formatLocale)(s)}`),l(`Required locales: ${(0,n.formatLocale)(c??s)}`),l(`Missing locales: ${u.missingLocales.length===0?(0,t.colorize)(`-`,t.ANSIColors.GREEN):(0,n.formatLocale)(u.missingLocales,t.ANSIColors.RED)}`),l(`Missing required locales: ${u.missingRequiredLocales.length===0?(0,t.colorize)(`-`,t.ANSIColors.GREEN):(0,n.formatLocale)(u.missingRequiredLocales,t.ANSIColors.RED)}`),l(`Total missing locales: ${(0,t.colorizeNumber)(u.missingLocales.length,{one:t.ANSIColors.RED,other:t.ANSIColors.RED,zero:t.ANSIColors.GREEN})}`),l(`Total missing required locales: ${(0,t.colorizeNumber)(u.missingRequiredLocales.length,{one:t.ANSIColors.RED,other:t.ANSIColors.RED,zero:t.ANSIColors.GREEN})}`),u.missingRequiredLocales.length>0&&process.exit(1)};exports.testMissingTranslations=a;
|
|
2
2
|
//# sourceMappingURL=test.cjs.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`../utils/setupAI.cjs`),n=require(`../utils/checkFileModifiedRange.cjs`),r=require(`../utils/getOutputFilePath.cjs`),i=require(`./translateFile.cjs`);let a=require(`@intlayer/
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`../utils/setupAI.cjs`),n=require(`../utils/checkFileModifiedRange.cjs`),r=require(`../utils/getOutputFilePath.cjs`),i=require(`./translateFile.cjs`);let a=require(`@intlayer/config/logger`),o=require(`@intlayer/chokidar/cli`),s=require(`@intlayer/chokidar/utils`),c=require(`@intlayer/config/node`),l=require(`node:path`),u=require(`node:fs`),d=require(`fast-glob`);d=e.__toESM(d);let f=require(`node:perf_hooks`);const p=async({docPattern:e,locales:p,excludedGlobPattern:m,baseLocale:h,aiOptions:g,nbSimultaneousFileProcessed:_=20,configOptions:v,customInstructions:y,skipIfModifiedBefore:b,skipIfModifiedAfter:x,skipIfExists:S,gitOptions:C,flushStrategy:w=`incremental`})=>{let T=(0,c.getConfiguration)(v);(0,o.logConfigDetails)(v);let E=(0,a.getAppLogger)(T),D=_,O=(0,s.pLimit)(D),k=await(0,d.default)(e,{ignore:m}),A=await t.setupAI(T,g);if(!A?.hasAIAccess)return;let{aiClient:j,aiConfig:M}=A;if(C){let e=await(0,o.listGitFiles)(C);e&&(k=k.filter(t=>e.some(e=>(0,l.join)(process.cwd(),t)===e)))}let N=f.performance.now();E(`Translating ${(0,a.colorizeNumber)(k.length)} files to ${(0,a.colorizeNumber)(p.length)} locales. \nGlobal Concurrency: ${(0,a.colorizeNumber)(D)} chunks in parallel.`);let P={count:0,maxErrors:5,shouldStop:!1};await(0,s.parallelize)(k.flatMap(e=>p.map(t=>async()=>{if(P.shouldStop)return;let a=(0,l.join)(T.content.baseDir,e),o=r.getOutputFilePath(a,t,h);if(S&&(0,u.existsSync)(o))return;w===`incremental`&&!(0,u.existsSync)(o)&&((0,u.mkdirSync)((0,l.dirname)(o),{recursive:!0}),(0,u.writeFileSync)(o,``));let s=n.checkFileModifiedRange(o,{skipIfModifiedBefore:b,skipIfModifiedAfter:x});if(s.isSkipped){E(s.message);return}await i.translateFile({baseFilePath:a,outputFilePath:o,locale:t,baseLocale:h,configuration:T,errorState:P,aiOptions:g,customInstructions:y,aiClient:j,aiConfig:M,flushStrategy:w,limit:O})})),e=>e(),50);let F=((f.performance.now()-N)/1e3).toFixed(2);P.count>0?E(`Finished with ${P.count} errors in ${F}s.`):E(`${(0,a.colorize)(`✔`,a.ANSIColors.GREEN)} Batch completed successfully in ${(0,a.colorizeNumber)(F)}s.`)};exports.translateDoc=p;
|
|
2
2
|
//# sourceMappingURL=translateDoc.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translateDoc.cjs","names":["setupAI","performance","getOutputFilePath","checkFileModifiedRange","translateFile","ANSIColors"],"sources":["../../../src/translateDoc/translateDoc.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { performance } from 'node:perf_hooks';\nimport { listGitFiles } from '@intlayer/chokidar/cli';\nimport { parallelize, pLimit } from '@intlayer/chokidar/utils';\nimport {\n ANSIColors,\n colorize,\n colorizeNumber,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport type { Locale } from '@intlayer/types';\nimport fg from 'fast-glob';\nimport { checkFileModifiedRange } from '../utils/checkFileModifiedRange';\nimport { getOutputFilePath } from '../utils/getOutputFilePath';\nimport { setupAI } from '../utils/setupAI';\nimport { translateFile } from './translateFile';\nimport type { ErrorState, TranslateDocOptions } from './types';\n\nexport const translateDoc = async ({\n docPattern,\n locales,\n excludedGlobPattern,\n baseLocale,\n aiOptions,\n nbSimultaneousFileProcessed = 20, // Default to a higher concurrency for chunks\n configOptions,\n customInstructions,\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n skipIfExists,\n gitOptions,\n flushStrategy = 'incremental',\n}: TranslateDocOptions) => {\n const configuration = getConfiguration(configOptions);\n const appLogger = getAppLogger(configuration);\n\n // 1. GLOBAL QUEUE SETUP\n // We use pLimit to create a single bottleneck for AI requests.\n // This queue is shared across all files and locales.\n const maxConcurrentChunks = nbSimultaneousFileProcessed;\n const globalChunkLimiter = pLimit(maxConcurrentChunks);\n\n let docList: string[] = await fg(docPattern, {\n ignore: excludedGlobPattern,\n });\n\n const aiResult = await setupAI(configuration, aiOptions);\n if (!aiResult?.hasAIAccess) return;\n const { aiClient, aiConfig } = aiResult;\n\n if (gitOptions) {\n const gitChangedFiles = await listGitFiles(gitOptions);\n if (gitChangedFiles) {\n docList = docList.filter((path) =>\n gitChangedFiles.some((gitFile) => join(process.cwd(), path) === gitFile)\n );\n }\n }\n\n const batchStartTime = performance.now();\n\n appLogger(\n `Translating ${colorizeNumber(docList.length)} files to ${colorizeNumber(locales.length)} locales. \\n` +\n `Global Concurrency: ${colorizeNumber(maxConcurrentChunks)} chunks in parallel.`\n );\n\n const errorState: ErrorState = {\n count: 0,\n maxErrors: 5,\n shouldStop: false,\n };\n\n // 2. FLATTENED TASK LIST\n // We create a task for every File x Locale combination.\n const allTasks = docList.flatMap((docPath) =>\n locales.map((locale) => async () => {\n if (errorState.shouldStop) return;\n\n const absoluteBaseFilePath = join(configuration.content.baseDir, docPath);\n const outputFilePath = getOutputFilePath(\n absoluteBaseFilePath,\n locale,\n baseLocale\n );\n\n // Skip logic\n if (skipIfExists && existsSync(outputFilePath)) return;\n\n if (flushStrategy === 'incremental' && !existsSync(outputFilePath)) {\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(outputFilePath, '');\n }\n\n const fileModificationData = checkFileModifiedRange(outputFilePath, {\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n });\n\n if (fileModificationData.isSkipped) {\n appLogger(fileModificationData.message);\n return;\n }\n\n // Execute translation using the SHARED limiter\n await translateFile({\n baseFilePath: absoluteBaseFilePath,\n outputFilePath,\n locale: locale as Locale,\n baseLocale,\n configuration,\n errorState,\n aiOptions,\n customInstructions,\n aiClient,\n aiConfig,\n flushStrategy,\n limit: globalChunkLimiter, // Pass the global queue\n });\n })\n );\n\n // 3. HIGH-THROUGHPUT FILE OPENER\n // We open many files simultaneously (e.g., 50) to ensure the global chunk queue\n // is always saturated with work.\n // If we open too few files, the chunk queue might drain faster than we can read new files.\n const FILE_OPEN_LIMIT = 50;\n\n await parallelize(allTasks, (task) => task(), FILE_OPEN_LIMIT);\n\n const batchEndTime = performance.now();\n const batchDuration = ((batchEndTime - batchStartTime) / 1000).toFixed(2);\n\n if (errorState.count > 0) {\n appLogger(`Finished with ${errorState.count} errors in ${batchDuration}s.`);\n } else {\n appLogger(\n `${colorize('✔', ANSIColors.GREEN)} Batch completed successfully in ${colorizeNumber(batchDuration)}s.`\n );\n }\n};\n"],"mappings":"iiBAoBA,MAAa,EAAe,MAAO,CACjC,aACA,UACA,sBACA,aACA,YACA,8BAA8B,GAC9B,gBACA,qBACA,uBACA,sBACA,eACA,aACA,gBAAgB,iBACS,CACzB,IAAM,GAAA,EAAA,EAAA,kBAAiC,EAAc,
|
|
1
|
+
{"version":3,"file":"translateDoc.cjs","names":["setupAI","performance","getOutputFilePath","checkFileModifiedRange","translateFile","ANSIColors"],"sources":["../../../src/translateDoc/translateDoc.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { performance } from 'node:perf_hooks';\nimport { listGitFiles, logConfigDetails } from '@intlayer/chokidar/cli';\nimport { parallelize, pLimit } from '@intlayer/chokidar/utils';\nimport {\n ANSIColors,\n colorize,\n colorizeNumber,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport type { Locale } from '@intlayer/types';\nimport fg from 'fast-glob';\nimport { checkFileModifiedRange } from '../utils/checkFileModifiedRange';\nimport { getOutputFilePath } from '../utils/getOutputFilePath';\nimport { setupAI } from '../utils/setupAI';\nimport { translateFile } from './translateFile';\nimport type { ErrorState, TranslateDocOptions } from './types';\n\nexport const translateDoc = async ({\n docPattern,\n locales,\n excludedGlobPattern,\n baseLocale,\n aiOptions,\n nbSimultaneousFileProcessed = 20, // Default to a higher concurrency for chunks\n configOptions,\n customInstructions,\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n skipIfExists,\n gitOptions,\n flushStrategy = 'incremental',\n}: TranslateDocOptions) => {\n const configuration = getConfiguration(configOptions);\n logConfigDetails(configOptions);\n\n const appLogger = getAppLogger(configuration);\n\n // 1. GLOBAL QUEUE SETUP\n // We use pLimit to create a single bottleneck for AI requests.\n // This queue is shared across all files and locales.\n const maxConcurrentChunks = nbSimultaneousFileProcessed;\n const globalChunkLimiter = pLimit(maxConcurrentChunks);\n\n let docList: string[] = await fg(docPattern, {\n ignore: excludedGlobPattern,\n });\n\n const aiResult = await setupAI(configuration, aiOptions);\n if (!aiResult?.hasAIAccess) return;\n const { aiClient, aiConfig } = aiResult;\n\n if (gitOptions) {\n const gitChangedFiles = await listGitFiles(gitOptions);\n if (gitChangedFiles) {\n docList = docList.filter((path) =>\n gitChangedFiles.some((gitFile) => join(process.cwd(), path) === gitFile)\n );\n }\n }\n\n const batchStartTime = performance.now();\n\n appLogger(\n `Translating ${colorizeNumber(docList.length)} files to ${colorizeNumber(locales.length)} locales. \\n` +\n `Global Concurrency: ${colorizeNumber(maxConcurrentChunks)} chunks in parallel.`\n );\n\n const errorState: ErrorState = {\n count: 0,\n maxErrors: 5,\n shouldStop: false,\n };\n\n // 2. FLATTENED TASK LIST\n // We create a task for every File x Locale combination.\n const allTasks = docList.flatMap((docPath) =>\n locales.map((locale) => async () => {\n if (errorState.shouldStop) return;\n\n const absoluteBaseFilePath = join(configuration.content.baseDir, docPath);\n const outputFilePath = getOutputFilePath(\n absoluteBaseFilePath,\n locale,\n baseLocale\n );\n\n // Skip logic\n if (skipIfExists && existsSync(outputFilePath)) return;\n\n if (flushStrategy === 'incremental' && !existsSync(outputFilePath)) {\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(outputFilePath, '');\n }\n\n const fileModificationData = checkFileModifiedRange(outputFilePath, {\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n });\n\n if (fileModificationData.isSkipped) {\n appLogger(fileModificationData.message);\n return;\n }\n\n // Execute translation using the SHARED limiter\n await translateFile({\n baseFilePath: absoluteBaseFilePath,\n outputFilePath,\n locale: locale as Locale,\n baseLocale,\n configuration,\n errorState,\n aiOptions,\n customInstructions,\n aiClient,\n aiConfig,\n flushStrategy,\n limit: globalChunkLimiter, // Pass the global queue\n });\n })\n );\n\n // 3. HIGH-THROUGHPUT FILE OPENER\n // We open many files simultaneously (e.g., 50) to ensure the global chunk queue\n // is always saturated with work.\n // If we open too few files, the chunk queue might drain faster than we can read new files.\n const FILE_OPEN_LIMIT = 50;\n\n await parallelize(allTasks, (task) => task(), FILE_OPEN_LIMIT);\n\n const batchEndTime = performance.now();\n const batchDuration = ((batchEndTime - batchStartTime) / 1000).toFixed(2);\n\n if (errorState.count > 0) {\n appLogger(`Finished with ${errorState.count} errors in ${batchDuration}s.`);\n } else {\n appLogger(\n `${colorize('✔', ANSIColors.GREEN)} Batch completed successfully in ${colorizeNumber(batchDuration)}s.`\n );\n }\n};\n"],"mappings":"iiBAoBA,MAAa,EAAe,MAAO,CACjC,aACA,UACA,sBACA,aACA,YACA,8BAA8B,GAC9B,gBACA,qBACA,uBACA,sBACA,eACA,aACA,gBAAgB,iBACS,CACzB,IAAM,GAAA,EAAA,EAAA,kBAAiC,EAAc,EACrD,EAAA,EAAA,kBAAiB,EAAc,CAE/B,IAAM,GAAA,EAAA,EAAA,cAAyB,EAAc,CAKvC,EAAsB,EACtB,GAAA,EAAA,EAAA,QAA4B,EAAoB,CAElD,EAAoB,MAAA,EAAA,EAAA,SAAS,EAAY,CAC3C,OAAQ,EACT,CAAC,CAEI,EAAW,MAAMA,EAAAA,QAAQ,EAAe,EAAU,CACxD,GAAI,CAAC,GAAU,YAAa,OAC5B,GAAM,CAAE,WAAU,YAAa,EAE/B,GAAI,EAAY,CACd,IAAM,EAAkB,MAAA,EAAA,EAAA,cAAmB,EAAW,CAClD,IACF,EAAU,EAAQ,OAAQ,GACxB,EAAgB,KAAM,IAAA,EAAA,EAAA,MAAiB,QAAQ,KAAK,CAAE,EAAK,GAAK,EAAQ,CACzE,EAIL,IAAM,EAAiBC,EAAAA,YAAY,KAAK,CAExC,EACE,gBAAA,EAAA,EAAA,gBAA8B,EAAQ,OAAO,CAAC,aAAA,EAAA,EAAA,gBAA2B,EAAQ,OAAO,CAAC,mCAAA,EAAA,EAAA,gBACjD,EAAoB,CAAC,sBAC9D,CAED,IAAM,EAAyB,CAC7B,MAAO,EACP,UAAW,EACX,WAAY,GACb,CAyDD,MAAA,EAAA,EAAA,aArDiB,EAAQ,QAAS,GAChC,EAAQ,IAAK,GAAW,SAAY,CAClC,GAAI,EAAW,WAAY,OAE3B,IAAM,GAAA,EAAA,EAAA,MAA4B,EAAc,QAAQ,QAAS,EAAQ,CACnE,EAAiBC,EAAAA,kBACrB,EACA,EACA,EACD,CAGD,GAAI,IAAA,EAAA,EAAA,YAA2B,EAAe,CAAE,OAE5C,IAAkB,eAAiB,EAAA,EAAA,EAAA,YAAY,EAAe,IAChE,EAAA,EAAA,YAAA,EAAA,EAAA,SAAkB,EAAe,CAAE,CAAE,UAAW,GAAM,CAAC,EACvD,EAAA,EAAA,eAAc,EAAgB,GAAG,EAGnC,IAAM,EAAuBC,EAAAA,uBAAuB,EAAgB,CAClE,uBACA,sBACD,CAAC,CAEF,GAAI,EAAqB,UAAW,CAClC,EAAU,EAAqB,QAAQ,CACvC,OAIF,MAAMC,EAAAA,cAAc,CAClB,aAAc,EACd,iBACQ,SACR,aACA,gBACA,aACA,YACA,qBACA,WACA,WACA,gBACA,MAAO,EACR,CAAC,EACF,CACH,CAQ4B,GAAS,GAAM,CAFpB,GAEsC,CAG9D,IAAM,IADeH,EAAAA,YAAY,KAAK,CACC,GAAkB,KAAM,QAAQ,EAAE,CAErE,EAAW,MAAQ,EACrB,EAAU,iBAAiB,EAAW,MAAM,aAAa,EAAc,IAAI,CAE3E,EACE,IAAA,EAAA,EAAA,UAAY,IAAKI,EAAAA,WAAW,MAAM,CAAC,oCAAA,EAAA,EAAA,gBAAkD,EAAc,CAAC,IACrG"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`../_virtual/_utils_asset.cjs`),t=require(`./validation.cjs`),n=require(`../utils/chunkInference.cjs`),r=require(`../utils/fixChunkStartEndChars.cjs`),i=require(`../utils/calculateChunks.cjs`);let a=require(`@intlayer/
|
|
2
|
-
`)},{role:`user`,content:`>>> TARGET CHUNK START <<<\n${m}\n>>> TARGET CHUNK END <<<`}],v,g,b,x),
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`../_virtual/_utils_asset.cjs`),t=require(`./validation.cjs`),n=require(`../utils/chunkInference.cjs`),r=require(`../utils/fixChunkStartEndChars.cjs`),i=require(`../utils/calculateChunks.cjs`);let a=require(`@intlayer/config/logger`),o=require(`@intlayer/chokidar/utils`),s=require(`node:path`),c=require(`@intlayer/config/utils`),l=require(`node:fs`),u=require(`node:fs/promises`),d=require(`node:perf_hooks`);const f=async({baseFilePath:f,outputFilePath:p,locale:m,baseLocale:h,configuration:g,errorState:_,aiOptions:v,customInstructions:y,aiClient:b,aiConfig:x,flushStrategy:S=`incremental`,onChunkReceive:C,limit:w})=>{if(_.shouldStop)return null;let T=(0,a.getAppLogger)(g,{config:{prefix:``}}),E=d.performance.now();try{let D=i.chunkText(await(0,u.readFile)(f,`utf-8`)),O=D.length,k=`${(0,a.colon)(`${a.ANSIColors.GREY_DARK}[${(0,o.formatPath)(f)}${a.ANSIColors.GREY_DARK}] `,{colSize:40})}${a.ANSIColors.RESET}`,A=`${(0,a.colon)(`${a.ANSIColors.GREY_DARK}[${(0,o.formatPath)(f)}${a.ANSIColors.GREY_DARK}][${(0,o.formatLocale)(m)}${a.ANSIColors.GREY_DARK}] `,{colSize:40})}${a.ANSIColors.RESET}`;T(`${k}Split into ${(0,a.colorizeNumber)(O)} chunks. Queuing...`);let j=e.readAsset(`./prompts/TRANSLATE_PROMPT.md`,`utf-8`).replaceAll(`{{localeName}}`,`${(0,o.formatLocale)(m,!1)}`).replaceAll(`{{baseLocaleName}}`,`${(0,o.formatLocale)(h,!1)}`).replace(`{{applicationContext}}`,v?.applicationContext??`-`).replace(`{{customInstructions}}`,y??`-`),M=Array(O).fill(``),N=w??(e=>e()),P=D.map((e,i)=>N(async()=>{if(_.shouldStop)return null;let o=(0,a.getAppLogger)(g,{config:{prefix:`${A} ${a.ANSIColors.GREY_DARK}[${i+1}/${O}] ${a.ANSIColors.RESET}`}}),u=d.performance.now(),f=i===0,m=e.content,h=()=>">>> CONTEXT: PREVIOUS SOURCE CONTENT <<<\n```\n"+(D[i-1]?.content??``)+"\n```\n>>> END PREVIOUS CONTEXT <<<",y=()=>">>> CONTEXT: NEXT CONTENT <<<\n```\n"+(D[i+1]?.content??``)+"\n```\n>>> END NEXT CONTEXT <<<";o(`Process started`);let{content:w,tokens:T}=await(0,c.retryManager)(async()=>{let e=await n.chunkInference([{role:`system`,content:j},...D[i+1]?[{role:`system`,content:y()}]:[],...f?[]:[{role:`system`,content:h()}],{role:`system`,content:[`You are translating TARGET CHUNK (${i+1}/${O}).`,`Translate ONLY the target chunk. Preserve frontmatter/code exactly.`].join(`
|
|
2
|
+
`)},{role:`user`,content:`>>> TARGET CHUNK START <<<\n${m}\n>>> TARGET CHUNK END <<<`}],v,g,b,x),a=t.sanitizeChunk(e?.fileContent,m);if(a=r.fixChunkStartEndChars(a,m),!t.validateTranslation(m,a,o))throw Error(`Validation failed for chunk ${i+1}/${O}`);return{content:a,tokens:e.tokenUsed}})(),E=(d.performance.now()-u).toFixed(0);if(M[i]=w,C&&C(w,i,O),S===`incremental`&&M.slice(0,i+1).every(e=>e&&e!==``)){let e=0;for(;e<O&&M[e]&&M[e]!==``;)e++;let t=M.slice(0,e).join(``);(0,l.mkdirSync)((0,s.dirname)(p),{recursive:!0}),(0,l.writeFileSync)(p,t)}o([`${(0,a.colorizeNumber)(T)} tokens used `,`${a.ANSIColors.GREY_DARK}in ${(0,a.colorizeNumber)(E)}ms${a.ANSIColors.RESET}`].join(``))}));await Promise.all(P);let F=M.join(``);(S===`end`||S===`incremental`)&&((0,l.mkdirSync)((0,s.dirname)(p),{recursive:!0}),(0,l.writeFileSync)(p,F));let I=((d.performance.now()-E)/1e3).toFixed(2),L=(0,s.relative)(g.content.baseDir,p);return T(`${(0,a.colorize)(`✔`,a.ANSIColors.GREEN)} File ${(0,o.formatPath)(L)} completed in ${(0,a.colorizeNumber)(I)}s.`),F}catch(e){_.count++;let t=e?.message??JSON.stringify(e);return T(`${(0,a.colorize)(`✖`,a.ANSIColors.RED)} Error: ${t}`),_.count>=_.maxErrors&&(_.shouldStop=!0),null}};exports.translateFile=f;
|
|
3
3
|
//# sourceMappingURL=translateFile.cjs.map
|
package/dist/cjs/watch.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./_virtual/_rolldown/runtime.cjs`);let e=require(`@intlayer/
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./_virtual/_rolldown/runtime.cjs`);let e=require(`@intlayer/config/logger`),t=require(`@intlayer/chokidar/cli`),n=require(`@intlayer/chokidar/utils`),r=require(`@intlayer/chokidar/watcher`),i=require(`@intlayer/config/node`);const a=async a=>{let o=(0,i.getConfiguration)(a?.configOptions);(0,t.logConfigDetails)(a?.configOptions);let s=(0,e.getAppLogger)(o),c;a?.with&&(c=(0,n.runParallel)(a.with),c.result.catch(()=>{process.exit(1)}));let l=(0,r.watch)({persistent:!0,skipPrepare:a?.skipPrepare??!1}),u=async()=>{process.off(`SIGINT`,u),process.off(`SIGTERM`,u),s(`Stopping Intlayer watcher...`);try{await l.close(),c&&`child`in c&&c.child?.kill(`SIGTERM`)}catch(e){console.error(`Error during shutdown:`,e)}finally{process.exit(0)}};process.on(`SIGINT`,u),process.on(`SIGTERM`,u)};exports.watchContentDeclaration=a;
|
|
2
2
|
//# sourceMappingURL=watch.cjs.map
|
package/dist/cjs/watch.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watch.cjs","names":[],"sources":["../../src/watch.ts"],"sourcesContent":["import { runParallel } from '@intlayer/chokidar/utils';\nimport { watch } from '@intlayer/chokidar/watcher';\nimport { getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\n\ntype WatchOptions = {\n skipPrepare?: boolean;\n with?: string | string[];\n configOptions?: GetConfigurationOptions;\n};\n\n/**\n * Get locales dictionaries .content.{json|ts|tsx|js|jsx|mjs|cjs} and build the JSON dictionaries in the .intlayer directory.\n * Watch mode available to get the change in the .content.{json|ts|tsx|js|jsx|mjs|cjs}\n */\nexport const watchContentDeclaration = async (options?: WatchOptions) => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config);\n\n // Store references to the child process\n let parallelProcess: ReturnType<typeof runParallel> | undefined;\n\n if (options?.with) {\n parallelProcess = runParallel(options.with);\n // Handle the promise to avoid unhandled rejection\n parallelProcess.result.catch(() => {\n // Parallel process failed or was terminated\n process.exit(1);\n });\n }\n\n // Capture the watcher instance\n const watcher = watch({\n persistent: true,\n skipPrepare: options?.skipPrepare ?? false,\n });\n\n // Define a Graceful Shutdown function\n const handleShutdown = async () => {\n // Prevent multiple calls\n process.off('SIGINT', handleShutdown);\n process.off('SIGTERM', handleShutdown);\n\n appLogger('Stopping Intlayer watcher...');\n\n try {\n // Close the file watcher immediately to stop \"esbuild service not running\" errors\n await watcher.close();\n\n // If runParallel exposes the child process, we can try to kill it explicitly.\n // Even if it doesn't, process.exit() below usually cleans up attached children.\n if (parallelProcess && 'child' in parallelProcess) {\n // @ts-ignore - Assuming child exists on the return type if runParallel is based on spawn/exec\n parallelProcess.child?.kill('SIGTERM');\n }\n } catch (error) {\n console.error('Error during shutdown:', error);\n } finally {\n process.exit(0);\n }\n };\n\n // Attach Signal Listeners\n process.on('SIGINT', handleShutdown);\n process.on('SIGTERM', handleShutdown);\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"watch.cjs","names":[],"sources":["../../src/watch.ts"],"sourcesContent":["import { logConfigDetails } from '@intlayer/chokidar/cli';\nimport { runParallel } from '@intlayer/chokidar/utils';\nimport { watch } from '@intlayer/chokidar/watcher';\nimport { getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\n\ntype WatchOptions = {\n skipPrepare?: boolean;\n with?: string | string[];\n configOptions?: GetConfigurationOptions;\n};\n\n/**\n * Get locales dictionaries .content.{json|ts|tsx|js|jsx|mjs|cjs} and build the JSON dictionaries in the .intlayer directory.\n * Watch mode available to get the change in the .content.{json|ts|tsx|js|jsx|mjs|cjs}\n */\nexport const watchContentDeclaration = async (options?: WatchOptions) => {\n const config = getConfiguration(options?.configOptions);\n logConfigDetails(options?.configOptions);\n\n const appLogger = getAppLogger(config);\n\n // Store references to the child process\n let parallelProcess: ReturnType<typeof runParallel> | undefined;\n\n if (options?.with) {\n parallelProcess = runParallel(options.with);\n // Handle the promise to avoid unhandled rejection\n parallelProcess.result.catch(() => {\n // Parallel process failed or was terminated\n process.exit(1);\n });\n }\n\n // Capture the watcher instance\n const watcher = watch({\n persistent: true,\n skipPrepare: options?.skipPrepare ?? false,\n });\n\n // Define a Graceful Shutdown function\n const handleShutdown = async () => {\n // Prevent multiple calls\n process.off('SIGINT', handleShutdown);\n process.off('SIGTERM', handleShutdown);\n\n appLogger('Stopping Intlayer watcher...');\n\n try {\n // Close the file watcher immediately to stop \"esbuild service not running\" errors\n await watcher.close();\n\n // If runParallel exposes the child process, we can try to kill it explicitly.\n // Even if it doesn't, process.exit() below usually cleans up attached children.\n if (parallelProcess && 'child' in parallelProcess) {\n // @ts-ignore - Assuming child exists on the return type if runParallel is based on spawn/exec\n parallelProcess.child?.kill('SIGTERM');\n }\n } catch (error) {\n console.error('Error during shutdown:', error);\n } finally {\n process.exit(0);\n }\n };\n\n // Attach Signal Listeners\n process.on('SIGINT', handleShutdown);\n process.on('SIGTERM', handleShutdown);\n};\n"],"mappings":"6SAmBA,MAAa,EAA0B,KAAO,IAA2B,CACvE,IAAM,GAAA,EAAA,EAAA,kBAA0B,GAAS,cAAc,EACvD,EAAA,EAAA,kBAAiB,GAAS,cAAc,CAExC,IAAM,GAAA,EAAA,EAAA,cAAyB,EAAO,CAGlC,EAEA,GAAS,OACX,GAAA,EAAA,EAAA,aAA8B,EAAQ,KAAK,CAE3C,EAAgB,OAAO,UAAY,CAEjC,QAAQ,KAAK,EAAE,EACf,EAIJ,IAAM,GAAA,EAAA,EAAA,OAAgB,CACpB,WAAY,GACZ,YAAa,GAAS,aAAe,GACtC,CAAC,CAGI,EAAiB,SAAY,CAEjC,QAAQ,IAAI,SAAU,EAAe,CACrC,QAAQ,IAAI,UAAW,EAAe,CAEtC,EAAU,+BAA+B,CAEzC,GAAI,CAEF,MAAM,EAAQ,OAAO,CAIjB,GAAmB,UAAW,GAEhC,EAAgB,OAAO,KAAK,UAAU,OAEjC,EAAO,CACd,QAAQ,MAAM,yBAA0B,EAAM,QACtC,CACR,QAAQ,KAAK,EAAE,GAKnB,QAAQ,GAAG,SAAU,EAAe,CACpC,QAAQ,GAAG,UAAW,EAAe"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{getIntlayerAPIProxy as e}from"@intlayer/api";import t from"@intlayer/config/built";import{getAppLogger as n}from"@intlayer/config/
|
|
1
|
+
import{getIntlayerAPIProxy as e}from"@intlayer/api";import t from"@intlayer/config/built";import{getAppLogger as n}from"@intlayer/config/logger";import{EventSource as r}from"eventsource";var i=class{appLogger=n(t);eventSource=null;reconnectAttempts=0;maxReconnectAttempts=5;reconnectDelay=1e3;isManuallyDisconnected=!1;reconnectTimeout=null;onDictionaryAdded;onDictionaryChange;onDictionaryDeleted;onConnectionOpen;onConnectionError;constructor(e=t){this.intlayerConfig=e,this.appLogger=n(this.intlayerConfig)}async initialize(){this.isManuallyDisconnected=!1,await this.connect()}async connect(){try{let t=this.intlayerConfig.editor.backendURL,n=await e(void 0,this.intlayerConfig).oAuth.getOAuth2AccessToken();if(!n)throw Error(`Failed to retrieve access token`);let i=`${t}/api/event-listener`;this.eventSource&&this.eventSource.close(),this.eventSource=new r(i,{fetch:(e,t)=>fetch(e,{...t,headers:{...t?.headers??{},Authorization:`Bearer ${n.data?.accessToken}`}})}),this.eventSource.onopen=()=>{this.reconnectAttempts=0,this.reconnectDelay=1e3,this.onConnectionOpen?.()},this.eventSource.onmessage=e=>this.handleMessage(e),this.eventSource.onerror=e=>this.handleError(e)}catch{this.appLogger(`Failed to establish EventSource connection:`,{level:`error`}),this.scheduleReconnect()}}cleanup(){this.isManuallyDisconnected=!0,this.reconnectTimeout&&=(clearTimeout(this.reconnectTimeout),null),this.eventSource&&=(this.eventSource.close(),null)}scheduleReconnect(){if(this.isManuallyDisconnected||this.reconnectAttempts>=this.maxReconnectAttempts){this.reconnectAttempts>=this.maxReconnectAttempts&&this.appLogger([`Max reconnection attempts (${this.maxReconnectAttempts}) reached. Giving up.`],{level:`error`});return}this.reconnectAttempts++;let e=this.reconnectDelay*2**(this.reconnectAttempts-1);this.appLogger(`Scheduling reconnection attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts} in ${e}ms`),this.reconnectTimeout=setTimeout(async()=>{this.isManuallyDisconnected||await this.connect()},e)}async handleMessage(e){try{let{data:t}=e,n=JSON.parse(t);for(let e of n)switch(e.object){case`DICTIONARY`:switch(e.status){case`ADDED`:await this.onDictionaryAdded?.(e.data);break;case`UPDATED`:await this.onDictionaryChange?.(e.data);break;case`DELETED`:await this.onDictionaryDeleted?.(e.data);break;default:this.appLogger([`Unhandled dictionary status:`,e.status],{level:`error`});break}break;default:this.appLogger([`Unknown object type:`,e.object],{level:`error`});break}}catch(e){this.appLogger([`Error processing dictionary update:`,e],{level:`error`})}}handleError(e){let t=e;this.appLogger([`EventSource error:`,{type:t.type,message:t.message,code:t.code,readyState:this.eventSource?.readyState,url:this.eventSource?.url}],{level:`error`}),this.onConnectionError?.(e),t.type===`error`&&(t.message?.includes(`terminated`)||t.message?.includes(`closed`)||this.eventSource?.readyState===r.CLOSED)&&!this.isManuallyDisconnected?(this.appLogger(`Connection was terminated by server, attempting to reconnect...`),this.scheduleReconnect()):this.cleanup()}};export{i as IntlayerEventListener};
|
|
2
2
|
//# sourceMappingURL=IntlayerEventListener.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IntlayerEventListener.mjs","names":[],"sources":["../../src/IntlayerEventListener.ts"],"sourcesContent":["import { getIntlayerAPIProxy } from '@intlayer/api';\n// @ts-ignore: @intlayer/backend is not built yet\nimport type { DictionaryAPI, MessageEventData } from '@intlayer/backend';\nimport configuration from '@intlayer/config/built';\nimport { getAppLogger } from '@intlayer/config/client';\nimport type { IntlayerConfig } from '@intlayer/types';\nimport { EventSource } from 'eventsource';\n\nexport type IntlayerMessageEvent = MessageEvent;\n\n/**\n * IntlayerEventListener class to listen for dictionary changes via SSE (Server-Sent Events).\n *\n * Usage example:\n *\n * import { buildIntlayerDictionary } from './transpiler/declaration_file_to_dictionary/intlayer_dictionary';\n * import { IntlayerEventListener } from '@intlayer/api';\n *\n * export const checkDictionaryChanges = async () => {\n * // Instantiate the listener\n * const eventListener = new IntlayerEventListener();\n *\n * // Set up your callbacks\n * eventListener.onDictionaryChange = async (dictionary) => {\n * await buildIntlayerDictionary(dictionary);\n * };\n *\n * // Initialize the listener\n * await eventListener.initialize();\n *\n * // Optionally, clean up later when you’re done\n * // eventListener.cleanup();\n * };\n */\nexport class IntlayerEventListener {\n private appLogger = getAppLogger(configuration);\n\n private eventSource: EventSource | null = null;\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 5;\n private reconnectDelay = 1000; // Start with 1 second\n private isManuallyDisconnected = false;\n private reconnectTimeout: NodeJS.Timeout | null = null;\n\n /**\n * Callback triggered when a Dictionary is ADDED.\n */\n public onDictionaryAdded?: (dictionary: DictionaryAPI) => any;\n\n /**\n * Callback triggered when a Dictionary is UPDATED.\n */\n public onDictionaryChange?: (dictionary: DictionaryAPI) => any;\n\n /**\n * Callback triggered when a Dictionary is DELETED.\n */\n public onDictionaryDeleted?: (dictionary: DictionaryAPI) => any;\n\n /**\n * Callback triggered when connection is established or re-established.\n */\n public onConnectionOpen?: () => any;\n\n /**\n * Callback triggered when connection encounters an error.\n */\n public onConnectionError?: (error: Event) => any;\n\n constructor(private intlayerConfig: IntlayerConfig = configuration) {\n this.appLogger = getAppLogger(this.intlayerConfig);\n }\n\n /**\n * Initializes the EventSource connection using the given intlayerConfig\n * (or the default config if none was provided).\n */\n public async initialize(): Promise<void> {\n this.isManuallyDisconnected = false;\n await this.connect();\n }\n\n /**\n * Establishes the EventSource connection with automatic reconnection support.\n */\n private async connect(): Promise<void> {\n try {\n const backendURL = this.intlayerConfig.editor.backendURL;\n\n // Retrieve the access token via proxy\n const accessToken = await getIntlayerAPIProxy(\n undefined,\n this.intlayerConfig\n ).oAuth.getOAuth2AccessToken();\n\n if (!accessToken) {\n throw new Error('Failed to retrieve access token');\n }\n\n const API_ROUTE = `${backendURL}/api/event-listener`;\n\n // Close existing connection if any\n if (this.eventSource) {\n this.eventSource.close();\n }\n\n this.eventSource = new EventSource(API_ROUTE, {\n fetch: (input, init) =>\n fetch(input, {\n ...init,\n headers: {\n ...(init?.headers ?? {}),\n Authorization: `Bearer ${accessToken.data?.accessToken}`,\n },\n }),\n });\n\n this.eventSource.onopen = () => {\n this.reconnectAttempts = 0;\n this.reconnectDelay = 1000; // Reset delay\n this.onConnectionOpen?.();\n };\n\n this.eventSource.onmessage = (event) => this.handleMessage(event);\n this.eventSource.onerror = (event) => this.handleError(event);\n } catch (_error) {\n this.appLogger('Failed to establish EventSource connection:', {\n level: 'error',\n });\n this.scheduleReconnect();\n }\n }\n\n /**\n * Cleans up (closes) the EventSource connection.\n */\n public cleanup(): void {\n this.isManuallyDisconnected = true;\n\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n\n if (this.eventSource) {\n this.eventSource.close();\n this.eventSource = null;\n }\n }\n\n /**\n * Schedules a reconnection attempt with exponential backoff.\n */\n private scheduleReconnect(): void {\n if (\n this.isManuallyDisconnected ||\n this.reconnectAttempts >= this.maxReconnectAttempts\n ) {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n this.appLogger(\n [\n `Max reconnection attempts (${this.maxReconnectAttempts}) reached. Giving up.`,\n ],\n {\n level: 'error',\n }\n );\n }\n return;\n }\n\n this.reconnectAttempts++;\n const delay = this.reconnectDelay * 2 ** (this.reconnectAttempts - 1); // Exponential backoff\n\n this.appLogger(\n `Scheduling reconnection attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts} in ${delay}ms`\n );\n\n this.reconnectTimeout = setTimeout(async () => {\n if (!this.isManuallyDisconnected) {\n await this.connect();\n }\n }, delay);\n }\n\n /**\n * Handles incoming SSE messages, parses the event data,\n * and invokes the appropriate callback.\n */\n private async handleMessage(event: IntlayerMessageEvent): Promise<void> {\n try {\n const { data } = event;\n\n const dataJSON: MessageEventData[] = JSON.parse(data);\n\n for (const dataEl of dataJSON) {\n switch (dataEl.object) {\n case 'DICTIONARY':\n switch (dataEl.status) {\n case 'ADDED':\n await this.onDictionaryAdded?.(dataEl.data);\n break;\n case 'UPDATED':\n await this.onDictionaryChange?.(dataEl.data);\n break;\n case 'DELETED':\n await this.onDictionaryDeleted?.(dataEl.data);\n break;\n default:\n this.appLogger(\n ['Unhandled dictionary status:', dataEl.status],\n {\n level: 'error',\n }\n );\n break;\n }\n break;\n default:\n this.appLogger(['Unknown object type:', dataEl.object], {\n level: 'error',\n });\n break;\n }\n }\n } catch (error) {\n this.appLogger(['Error processing dictionary update:', error], {\n level: 'error',\n });\n }\n }\n\n /**\n * Handles any SSE errors and attempts reconnection if appropriate.\n */\n private handleError(event: Event): void {\n const errorEvent = event as any;\n\n // Log detailed error information\n this.appLogger(\n [\n 'EventSource error:',\n {\n type: errorEvent.type,\n message: errorEvent.message,\n code: errorEvent.code,\n readyState: this.eventSource?.readyState,\n url: this.eventSource?.url,\n },\n ],\n {\n level: 'error',\n }\n );\n\n // Notify error callback\n this.onConnectionError?.(event);\n\n // Check if this is a connection close error\n const isConnectionClosed =\n errorEvent.type === 'error' &&\n (errorEvent.message?.includes('terminated') ||\n errorEvent.message?.includes('closed') ||\n this.eventSource?.readyState === EventSource.CLOSED);\n\n if (isConnectionClosed && !this.isManuallyDisconnected) {\n this.appLogger(\n 'Connection was terminated by server, attempting to reconnect...'\n );\n this.scheduleReconnect();\n } else {\n // For other types of errors, close the connection\n this.cleanup();\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"IntlayerEventListener.mjs","names":[],"sources":["../../src/IntlayerEventListener.ts"],"sourcesContent":["import { getIntlayerAPIProxy } from '@intlayer/api';\n// @ts-ignore: @intlayer/backend is not built yet\nimport type { DictionaryAPI, MessageEventData } from '@intlayer/backend';\nimport configuration from '@intlayer/config/built';\nimport { getAppLogger } from '@intlayer/config/logger';\nimport type { IntlayerConfig } from '@intlayer/types';\nimport { EventSource } from 'eventsource';\n\nexport type IntlayerMessageEvent = MessageEvent;\n\n/**\n * IntlayerEventListener class to listen for dictionary changes via SSE (Server-Sent Events).\n *\n * Usage example:\n *\n * import { buildIntlayerDictionary } from './transpiler/declaration_file_to_dictionary/intlayer_dictionary';\n * import { IntlayerEventListener } from '@intlayer/api';\n *\n * export const checkDictionaryChanges = async () => {\n * // Instantiate the listener\n * const eventListener = new IntlayerEventListener();\n *\n * // Set up your callbacks\n * eventListener.onDictionaryChange = async (dictionary) => {\n * await buildIntlayerDictionary(dictionary);\n * };\n *\n * // Initialize the listener\n * await eventListener.initialize();\n *\n * // Optionally, clean up later when you’re done\n * // eventListener.cleanup();\n * };\n */\nexport class IntlayerEventListener {\n private appLogger = getAppLogger(configuration);\n\n private eventSource: EventSource | null = null;\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 5;\n private reconnectDelay = 1000; // Start with 1 second\n private isManuallyDisconnected = false;\n private reconnectTimeout: NodeJS.Timeout | null = null;\n\n /**\n * Callback triggered when a Dictionary is ADDED.\n */\n public onDictionaryAdded?: (dictionary: DictionaryAPI) => any;\n\n /**\n * Callback triggered when a Dictionary is UPDATED.\n */\n public onDictionaryChange?: (dictionary: DictionaryAPI) => any;\n\n /**\n * Callback triggered when a Dictionary is DELETED.\n */\n public onDictionaryDeleted?: (dictionary: DictionaryAPI) => any;\n\n /**\n * Callback triggered when connection is established or re-established.\n */\n public onConnectionOpen?: () => any;\n\n /**\n * Callback triggered when connection encounters an error.\n */\n public onConnectionError?: (error: Event) => any;\n\n constructor(private intlayerConfig: IntlayerConfig = configuration) {\n this.appLogger = getAppLogger(this.intlayerConfig);\n }\n\n /**\n * Initializes the EventSource connection using the given intlayerConfig\n * (or the default config if none was provided).\n */\n public async initialize(): Promise<void> {\n this.isManuallyDisconnected = false;\n await this.connect();\n }\n\n /**\n * Establishes the EventSource connection with automatic reconnection support.\n */\n private async connect(): Promise<void> {\n try {\n const backendURL = this.intlayerConfig.editor.backendURL;\n\n // Retrieve the access token via proxy\n const accessToken = await getIntlayerAPIProxy(\n undefined,\n this.intlayerConfig\n ).oAuth.getOAuth2AccessToken();\n\n if (!accessToken) {\n throw new Error('Failed to retrieve access token');\n }\n\n const API_ROUTE = `${backendURL}/api/event-listener`;\n\n // Close existing connection if any\n if (this.eventSource) {\n this.eventSource.close();\n }\n\n this.eventSource = new EventSource(API_ROUTE, {\n fetch: (input, init) =>\n fetch(input, {\n ...init,\n headers: {\n ...(init?.headers ?? {}),\n Authorization: `Bearer ${accessToken.data?.accessToken}`,\n },\n }),\n });\n\n this.eventSource.onopen = () => {\n this.reconnectAttempts = 0;\n this.reconnectDelay = 1000; // Reset delay\n this.onConnectionOpen?.();\n };\n\n this.eventSource.onmessage = (event) => this.handleMessage(event);\n this.eventSource.onerror = (event) => this.handleError(event);\n } catch (_error) {\n this.appLogger('Failed to establish EventSource connection:', {\n level: 'error',\n });\n this.scheduleReconnect();\n }\n }\n\n /**\n * Cleans up (closes) the EventSource connection.\n */\n public cleanup(): void {\n this.isManuallyDisconnected = true;\n\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n\n if (this.eventSource) {\n this.eventSource.close();\n this.eventSource = null;\n }\n }\n\n /**\n * Schedules a reconnection attempt with exponential backoff.\n */\n private scheduleReconnect(): void {\n if (\n this.isManuallyDisconnected ||\n this.reconnectAttempts >= this.maxReconnectAttempts\n ) {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n this.appLogger(\n [\n `Max reconnection attempts (${this.maxReconnectAttempts}) reached. Giving up.`,\n ],\n {\n level: 'error',\n }\n );\n }\n return;\n }\n\n this.reconnectAttempts++;\n const delay = this.reconnectDelay * 2 ** (this.reconnectAttempts - 1); // Exponential backoff\n\n this.appLogger(\n `Scheduling reconnection attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts} in ${delay}ms`\n );\n\n this.reconnectTimeout = setTimeout(async () => {\n if (!this.isManuallyDisconnected) {\n await this.connect();\n }\n }, delay);\n }\n\n /**\n * Handles incoming SSE messages, parses the event data,\n * and invokes the appropriate callback.\n */\n private async handleMessage(event: IntlayerMessageEvent): Promise<void> {\n try {\n const { data } = event;\n\n const dataJSON: MessageEventData[] = JSON.parse(data);\n\n for (const dataEl of dataJSON) {\n switch (dataEl.object) {\n case 'DICTIONARY':\n switch (dataEl.status) {\n case 'ADDED':\n await this.onDictionaryAdded?.(dataEl.data);\n break;\n case 'UPDATED':\n await this.onDictionaryChange?.(dataEl.data);\n break;\n case 'DELETED':\n await this.onDictionaryDeleted?.(dataEl.data);\n break;\n default:\n this.appLogger(\n ['Unhandled dictionary status:', dataEl.status],\n {\n level: 'error',\n }\n );\n break;\n }\n break;\n default:\n this.appLogger(['Unknown object type:', dataEl.object], {\n level: 'error',\n });\n break;\n }\n }\n } catch (error) {\n this.appLogger(['Error processing dictionary update:', error], {\n level: 'error',\n });\n }\n }\n\n /**\n * Handles any SSE errors and attempts reconnection if appropriate.\n */\n private handleError(event: Event): void {\n const errorEvent = event as any;\n\n // Log detailed error information\n this.appLogger(\n [\n 'EventSource error:',\n {\n type: errorEvent.type,\n message: errorEvent.message,\n code: errorEvent.code,\n readyState: this.eventSource?.readyState,\n url: this.eventSource?.url,\n },\n ],\n {\n level: 'error',\n }\n );\n\n // Notify error callback\n this.onConnectionError?.(event);\n\n // Check if this is a connection close error\n const isConnectionClosed =\n errorEvent.type === 'error' &&\n (errorEvent.message?.includes('terminated') ||\n errorEvent.message?.includes('closed') ||\n this.eventSource?.readyState === EventSource.CLOSED);\n\n if (isConnectionClosed && !this.isManuallyDisconnected) {\n this.appLogger(\n 'Connection was terminated by server, attempting to reconnect...'\n );\n this.scheduleReconnect();\n } else {\n // For other types of errors, close the connection\n this.cleanup();\n }\n }\n}\n"],"mappings":"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"}
|