@intlayer/cli 8.4.2 → 8.4.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.
Files changed (72) hide show
  1. package/dist/cjs/auth/login.cjs +2 -2
  2. package/dist/cjs/auth/login.cjs.map +1 -1
  3. package/dist/cjs/extract.cjs +1 -1
  4. package/dist/cjs/extract.cjs.map +1 -1
  5. package/dist/cjs/fill/fill.cjs +1 -1
  6. package/dist/cjs/fill/fill.cjs.map +1 -1
  7. package/dist/cjs/fill/listTranslationsTasks.cjs +1 -1
  8. package/dist/cjs/fill/listTranslationsTasks.cjs.map +1 -1
  9. package/dist/cjs/fill/translateDictionary.cjs +1 -1
  10. package/dist/cjs/fill/translateDictionary.cjs.map +1 -1
  11. package/dist/cjs/pull.cjs +1 -1
  12. package/dist/cjs/pull.cjs.map +1 -1
  13. package/dist/cjs/push/pullLog.cjs +3 -3
  14. package/dist/cjs/push/pullLog.cjs.map +1 -1
  15. package/dist/cjs/push/push.cjs +1 -1
  16. package/dist/cjs/push/push.cjs.map +1 -1
  17. package/dist/cjs/pushLog.cjs +3 -3
  18. package/dist/cjs/pushLog.cjs.map +1 -1
  19. package/dist/cjs/reviewDoc/reviewDoc.cjs +1 -1
  20. package/dist/cjs/reviewDoc/reviewDoc.cjs.map +1 -1
  21. package/dist/cjs/reviewDoc/reviewDocBlockAware.cjs +1 -1
  22. package/dist/cjs/reviewDoc/reviewDocBlockAware.cjs.map +1 -1
  23. package/dist/cjs/test/test.cjs +1 -1
  24. package/dist/cjs/test/test.cjs.map +1 -1
  25. package/dist/cjs/translateDoc/translateDoc.cjs +1 -1
  26. package/dist/cjs/translateDoc/translateDoc.cjs.map +1 -1
  27. package/dist/cjs/translateDoc/translateFile.cjs +2 -2
  28. package/dist/cjs/translateDoc/translateFile.cjs.map +1 -1
  29. package/dist/cjs/utils/checkAccess.cjs +1 -1
  30. package/dist/cjs/utils/checkAccess.cjs.map +1 -1
  31. package/dist/cjs/utils/setupAI.cjs +1 -1
  32. package/dist/cjs/utils/setupAI.cjs.map +1 -1
  33. package/dist/esm/auth/login.mjs +1 -1
  34. package/dist/esm/auth/login.mjs.map +1 -1
  35. package/dist/esm/extract.mjs +1 -1
  36. package/dist/esm/extract.mjs.map +1 -1
  37. package/dist/esm/fill/fill.mjs +1 -1
  38. package/dist/esm/fill/fill.mjs.map +1 -1
  39. package/dist/esm/fill/listTranslationsTasks.mjs +1 -1
  40. package/dist/esm/fill/listTranslationsTasks.mjs.map +1 -1
  41. package/dist/esm/fill/translateDictionary.mjs +1 -1
  42. package/dist/esm/fill/translateDictionary.mjs.map +1 -1
  43. package/dist/esm/pull.mjs +1 -1
  44. package/dist/esm/pull.mjs.map +1 -1
  45. package/dist/esm/push/pullLog.mjs +1 -1
  46. package/dist/esm/push/pullLog.mjs.map +1 -1
  47. package/dist/esm/push/push.mjs +1 -1
  48. package/dist/esm/push/push.mjs.map +1 -1
  49. package/dist/esm/pushLog.mjs +1 -1
  50. package/dist/esm/pushLog.mjs.map +1 -1
  51. package/dist/esm/reviewDoc/reviewDoc.mjs +1 -1
  52. package/dist/esm/reviewDoc/reviewDoc.mjs.map +1 -1
  53. package/dist/esm/reviewDoc/reviewDocBlockAware.mjs +1 -1
  54. package/dist/esm/reviewDoc/reviewDocBlockAware.mjs.map +1 -1
  55. package/dist/esm/test/test.mjs +1 -1
  56. package/dist/esm/test/test.mjs.map +1 -1
  57. package/dist/esm/translateDoc/translateDoc.mjs +1 -1
  58. package/dist/esm/translateDoc/translateDoc.mjs.map +1 -1
  59. package/dist/esm/translateDoc/translateFile.mjs +1 -1
  60. package/dist/esm/translateDoc/translateFile.mjs.map +1 -1
  61. package/dist/esm/utils/checkAccess.mjs +1 -1
  62. package/dist/esm/utils/checkAccess.mjs.map +1 -1
  63. package/dist/esm/utils/setupAI.mjs +1 -1
  64. package/dist/esm/utils/setupAI.mjs.map +1 -1
  65. package/dist/types/auth/login.d.ts.map +1 -1
  66. package/dist/types/extract.d.ts.map +1 -1
  67. package/dist/types/pull.d.ts.map +1 -1
  68. package/dist/types/push/pullLog.d.ts.map +1 -1
  69. package/dist/types/push/push.d.ts.map +1 -1
  70. package/dist/types/setupAI-Bosjx7ah.d.ts.map +1 -1
  71. package/dist/types/utils/checkAccess.d.ts.map +1 -1
  72. package/package.json +12 -12
@@ -1 +1 @@
1
- {"version":3,"file":"pullLog.mjs","names":[],"sources":["../../../src/push/pullLog.ts"],"sourcesContent":["import type { DictionaryStatus } from '@intlayer/chokidar/build';\nimport { ANSIColors, colorize, spinnerFrames } from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\n\nexport type PullStatus = {\n dictionaryKey: string;\n status: DictionaryStatus | 'pending' | 'fetching';\n errorMessage?: string;\n};\n\nexport class PullLogger {\n private statuses: PullStatus[] = [];\n private spinnerTimer: NodeJS.Timeout | null = null;\n private spinnerIndex = 0;\n private renderedLines = 0;\n private readonly spinnerFrames = spinnerFrames;\n private isFinished = false;\n private readonly prefix: string;\n private lastRenderedState: string = '';\n\n constructor() {\n const configuration = getConfiguration();\n this.prefix = configuration.log.prefix;\n }\n\n update(newStatuses: PullStatus[]) {\n if (this.isFinished) return;\n for (const status of newStatuses) {\n const index = this.statuses.findIndex(\n (s) => s.dictionaryKey === status.dictionaryKey\n );\n if (index >= 0) {\n this.statuses[index] = status;\n } else {\n this.statuses.push(status);\n }\n }\n\n this.startSpinner();\n this.render();\n }\n\n finish() {\n this.isFinished = true;\n this.stopSpinner();\n this.render();\n }\n\n private startSpinner() {\n if (this.spinnerTimer || this.isFinished) return;\n this.spinnerTimer = setInterval(() => {\n this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length;\n this.render();\n }, 100);\n }\n\n private stopSpinner() {\n if (!this.spinnerTimer) return;\n clearInterval(this.spinnerTimer);\n this.spinnerTimer = null;\n }\n\n private render() {\n const { total, done, success, errors } = this.computeProgress();\n\n const frame = this.spinnerFrames[this.spinnerIndex];\n const lines: string[] = [];\n\n const isDone = done === total;\n const progressLabel = `dictionaries: ${done}/${total}`;\n const details: string[] = [];\n if (success > 0) details.push(`ok: ${success}`);\n if (errors > 0) details.push(colorize(`errors: ${errors}`, ANSIColors.RED));\n\n const suffix = details.length > 0 ? ` (${details.join(', ')})` : '';\n\n if (isDone) {\n lines.push(\n `${this.prefix} ${colorize('✔', ANSIColors.GREEN)} fetched ${progressLabel}${suffix}`\n );\n } else {\n lines.push(\n `${this.prefix} ${colorize(frame, ANSIColors.BLUE)} fetching ${progressLabel}${suffix}`\n );\n }\n\n const currentState = lines.join('\\n');\n if (currentState === this.lastRenderedState) {\n return;\n }\n this.lastRenderedState = currentState;\n\n if (this.renderedLines > 0) {\n process.stdout.write(`\\x1b[${this.renderedLines}F`);\n }\n\n const totalLinesToClear = Math.max(this.renderedLines, lines.length);\n for (let i = 0; i < totalLinesToClear; i++) {\n process.stdout.write('\\x1b[2K');\n const line = lines[i];\n if (line !== undefined) {\n process.stdout.write(line);\n }\n process.stdout.write('\\n');\n }\n\n this.renderedLines = lines.length;\n }\n\n private computeProgress() {\n const keys = new Set(this.statuses.map((s) => s.dictionaryKey));\n\n const doneSet = new Set<DictionaryStatus | 'error'>([\n 'fetched',\n 'imported',\n 'updated',\n 'up-to-date',\n 'reimported in JSON',\n 'new content file',\n 'error',\n ] as const);\n\n const successesSet = new Set<DictionaryStatus>([\n 'fetched',\n 'imported',\n 'updated',\n 'up-to-date',\n 'reimported in JSON',\n 'new content file',\n ] as const);\n\n const done = this.statuses.filter((s) =>\n doneSet.has(s.status as any)\n ).length;\n const success = this.statuses.filter((s) =>\n successesSet.has(s.status as any)\n ).length;\n const errors = this.statuses.filter((s) => s.status === 'error').length;\n\n return {\n total: keys.size,\n done,\n success,\n errors,\n } as const;\n }\n}\n"],"mappings":"+IAUA,IAAa,EAAb,KAAwB,CACtB,SAAiC,EAAE,CACnC,aAA8C,KAC9C,aAAuB,EACvB,cAAwB,EACxB,cAAiC,EACjC,WAAqB,GACrB,OACA,kBAAoC,GAEpC,aAAc,CAEZ,KAAK,OADiB,GAAkB,CACZ,IAAI,OAGlC,OAAO,EAA2B,CAC5B,SAAK,WACT,KAAK,IAAM,KAAU,EAAa,CAChC,IAAM,EAAQ,KAAK,SAAS,UACzB,GAAM,EAAE,gBAAkB,EAAO,cACnC,CACG,GAAS,EACX,KAAK,SAAS,GAAS,EAEvB,KAAK,SAAS,KAAK,EAAO,CAI9B,KAAK,cAAc,CACnB,KAAK,QAAQ,EAGf,QAAS,CACP,KAAK,WAAa,GAClB,KAAK,aAAa,CAClB,KAAK,QAAQ,CAGf,cAAuB,CACjB,KAAK,cAAgB,KAAK,aAC9B,KAAK,aAAe,gBAAkB,CACpC,KAAK,cAAgB,KAAK,aAAe,GAAK,KAAK,cAAc,OACjE,KAAK,QAAQ,EACZ,IAAI,EAGT,aAAsB,CACf,AAEL,KAAK,gBADL,cAAc,KAAK,aAAa,CACZ,MAGtB,QAAiB,CACf,GAAM,CAAE,QAAO,OAAM,UAAS,UAAW,KAAK,iBAAiB,CAEzD,EAAQ,KAAK,cAAc,KAAK,cAChC,EAAkB,EAAE,CAEpB,EAAS,IAAS,EAClB,EAAgB,iBAAiB,EAAK,GAAG,IACzC,EAAoB,EAAE,CACxB,EAAU,GAAG,EAAQ,KAAK,OAAO,IAAU,CAC3C,EAAS,GAAG,EAAQ,KAAK,EAAS,WAAW,IAAU,EAAW,IAAI,CAAC,CAE3E,IAAM,EAAS,EAAQ,OAAS,EAAI,KAAK,EAAQ,KAAK,KAAK,CAAC,GAAK,GAE7D,EACF,EAAM,KACJ,GAAG,KAAK,OAAO,GAAG,EAAS,IAAK,EAAW,MAAM,CAAC,WAAW,IAAgB,IAC9E,CAED,EAAM,KACJ,GAAG,KAAK,OAAO,GAAG,EAAS,EAAO,EAAW,KAAK,CAAC,YAAY,IAAgB,IAChF,CAGH,IAAM,EAAe,EAAM,KAAK;EAAK,CACrC,GAAI,IAAiB,KAAK,kBACxB,OAEF,KAAK,kBAAoB,EAErB,KAAK,cAAgB,GACvB,QAAQ,OAAO,MAAM,QAAQ,KAAK,cAAc,GAAG,CAGrD,IAAM,EAAoB,KAAK,IAAI,KAAK,cAAe,EAAM,OAAO,CACpE,IAAK,IAAI,EAAI,EAAG,EAAI,EAAmB,IAAK,CAC1C,QAAQ,OAAO,MAAM,UAAU,CAC/B,IAAM,EAAO,EAAM,GACf,IAAS,IAAA,IACX,QAAQ,OAAO,MAAM,EAAK,CAE5B,QAAQ,OAAO,MAAM;EAAK,CAG5B,KAAK,cAAgB,EAAM,OAG7B,iBAA0B,CACxB,IAAM,EAAO,IAAI,IAAI,KAAK,SAAS,IAAK,GAAM,EAAE,cAAc,CAAC,CAEzD,EAAU,IAAI,IAAgC,CAClD,UACA,WACA,UACA,aACA,qBACA,mBACA,QACD,CAAU,CAEL,EAAe,IAAI,IAAsB,CAC7C,UACA,WACA,UACA,aACA,qBACA,mBACD,CAAU,CAEL,EAAO,KAAK,SAAS,OAAQ,GACjC,EAAQ,IAAI,EAAE,OAAc,CAC7B,CAAC,OACI,EAAU,KAAK,SAAS,OAAQ,GACpC,EAAa,IAAI,EAAE,OAAc,CAClC,CAAC,OACI,EAAS,KAAK,SAAS,OAAQ,GAAM,EAAE,SAAW,QAAQ,CAAC,OAEjE,MAAO,CACL,MAAO,EAAK,KACZ,OACA,UACA,SACD"}
1
+ {"version":3,"file":"pullLog.mjs","names":[],"sources":["../../../src/push/pullLog.ts"],"sourcesContent":["import type { DictionaryStatus } from '@intlayer/chokidar/build';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, spinnerFrames } from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\n\nexport type PullStatus = {\n dictionaryKey: string;\n status: DictionaryStatus | 'pending' | 'fetching';\n errorMessage?: string;\n};\n\nexport class PullLogger {\n private statuses: PullStatus[] = [];\n private spinnerTimer: NodeJS.Timeout | null = null;\n private spinnerIndex = 0;\n private renderedLines = 0;\n private readonly spinnerFrames = spinnerFrames;\n private isFinished = false;\n private readonly prefix: string;\n private lastRenderedState: string = '';\n\n constructor() {\n const configuration = getConfiguration();\n this.prefix = configuration.log.prefix;\n }\n\n update(newStatuses: PullStatus[]) {\n if (this.isFinished) return;\n for (const status of newStatuses) {\n const index = this.statuses.findIndex(\n (s) => s.dictionaryKey === status.dictionaryKey\n );\n if (index >= 0) {\n this.statuses[index] = status;\n } else {\n this.statuses.push(status);\n }\n }\n\n this.startSpinner();\n this.render();\n }\n\n finish() {\n this.isFinished = true;\n this.stopSpinner();\n this.render();\n }\n\n private startSpinner() {\n if (this.spinnerTimer || this.isFinished) return;\n this.spinnerTimer = setInterval(() => {\n this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length;\n this.render();\n }, 100);\n }\n\n private stopSpinner() {\n if (!this.spinnerTimer) return;\n clearInterval(this.spinnerTimer);\n this.spinnerTimer = null;\n }\n\n private render() {\n const { total, done, success, errors } = this.computeProgress();\n\n const frame = this.spinnerFrames[this.spinnerIndex];\n const lines: string[] = [];\n\n const isDone = done === total;\n const progressLabel = `dictionaries: ${done}/${total}`;\n const details: string[] = [];\n if (success > 0) details.push(`ok: ${success}`);\n if (errors > 0) details.push(colorize(`errors: ${errors}`, ANSIColors.RED));\n\n const suffix = details.length > 0 ? ` (${details.join(', ')})` : '';\n\n if (isDone) {\n lines.push(\n `${this.prefix} ${colorize('✔', ANSIColors.GREEN)} fetched ${progressLabel}${suffix}`\n );\n } else {\n lines.push(\n `${this.prefix} ${colorize(frame, ANSIColors.BLUE)} fetching ${progressLabel}${suffix}`\n );\n }\n\n const currentState = lines.join('\\n');\n if (currentState === this.lastRenderedState) {\n return;\n }\n this.lastRenderedState = currentState;\n\n if (this.renderedLines > 0) {\n process.stdout.write(`\\x1b[${this.renderedLines}F`);\n }\n\n const totalLinesToClear = Math.max(this.renderedLines, lines.length);\n for (let i = 0; i < totalLinesToClear; i++) {\n process.stdout.write('\\x1b[2K');\n const line = lines[i];\n if (line !== undefined) {\n process.stdout.write(line);\n }\n process.stdout.write('\\n');\n }\n\n this.renderedLines = lines.length;\n }\n\n private computeProgress() {\n const keys = new Set(this.statuses.map((s) => s.dictionaryKey));\n\n const doneSet = new Set<DictionaryStatus | 'error'>([\n 'fetched',\n 'imported',\n 'updated',\n 'up-to-date',\n 'reimported in JSON',\n 'new content file',\n 'error',\n ] as const);\n\n const successesSet = new Set<DictionaryStatus>([\n 'fetched',\n 'imported',\n 'updated',\n 'up-to-date',\n 'reimported in JSON',\n 'new content file',\n ] as const);\n\n const done = this.statuses.filter((s) =>\n doneSet.has(s.status as any)\n ).length;\n const success = this.statuses.filter((s) =>\n successesSet.has(s.status as any)\n ).length;\n const errors = this.statuses.filter((s) => s.status === 'error').length;\n\n return {\n total: keys.size,\n done,\n success,\n errors,\n } as const;\n }\n}\n"],"mappings":"yKAWA,IAAa,EAAb,KAAwB,CACtB,SAAiC,EAAE,CACnC,aAA8C,KAC9C,aAAuB,EACvB,cAAwB,EACxB,cAAiC,EACjC,WAAqB,GACrB,OACA,kBAAoC,GAEpC,aAAc,CAEZ,KAAK,OADiB,GAAkB,CACZ,IAAI,OAGlC,OAAO,EAA2B,CAC5B,SAAK,WACT,KAAK,IAAM,KAAU,EAAa,CAChC,IAAM,EAAQ,KAAK,SAAS,UACzB,GAAM,EAAE,gBAAkB,EAAO,cACnC,CACG,GAAS,EACX,KAAK,SAAS,GAAS,EAEvB,KAAK,SAAS,KAAK,EAAO,CAI9B,KAAK,cAAc,CACnB,KAAK,QAAQ,EAGf,QAAS,CACP,KAAK,WAAa,GAClB,KAAK,aAAa,CAClB,KAAK,QAAQ,CAGf,cAAuB,CACjB,KAAK,cAAgB,KAAK,aAC9B,KAAK,aAAe,gBAAkB,CACpC,KAAK,cAAgB,KAAK,aAAe,GAAK,KAAK,cAAc,OACjE,KAAK,QAAQ,EACZ,IAAI,EAGT,aAAsB,CACf,AAEL,KAAK,gBADL,cAAc,KAAK,aAAa,CACZ,MAGtB,QAAiB,CACf,GAAM,CAAE,QAAO,OAAM,UAAS,UAAW,KAAK,iBAAiB,CAEzD,EAAQ,KAAK,cAAc,KAAK,cAChC,EAAkB,EAAE,CAEpB,EAAS,IAAS,EAClB,EAAgB,iBAAiB,EAAK,GAAG,IACzC,EAAoB,EAAE,CACxB,EAAU,GAAG,EAAQ,KAAK,OAAO,IAAU,CAC3C,EAAS,GAAG,EAAQ,KAAK,EAAS,WAAW,IAAU,EAAW,IAAI,CAAC,CAE3E,IAAM,EAAS,EAAQ,OAAS,EAAI,KAAK,EAAQ,KAAK,KAAK,CAAC,GAAK,GAE7D,EACF,EAAM,KACJ,GAAG,KAAK,OAAO,GAAG,EAAS,IAAK,EAAW,MAAM,CAAC,WAAW,IAAgB,IAC9E,CAED,EAAM,KACJ,GAAG,KAAK,OAAO,GAAG,EAAS,EAAO,EAAW,KAAK,CAAC,YAAY,IAAgB,IAChF,CAGH,IAAM,EAAe,EAAM,KAAK;EAAK,CACrC,GAAI,IAAiB,KAAK,kBACxB,OAEF,KAAK,kBAAoB,EAErB,KAAK,cAAgB,GACvB,QAAQ,OAAO,MAAM,QAAQ,KAAK,cAAc,GAAG,CAGrD,IAAM,EAAoB,KAAK,IAAI,KAAK,cAAe,EAAM,OAAO,CACpE,IAAK,IAAI,EAAI,EAAG,EAAI,EAAmB,IAAK,CAC1C,QAAQ,OAAO,MAAM,UAAU,CAC/B,IAAM,EAAO,EAAM,GACf,IAAS,IAAA,IACX,QAAQ,OAAO,MAAM,EAAK,CAE5B,QAAQ,OAAO,MAAM;EAAK,CAG5B,KAAK,cAAgB,EAAM,OAG7B,iBAA0B,CACxB,IAAM,EAAO,IAAI,IAAI,KAAK,SAAS,IAAK,GAAM,EAAE,cAAc,CAAC,CAEzD,EAAU,IAAI,IAAgC,CAClD,UACA,WACA,UACA,aACA,qBACA,mBACA,QACD,CAAU,CAEL,EAAe,IAAI,IAAsB,CAC7C,UACA,WACA,UACA,aACA,qBACA,mBACD,CAAU,CAEL,EAAO,KAAK,SAAS,OAAQ,GACjC,EAAQ,IAAI,EAAE,OAAc,CAC7B,CAAC,OACI,EAAU,KAAK,SAAS,OAAQ,GACpC,EAAa,IAAI,EAAE,OAAc,CAClC,CAAC,OACI,EAAS,KAAK,SAAS,OAAQ,GAAM,EAAE,SAAW,QAAQ,CAAC,OAEjE,MAAO,CACL,MAAO,EAAK,KACZ,OACA,UACA,SACD"}
@@ -1,2 +1,2 @@
1
- import{checkCMSAuth as e}from"../utils/checkAccess.mjs";import{PushLogger as t}from"../pushLog.mjs";import{join as n}from"node:path";import{prepareIntlayer as r,writeContentDeclaration as i}from"@intlayer/chokidar/build";import{listGitFiles as a,logConfigDetails as o}from"@intlayer/chokidar/cli";import{formatPath as s,parallelize as c}from"@intlayer/chokidar/utils";import{ANSIColors as l,colorize as u,colorizeKey as d,getAppLogger as f}from"@intlayer/config/logger";import{getConfiguration as p}from"@intlayer/config/node";import{getUnmergedDictionaries as m}from"@intlayer/unmerged-dictionaries-entry";import{getIntlayerAPIProxy as h}from"@intlayer/api";import*as g from"node:fs/promises";const _={pushed:{icon:`✔`,color:l.GREEN},modified:{icon:`✔`,color:l.GREEN},error:{icon:`✖`,color:l.RED},default:{icon:`⏲`,color:l.BLUE}},v=e=>_[e]??_.default,y=async s=>{let g=p(s?.configOptions);o(s?.configOptions);let _=f(g);s?.build===!0?await r(g,{forceRun:!0}):s?.build===void 0&&await r(g);try{if(!await e(g))return;let r=h(void 0,g),o=m(g),f=Object.values(o).flat(),p=Array.from(new Set(f.map(e=>e.location).filter(e=>e&&![`remote`,`local`,`hybrid`].includes(e)))),y=[];if(p.length>0){let{multiselect:e,confirm:t,isCancel:n}=await import(`@clack/prompts`);if(p.length===1){let e=await t({message:`Do you want to push dictionaries with custom location ${u(p[0],l.BLUE,l.RESET)}?`,initialValue:!1});if(n(e))return;e&&(y=[p[0]])}else{let t=await e({message:`Select custom locations to push:`,options:p.map(e=>({value:e,label:e})),required:!1});if(n(t))return;y=t}}let x=f.filter(e=>{let t=e.location??g.dictionary?.location??`local`;return t===`remote`||t===`hybrid`||y.includes(t)});if(x.length===0){_(`No dictionaries found to push. Only dictionaries with location ${u(`remote`,l.BLUE,l.RESET)}, ${u(`hybrid`,l.BLUE,l.RESET)} or selected custom locations are pushed.`,{level:`warn`}),_(`You can set the location in your dictionary file (e.g. ${u(`{ key: 'my-key', location: 'hybrid', ... }`,l.BLUE,l.RESET)} or globally in your intlayer.config.ts file (e.g. ${u(`{ dictionary: { location: 'hybrid' } }`,l.BLUE,l.RESET)}).`,{level:`info`});return}let S=Object.keys(o);if(s?.dictionaries){let e=s.dictionaries.filter(e=>!S.includes(e));e.length>0&&_(`The following dictionaries do not exist: ${e.join(`, `)} and have been ignored.`,{level:`error`}),x=x.filter(e=>s.dictionaries?.includes(e.key))}if(s?.gitOptions){let e=await a(s.gitOptions);x=x.filter(t=>e.includes(n(g.system.baseDir,t.filePath??``)))}if(x.length===0){_(`No local dictionaries found`,{level:`error`});return}_(`Pushing dictionaries:`);let C=x.map(e=>({dictionary:e,status:`pending`})),w=new t;w.update(C.map(e=>({dictionaryKey:e.dictionary.key,status:`pending`})));let T=[];await c(C,async e=>{e.status=`pushing`,w.update([{dictionaryKey:e.dictionary.key,status:`pushing`}]);try{let t=await r.dictionary.pushDictionaries([e.dictionary]),n=t.data?.updatedDictionaries??[],a=t.data?.newDictionaries??[],s=[...n,...a];for(let e of s){let t=o[e.key]?.find(t=>t.localId===e.localId);t&&await i({...t,id:e.id},g)}n.some(t=>t.key===e.dictionary.key)?(e.status=`modified`,T.push(e.dictionary),w.update([{dictionaryKey:e.dictionary.key,status:`modified`}])):a.some(t=>t.key===e.dictionary.key)?(e.status=`pushed`,T.push(e.dictionary),w.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}`,w.update([{dictionaryKey:e.dictionary.key,status:`error`}])}},5),w.finish();for(let e of C){let{icon:t,color:n}=v(e.status);_(` - ${d(e.dictionary.key)} ${l.GREY}[${n}${t} ${e.status}${l.GREY}]${l.RESET}`)}for(let e of C)e.errorMessage&&_(e.errorMessage,{level:`error`});let E=s?.deleteLocaleDictionary,D=s?.keepLocaleDictionary;if(E&&D)throw Error(`Cannot specify both --deleteLocaleDictionary and --keepLocaleDictionary options.`);if(E)await b(T,s);else if(!D){let e=T.filter(e=>e.location===`remote`),t=e.map(e=>e.key);if(e.length>0){let{confirm:n,isCancel:r}=await import(`@clack/prompts`),i=await n({message:`Do you want to delete the local dictionaries that were successfully pushed? ${u(`(Dictionaries:`,l.GREY,l.RESET)} ${d(t)}${u(`)`,l.GREY,l.RESET)}`,initialValue:!1});if(r(i))return;i&&await b(e,s)}}}catch(e){_(e,{level:`error`})}},b=async(e,t)=>{let n=f(p(t?.configOptions)),r=new Set;for(let t of e){let{filePath:e}=t;if(!e){n(`Dictionary ${d(t.key)} does not have a file path`,{level:`error`});continue}r.add(e)}for(let e of r)try{let t=await g.lstat(e);t.isFile()?(await g.unlink(e),n(`Deleted file ${s(e)}`,{})):t.isDirectory()?n(`Path is a directory ${s(e)}, skipping.`,{}):n(`Unknown file type for ${s(e)}, skipping.`,{})}catch(t){n(`Error deleting ${s(e)}: ${t}`,{level:`error`})}};export{y as push};
1
+ import{checkCMSAuth as e}from"../utils/checkAccess.mjs";import{PushLogger as t}from"../pushLog.mjs";import{join as n}from"node:path";import{prepareIntlayer as r,writeContentDeclaration as i}from"@intlayer/chokidar/build";import{listGitFiles as a,logConfigDetails as o}from"@intlayer/chokidar/cli";import{formatPath as s,parallelize as c}from"@intlayer/chokidar/utils";import*as l from"@intlayer/config/colors";import{colorize as u,colorizeKey as d,getAppLogger as f}from"@intlayer/config/logger";import{getConfiguration as p}from"@intlayer/config/node";import{getUnmergedDictionaries as m}from"@intlayer/unmerged-dictionaries-entry";import{getIntlayerAPIProxy as h}from"@intlayer/api";import*as g from"node:fs/promises";const _={pushed:{icon:`✔`,color:l.GREEN},modified:{icon:`✔`,color:l.GREEN},error:{icon:`✖`,color:l.RED},default:{icon:`⏲`,color:l.BLUE}},v=e=>_[e]??_.default,y=async s=>{let g=p(s?.configOptions);o(s?.configOptions);let _=f(g);s?.build===!0?await r(g,{forceRun:!0}):s?.build===void 0&&await r(g);try{if(!await e(g))return;let r=h(void 0,g),o=m(g),f=Object.values(o).flat(),p=Array.from(new Set(f.map(e=>e.location).filter(e=>e&&![`remote`,`local`,`hybrid`].includes(e)))),y=[];if(p.length>0){let{multiselect:e,confirm:t,isCancel:n}=await import(`@clack/prompts`);if(p.length===1){let e=await t({message:`Do you want to push dictionaries with custom location ${u(p[0],l.BLUE,l.RESET)}?`,initialValue:!1});if(n(e))return;e&&(y=[p[0]])}else{let t=await e({message:`Select custom locations to push:`,options:p.map(e=>({value:e,label:e})),required:!1});if(n(t))return;y=t}}let x=f.filter(e=>{let t=e.location??g.dictionary?.location??`local`;return t===`remote`||t===`hybrid`||y.includes(t)});if(x.length===0){_(`No dictionaries found to push. Only dictionaries with location ${u(`remote`,l.BLUE,l.RESET)}, ${u(`hybrid`,l.BLUE,l.RESET)} or selected custom locations are pushed.`,{level:`warn`}),_(`You can set the location in your dictionary file (e.g. ${u(`{ key: 'my-key', location: 'hybrid', ... }`,l.BLUE,l.RESET)} or globally in your intlayer.config.ts file (e.g. ${u(`{ dictionary: { location: 'hybrid' } }`,l.BLUE,l.RESET)}).`,{level:`info`});return}let S=Object.keys(o);if(s?.dictionaries){let e=s.dictionaries.filter(e=>!S.includes(e));e.length>0&&_(`The following dictionaries do not exist: ${e.join(`, `)} and have been ignored.`,{level:`error`}),x=x.filter(e=>s.dictionaries?.includes(e.key))}if(s?.gitOptions){let e=await a(s.gitOptions);x=x.filter(t=>e.includes(n(g.system.baseDir,t.filePath??``)))}if(x.length===0){_(`No local dictionaries found`,{level:`error`});return}_(`Pushing dictionaries:`);let C=x.map(e=>({dictionary:e,status:`pending`})),w=new t;w.update(C.map(e=>({dictionaryKey:e.dictionary.key,status:`pending`})));let T=[];await c(C,async e=>{e.status=`pushing`,w.update([{dictionaryKey:e.dictionary.key,status:`pushing`}]);try{let t=await r.dictionary.pushDictionaries([e.dictionary]),n=t.data?.updatedDictionaries??[],a=t.data?.newDictionaries??[],s=[...n,...a];for(let e of s){let t=o[e.key]?.find(t=>t.localId===e.localId);t&&await i({...t,id:e.id},g)}n.some(t=>t.key===e.dictionary.key)?(e.status=`modified`,T.push(e.dictionary),w.update([{dictionaryKey:e.dictionary.key,status:`modified`}])):a.some(t=>t.key===e.dictionary.key)?(e.status=`pushed`,T.push(e.dictionary),w.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}`,w.update([{dictionaryKey:e.dictionary.key,status:`error`}])}},5),w.finish();for(let e of C){let{icon:t,color:n}=v(e.status);_(` - ${d(e.dictionary.key)} ${l.GREY}[${n}${t} ${e.status}${l.GREY}]${l.RESET}`)}for(let e of C)e.errorMessage&&_(e.errorMessage,{level:`error`});let E=s?.deleteLocaleDictionary,D=s?.keepLocaleDictionary;if(E&&D)throw Error(`Cannot specify both --deleteLocaleDictionary and --keepLocaleDictionary options.`);if(E)await b(T,s);else if(!D){let e=T.filter(e=>e.location===`remote`),t=e.map(e=>e.key);if(e.length>0){let{confirm:n,isCancel:r}=await import(`@clack/prompts`),i=await n({message:`Do you want to delete the local dictionaries that were successfully pushed? ${u(`(Dictionaries:`,l.GREY,l.RESET)} ${d(t)}${u(`)`,l.GREY,l.RESET)}`,initialValue:!1});if(r(i))return;i&&await b(e,s)}}}catch(e){_(e,{level:`error`})}},b=async(e,t)=>{let n=f(p(t?.configOptions)),r=new Set;for(let t of e){let{filePath:e}=t;if(!e){n(`Dictionary ${d(t.key)} does not have a file path`,{level:`error`});continue}r.add(e)}for(let e of r)try{let t=await g.lstat(e);t.isFile()?(await g.unlink(e),n(`Deleted file ${s(e)}`,{})):t.isDirectory()?n(`Path is a directory ${s(e)}, skipping.`,{}):n(`Unknown file type for ${s(e)}, skipping.`,{})}catch(t){n(`Error deleting ${s(e)}: ${t}`,{level:`error`})}};export{y as push};
2
2
  //# sourceMappingURL=push.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"push.mjs","names":[],"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/dictionary';\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.system.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.mjs","names":[],"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 * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport type { Dictionary } from '@intlayer/types/dictionary';\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.system.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,4 +1,4 @@
1
- import{ANSIColors as e,colorize as t,colorizeNumber as n,spinnerFrames as r}from"@intlayer/config/logger";var i=class{statuses=[];spinnerTimer=null;spinnerIndex=0;renderedLines=0;spinnerFrames=r;isFinished=!1;lastRenderedState=``;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:r,done:i,pushed:a,modified:o,errors:s}=this.computeProgress(),c=this.spinnerFrames[this.spinnerIndex],l=[],u=i===r,d=`dictionaries: ${n(i)}/${n(r)}`,f=[];a>0&&f.push(`new: ${n(a)}`),o>0&&f.push(`modified: ${n(o)}`),s>0&&f.push(t(`errors: ${n(s)}`,e.RED));let p=f.length>0?` (${f.join(`, `)})`:``;u?l.push(`${t(`✔`,e.GREEN)} pushed ${d}${p}`):l.push(`${t(c,e.BLUE)} pushing ${d}${p}`);let m=l.join(`
1
+ import*as e from"@intlayer/config/colors";import{colorize as t,colorizeNumber as n,spinnerFrames as r}from"@intlayer/config/logger";var i=class{statuses=[];spinnerTimer=null;spinnerIndex=0;renderedLines=0;spinnerFrames=r;isFinished=!1;lastRenderedState=``;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:r,done:i,pushed:a,modified:o,errors:s}=this.computeProgress(),c=this.spinnerFrames[this.spinnerIndex],l=[],u=i===r,d=`dictionaries: ${n(i)}/${n(r)}`,f=[];a>0&&f.push(`new: ${n(a)}`),o>0&&f.push(`modified: ${n(o)}`),s>0&&f.push(t(`errors: ${n(s)}`,e.RED));let p=f.length>0?` (${f.join(`, `)})`:``;u?l.push(`${t(`✔`,e.GREEN)} pushed ${d}${p}`):l.push(`${t(c,e.BLUE)} pushing ${d}${p}`);let m=l.join(`
2
2
  `);if(m===this.lastRenderedState)return;this.lastRenderedState=m,this.renderedLines>0&&process.stdout.write(`\x1b[${this.renderedLines}F`);let h=Math.max(this.renderedLines,l.length);for(let e=0;e<h;e++){process.stdout.write(`\x1B[2K`);let t=l[e];t!==void 0&&process.stdout.write(t),process.stdout.write(`
3
3
  `)}this.renderedLines=l.length}computeProgress(){let e=new Set(this.statuses.map(e=>e.dictionaryKey)),t=this.statuses.filter(e=>e.status===`pushed`).length,n=this.statuses.filter(e=>e.status===`modified`).length,r=this.statuses.filter(e=>e.status===`error`).length,i=t+n+r;return{total:e.size,done:i,pushed:t,modified:n,errors:r}}};export{i as PushLogger};
4
4
  //# sourceMappingURL=pushLog.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"pushLog.mjs","names":[],"sources":["../../src/pushLog.ts"],"sourcesContent":["import {\n ANSIColors,\n colorize,\n colorizeNumber,\n spinnerFrames,\n} from '@intlayer/config/logger';\n\nexport type PushStatus = {\n dictionaryKey: string;\n status: 'pending' | 'pushing' | 'pushed' | 'modified' | 'error';\n errorMessage?: string;\n};\n\nexport class PushLogger {\n private statuses: PushStatus[] = [];\n private spinnerTimer: NodeJS.Timeout | null = null;\n private spinnerIndex = 0;\n private renderedLines = 0;\n private readonly spinnerFrames = spinnerFrames;\n private isFinished = false;\n private lastRenderedState: string = '';\n\n update(newStatuses: PushStatus[]) {\n if (this.isFinished) return;\n for (const status of newStatuses) {\n const index = this.statuses.findIndex(\n (s) => s.dictionaryKey === status.dictionaryKey\n );\n if (index >= 0) {\n this.statuses[index] = status;\n } else {\n this.statuses.push(status);\n }\n }\n\n this.startSpinner();\n this.render();\n }\n\n finish() {\n this.isFinished = true;\n this.stopSpinner();\n this.render();\n }\n\n private startSpinner() {\n if (this.spinnerTimer || this.isFinished) return;\n this.spinnerTimer = setInterval(() => {\n this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length;\n this.render();\n }, 100);\n }\n\n private stopSpinner() {\n if (!this.spinnerTimer) return;\n clearInterval(this.spinnerTimer);\n this.spinnerTimer = null;\n }\n\n private render() {\n const { total, done, pushed, modified, errors } = this.computeProgress();\n\n const frame = this.spinnerFrames[this.spinnerIndex];\n const lines: string[] = [];\n\n const isDone = done === total;\n\n const progressLabel = `dictionaries: ${colorizeNumber(done)}/${colorizeNumber(total)}`;\n const details: string[] = [];\n if (pushed > 0) details.push(`new: ${colorizeNumber(pushed)}`);\n if (modified > 0) details.push(`modified: ${colorizeNumber(modified)}`);\n if (errors > 0)\n details.push(\n colorize(`errors: ${colorizeNumber(errors)}`, ANSIColors.RED)\n );\n\n const suffix = details.length > 0 ? ` (${details.join(', ')})` : '';\n\n if (isDone) {\n lines.push(\n `${colorize('✔', ANSIColors.GREEN)} pushed ${progressLabel}${suffix}`\n );\n } else {\n lines.push(\n `${colorize(frame, ANSIColors.BLUE)} pushing ${progressLabel}${suffix}`\n );\n }\n\n const currentState = lines.join('\\n');\n if (currentState === this.lastRenderedState) {\n return;\n }\n this.lastRenderedState = currentState;\n\n if (this.renderedLines > 0) {\n process.stdout.write(`\\x1b[${this.renderedLines}F`);\n }\n\n const totalLinesToClear = Math.max(this.renderedLines, lines.length);\n for (let i = 0; i < totalLinesToClear; i++) {\n process.stdout.write('\\x1b[2K');\n const line = lines[i];\n if (line !== undefined) {\n process.stdout.write(line);\n }\n process.stdout.write('\\n');\n }\n\n this.renderedLines = lines.length;\n }\n\n private computeProgress() {\n const keys = new Set(this.statuses.map((s) => s.dictionaryKey));\n\n const pushed = this.statuses.filter((s) => s.status === 'pushed').length;\n const modified = this.statuses.filter(\n (s) => s.status === 'modified'\n ).length;\n const errors = this.statuses.filter((s) => s.status === 'error').length;\n const done = pushed + modified + errors;\n\n return {\n total: keys.size,\n done,\n pushed,\n modified,\n errors,\n } as const;\n }\n}\n"],"mappings":"0GAaA,IAAa,EAAb,KAAwB,CACtB,SAAiC,EAAE,CACnC,aAA8C,KAC9C,aAAuB,EACvB,cAAwB,EACxB,cAAiC,EACjC,WAAqB,GACrB,kBAAoC,GAEpC,OAAO,EAA2B,CAC5B,SAAK,WACT,KAAK,IAAM,KAAU,EAAa,CAChC,IAAM,EAAQ,KAAK,SAAS,UACzB,GAAM,EAAE,gBAAkB,EAAO,cACnC,CACG,GAAS,EACX,KAAK,SAAS,GAAS,EAEvB,KAAK,SAAS,KAAK,EAAO,CAI9B,KAAK,cAAc,CACnB,KAAK,QAAQ,EAGf,QAAS,CACP,KAAK,WAAa,GAClB,KAAK,aAAa,CAClB,KAAK,QAAQ,CAGf,cAAuB,CACjB,KAAK,cAAgB,KAAK,aAC9B,KAAK,aAAe,gBAAkB,CACpC,KAAK,cAAgB,KAAK,aAAe,GAAK,KAAK,cAAc,OACjE,KAAK,QAAQ,EACZ,IAAI,EAGT,aAAsB,CACf,AAEL,KAAK,gBADL,cAAc,KAAK,aAAa,CACZ,MAGtB,QAAiB,CACf,GAAM,CAAE,QAAO,OAAM,SAAQ,WAAU,UAAW,KAAK,iBAAiB,CAElE,EAAQ,KAAK,cAAc,KAAK,cAChC,EAAkB,EAAE,CAEpB,EAAS,IAAS,EAElB,EAAgB,iBAAiB,EAAe,EAAK,CAAC,GAAG,EAAe,EAAM,GAC9E,EAAoB,EAAE,CACxB,EAAS,GAAG,EAAQ,KAAK,QAAQ,EAAe,EAAO,GAAG,CAC1D,EAAW,GAAG,EAAQ,KAAK,aAAa,EAAe,EAAS,GAAG,CACnE,EAAS,GACX,EAAQ,KACN,EAAS,WAAW,EAAe,EAAO,GAAI,EAAW,IAAI,CAC9D,CAEH,IAAM,EAAS,EAAQ,OAAS,EAAI,KAAK,EAAQ,KAAK,KAAK,CAAC,GAAK,GAE7D,EACF,EAAM,KACJ,GAAG,EAAS,IAAK,EAAW,MAAM,CAAC,UAAU,IAAgB,IAC9D,CAED,EAAM,KACJ,GAAG,EAAS,EAAO,EAAW,KAAK,CAAC,WAAW,IAAgB,IAChE,CAGH,IAAM,EAAe,EAAM,KAAK;EAAK,CACrC,GAAI,IAAiB,KAAK,kBACxB,OAEF,KAAK,kBAAoB,EAErB,KAAK,cAAgB,GACvB,QAAQ,OAAO,MAAM,QAAQ,KAAK,cAAc,GAAG,CAGrD,IAAM,EAAoB,KAAK,IAAI,KAAK,cAAe,EAAM,OAAO,CACpE,IAAK,IAAI,EAAI,EAAG,EAAI,EAAmB,IAAK,CAC1C,QAAQ,OAAO,MAAM,UAAU,CAC/B,IAAM,EAAO,EAAM,GACf,IAAS,IAAA,IACX,QAAQ,OAAO,MAAM,EAAK,CAE5B,QAAQ,OAAO,MAAM;EAAK,CAG5B,KAAK,cAAgB,EAAM,OAG7B,iBAA0B,CACxB,IAAM,EAAO,IAAI,IAAI,KAAK,SAAS,IAAK,GAAM,EAAE,cAAc,CAAC,CAEzD,EAAS,KAAK,SAAS,OAAQ,GAAM,EAAE,SAAW,SAAS,CAAC,OAC5D,EAAW,KAAK,SAAS,OAC5B,GAAM,EAAE,SAAW,WACrB,CAAC,OACI,EAAS,KAAK,SAAS,OAAQ,GAAM,EAAE,SAAW,QAAQ,CAAC,OAC3D,EAAO,EAAS,EAAW,EAEjC,MAAO,CACL,MAAO,EAAK,KACZ,OACA,SACA,WACA,SACD"}
1
+ {"version":3,"file":"pushLog.mjs","names":[],"sources":["../../src/pushLog.ts"],"sourcesContent":["import * as ANSIColors from '@intlayer/config/colors';\nimport {\n colorize,\n colorizeNumber,\n spinnerFrames,\n} from '@intlayer/config/logger';\n\nexport type PushStatus = {\n dictionaryKey: string;\n status: 'pending' | 'pushing' | 'pushed' | 'modified' | 'error';\n errorMessage?: string;\n};\n\nexport class PushLogger {\n private statuses: PushStatus[] = [];\n private spinnerTimer: NodeJS.Timeout | null = null;\n private spinnerIndex = 0;\n private renderedLines = 0;\n private readonly spinnerFrames = spinnerFrames;\n private isFinished = false;\n private lastRenderedState: string = '';\n\n update(newStatuses: PushStatus[]) {\n if (this.isFinished) return;\n for (const status of newStatuses) {\n const index = this.statuses.findIndex(\n (s) => s.dictionaryKey === status.dictionaryKey\n );\n if (index >= 0) {\n this.statuses[index] = status;\n } else {\n this.statuses.push(status);\n }\n }\n\n this.startSpinner();\n this.render();\n }\n\n finish() {\n this.isFinished = true;\n this.stopSpinner();\n this.render();\n }\n\n private startSpinner() {\n if (this.spinnerTimer || this.isFinished) return;\n this.spinnerTimer = setInterval(() => {\n this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length;\n this.render();\n }, 100);\n }\n\n private stopSpinner() {\n if (!this.spinnerTimer) return;\n clearInterval(this.spinnerTimer);\n this.spinnerTimer = null;\n }\n\n private render() {\n const { total, done, pushed, modified, errors } = this.computeProgress();\n\n const frame = this.spinnerFrames[this.spinnerIndex];\n const lines: string[] = [];\n\n const isDone = done === total;\n\n const progressLabel = `dictionaries: ${colorizeNumber(done)}/${colorizeNumber(total)}`;\n const details: string[] = [];\n if (pushed > 0) details.push(`new: ${colorizeNumber(pushed)}`);\n if (modified > 0) details.push(`modified: ${colorizeNumber(modified)}`);\n if (errors > 0)\n details.push(\n colorize(`errors: ${colorizeNumber(errors)}`, ANSIColors.RED)\n );\n\n const suffix = details.length > 0 ? ` (${details.join(', ')})` : '';\n\n if (isDone) {\n lines.push(\n `${colorize('✔', ANSIColors.GREEN)} pushed ${progressLabel}${suffix}`\n );\n } else {\n lines.push(\n `${colorize(frame, ANSIColors.BLUE)} pushing ${progressLabel}${suffix}`\n );\n }\n\n const currentState = lines.join('\\n');\n if (currentState === this.lastRenderedState) {\n return;\n }\n this.lastRenderedState = currentState;\n\n if (this.renderedLines > 0) {\n process.stdout.write(`\\x1b[${this.renderedLines}F`);\n }\n\n const totalLinesToClear = Math.max(this.renderedLines, lines.length);\n for (let i = 0; i < totalLinesToClear; i++) {\n process.stdout.write('\\x1b[2K');\n const line = lines[i];\n if (line !== undefined) {\n process.stdout.write(line);\n }\n process.stdout.write('\\n');\n }\n\n this.renderedLines = lines.length;\n }\n\n private computeProgress() {\n const keys = new Set(this.statuses.map((s) => s.dictionaryKey));\n\n const pushed = this.statuses.filter((s) => s.status === 'pushed').length;\n const modified = this.statuses.filter(\n (s) => s.status === 'modified'\n ).length;\n const errors = this.statuses.filter((s) => s.status === 'error').length;\n const done = pushed + modified + errors;\n\n return {\n total: keys.size,\n done,\n pushed,\n modified,\n errors,\n } as const;\n }\n}\n"],"mappings":"oIAaA,IAAa,EAAb,KAAwB,CACtB,SAAiC,EAAE,CACnC,aAA8C,KAC9C,aAAuB,EACvB,cAAwB,EACxB,cAAiC,EACjC,WAAqB,GACrB,kBAAoC,GAEpC,OAAO,EAA2B,CAC5B,SAAK,WACT,KAAK,IAAM,KAAU,EAAa,CAChC,IAAM,EAAQ,KAAK,SAAS,UACzB,GAAM,EAAE,gBAAkB,EAAO,cACnC,CACG,GAAS,EACX,KAAK,SAAS,GAAS,EAEvB,KAAK,SAAS,KAAK,EAAO,CAI9B,KAAK,cAAc,CACnB,KAAK,QAAQ,EAGf,QAAS,CACP,KAAK,WAAa,GAClB,KAAK,aAAa,CAClB,KAAK,QAAQ,CAGf,cAAuB,CACjB,KAAK,cAAgB,KAAK,aAC9B,KAAK,aAAe,gBAAkB,CACpC,KAAK,cAAgB,KAAK,aAAe,GAAK,KAAK,cAAc,OACjE,KAAK,QAAQ,EACZ,IAAI,EAGT,aAAsB,CACf,AAEL,KAAK,gBADL,cAAc,KAAK,aAAa,CACZ,MAGtB,QAAiB,CACf,GAAM,CAAE,QAAO,OAAM,SAAQ,WAAU,UAAW,KAAK,iBAAiB,CAElE,EAAQ,KAAK,cAAc,KAAK,cAChC,EAAkB,EAAE,CAEpB,EAAS,IAAS,EAElB,EAAgB,iBAAiB,EAAe,EAAK,CAAC,GAAG,EAAe,EAAM,GAC9E,EAAoB,EAAE,CACxB,EAAS,GAAG,EAAQ,KAAK,QAAQ,EAAe,EAAO,GAAG,CAC1D,EAAW,GAAG,EAAQ,KAAK,aAAa,EAAe,EAAS,GAAG,CACnE,EAAS,GACX,EAAQ,KACN,EAAS,WAAW,EAAe,EAAO,GAAI,EAAW,IAAI,CAC9D,CAEH,IAAM,EAAS,EAAQ,OAAS,EAAI,KAAK,EAAQ,KAAK,KAAK,CAAC,GAAK,GAE7D,EACF,EAAM,KACJ,GAAG,EAAS,IAAK,EAAW,MAAM,CAAC,UAAU,IAAgB,IAC9D,CAED,EAAM,KACJ,GAAG,EAAS,EAAO,EAAW,KAAK,CAAC,WAAW,IAAgB,IAChE,CAGH,IAAM,EAAe,EAAM,KAAK;EAAK,CACrC,GAAI,IAAiB,KAAK,kBACxB,OAEF,KAAK,kBAAoB,EAErB,KAAK,cAAgB,GACvB,QAAQ,OAAO,MAAM,QAAQ,KAAK,cAAc,GAAG,CAGrD,IAAM,EAAoB,KAAK,IAAI,KAAK,cAAe,EAAM,OAAO,CACpE,IAAK,IAAI,EAAI,EAAG,EAAI,EAAmB,IAAK,CAC1C,QAAQ,OAAO,MAAM,UAAU,CAC/B,IAAM,EAAO,EAAM,GACf,IAAS,IAAA,IACX,QAAQ,OAAO,MAAM,EAAK,CAE5B,QAAQ,OAAO,MAAM;EAAK,CAG5B,KAAK,cAAgB,EAAM,OAG7B,iBAA0B,CACxB,IAAM,EAAO,IAAI,IAAI,KAAK,SAAS,IAAK,GAAM,EAAE,cAAc,CAAC,CAEzD,EAAS,KAAK,SAAS,OAAQ,GAAM,EAAE,SAAW,SAAS,CAAC,OAC5D,EAAW,KAAK,SAAS,OAC5B,GAAM,EAAE,SAAW,WACrB,CAAC,OACI,EAAS,KAAK,SAAS,OAAQ,GAAM,EAAE,SAAW,QAAQ,CAAC,OAC3D,EAAO,EAAS,EAAW,EAEjC,MAAO,CACL,MAAO,EAAK,KACZ,OACA,SACA,WACA,SACD"}
@@ -1,2 +1,2 @@
1
- import{setupAI as e}from"../utils/setupAI.mjs";import{checkFileModifiedRange as t}from"../utils/checkFileModifiedRange.mjs";import{getOutputFilePath as n}from"../utils/getOutputFilePath.mjs";import"../_utils_asset-B187VPMw.mjs";import{reviewFileBlockAware as r}from"./reviewDocBlockAware.mjs";import{existsSync as i}from"node:fs";import{join as a,relative as o}from"node:path";import{listGitFiles as s,listGitLines as c,logConfigDetails as l}from"@intlayer/chokidar/cli";import{formatLocale as u,formatPath as d,parallelize as f}from"@intlayer/chokidar/utils";import{ANSIColors as p,colorize as m,colorizeNumber as h,getAppLogger as g}from"@intlayer/config/logger";import{getConfiguration as _}from"@intlayer/config/node";import v from"fast-glob";const y=async({docPattern:y,locales:b,excludedGlobPattern:x,baseLocale:S,aiOptions:C,nbSimultaneousFileProcessed:w,configOptions:T,customInstructions:E,skipIfModifiedBefore:D,skipIfModifiedAfter:O,skipIfExists:k,gitOptions:A})=>{let j=_(T);l(T);let M=g(j),N=await e(j,C);if(!N?.hasAIAccess)return;let{aiClient:P,aiConfig:F}=N;w&&w>10&&(M(`Warning: nbSimultaneousFileProcessed is set to ${w}, which is greater than 10. Setting it to 10.`),w=10);let I=await v(y,{ignore:x});if(A){let e=await s(A);e&&(I=I.filter(t=>e.some(e=>a(process.cwd(),t)===e)))}M(`Base locale is ${u(S)}`),M(`Reviewing ${h(b.length)} locales: [ ${u(b)} ]`),M(`Reviewing ${h(I.length)} files:`),M(I.map(e=>` - ${d(e)}\n`)),await f(I.flatMap(e=>b.map(s=>async()=>{M(`Reviewing file: ${d(e)} to ${u(s)}`);let l=a(j.system.baseDir,e),f=n(l,s,S);if(k&&i(f)){let e=o(j.system.baseDir,f);M(`${m(`⊘`,p.YELLOW)} File ${d(e)} already exists, skipping.`);return}if(i(f)){let e=t(f,{skipIfModifiedBefore:D,skipIfModifiedAfter:O});if(e.isSkipped){M(e.message);return}}else (D||O)&&M(`${m(`!`,p.YELLOW)} File ${d(f)} does not exist, skipping modification date check.`);let h;if(A){let e=await c(l,A);M(`Git changed lines: ${e.join(`, `)}`),h=e}await r(l,f,s,S,C,T,E,h,P,F)})),e=>e(),w??3)};export{y as reviewDoc};
1
+ import{setupAI as e}from"../utils/setupAI.mjs";import{checkFileModifiedRange as t}from"../utils/checkFileModifiedRange.mjs";import{getOutputFilePath as n}from"../utils/getOutputFilePath.mjs";import"../_utils_asset-B187VPMw.mjs";import{reviewFileBlockAware as r}from"./reviewDocBlockAware.mjs";import{existsSync as i}from"node:fs";import{join as a,relative as o}from"node:path";import{listGitFiles as s,listGitLines as c,logConfigDetails as l}from"@intlayer/chokidar/cli";import{formatLocale as u,formatPath as d,parallelize as f}from"@intlayer/chokidar/utils";import*as p from"@intlayer/config/colors";import{colorize as m,colorizeNumber as h,getAppLogger as g}from"@intlayer/config/logger";import{getConfiguration as _}from"@intlayer/config/node";import v from"fast-glob";const y=async({docPattern:y,locales:b,excludedGlobPattern:x,baseLocale:S,aiOptions:C,nbSimultaneousFileProcessed:w,configOptions:T,customInstructions:E,skipIfModifiedBefore:D,skipIfModifiedAfter:O,skipIfExists:k,gitOptions:A})=>{let j=_(T);l(T);let M=g(j),N=await e(j,C);if(!N?.hasAIAccess)return;let{aiClient:P,aiConfig:F}=N;w&&w>10&&(M(`Warning: nbSimultaneousFileProcessed is set to ${w}, which is greater than 10. Setting it to 10.`),w=10);let I=await v(y,{ignore:x});if(A){let e=await s(A);e&&(I=I.filter(t=>e.some(e=>a(process.cwd(),t)===e)))}M(`Base locale is ${u(S)}`),M(`Reviewing ${h(b.length)} locales: [ ${u(b)} ]`),M(`Reviewing ${h(I.length)} files:`),M(I.map(e=>` - ${d(e)}\n`)),await f(I.flatMap(e=>b.map(s=>async()=>{M(`Reviewing file: ${d(e)} to ${u(s)}`);let l=a(j.system.baseDir,e),f=n(l,s,S);if(k&&i(f)){let e=o(j.system.baseDir,f);M(`${m(`⊘`,p.YELLOW)} File ${d(e)} already exists, skipping.`);return}if(i(f)){let e=t(f,{skipIfModifiedBefore:D,skipIfModifiedAfter:O});if(e.isSkipped){M(e.message);return}}else (D||O)&&M(`${m(`!`,p.YELLOW)} File ${d(f)} does not exist, skipping modification date check.`);let h;if(A){let e=await c(l,A);M(`Git changed lines: ${e.join(`, `)}`),h=e}await r(l,f,s,S,C,T,E,h,P,F)})),e=>e(),w??3)};export{y as reviewDoc};
2
2
  //# sourceMappingURL=reviewDoc.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"reviewDoc.mjs","names":[],"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/allLocales';\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.system.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.system.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":"2uBAkDA,MAAa,EAAY,MAAO,CAC9B,aACA,UACA,sBACA,aACA,YACA,8BACA,gBACA,qBACA,uBACA,sBACA,eACA,gBACsB,CACtB,IAAM,EAAgB,EAAiB,EAAc,CACrD,EAAiB,EAAc,CAE/B,IAAM,EAAY,EAAa,EAAc,CAEvC,EAAW,MAAM,EAAQ,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,MAAM,EAAG,EAAY,CAC3C,OAAQ,EACT,CAAC,CAEF,GAAI,EAAY,CACd,IAAM,EAAkB,MAAM,EAAa,EAAW,CAElD,IAIF,EAAU,EAAQ,OAAQ,GACxB,EAAgB,KAAM,GAAY,EAAK,QAAQ,KAAK,CAAE,EAAK,GAAK,EAAQ,CACzE,EAML,EAAU,kBAAkB,EAAa,EAAW,GAAG,CACvD,EACE,aAAa,EAAe,EAAQ,OAAO,CAAC,cAAc,EAAa,EAAQ,CAAC,IACjF,CAED,EAAU,aAAa,EAAe,EAAQ,OAAO,CAAC,SAAS,CAC/D,EAAU,EAAQ,IAAK,GAAS,MAAM,EAAW,EAAK,CAAC,IAAI,CAAC,CAyE5D,MAAM,EAtEW,EAAQ,QAAS,GAChC,EAAQ,IAAK,GAAW,SAAY,CAClC,EACE,mBAAmB,EAAW,EAAQ,CAAC,MAAM,EAAa,EAAO,GAClE,CAED,IAAM,EAAuB,EAAK,EAAc,OAAO,QAAS,EAAQ,CAClE,EAAiB,EACrB,EACA,EACA,EACD,CAGD,GAAI,GAAgB,EAAW,EAAe,CAAE,CAC9C,IAAM,EAAe,EACnB,EAAc,OAAO,QACrB,EACD,CACD,EACE,GAAG,EAAS,IAAK,EAAW,OAAO,CAAC,QAAQ,EAAW,EAAa,CAAC,4BACtE,CACD,OAIF,GAAI,EAAW,EAAe,CAAE,CAC9B,IAAM,EAAuB,EAAuB,EAAgB,CAClE,uBACA,sBACD,CAAC,CAEF,GAAI,EAAqB,UAAW,CAClC,EAAU,EAAqB,QAAQ,CACvC,cAEO,GAAwB,IAEjC,EACE,GAAG,EAAS,IAAK,EAAW,OAAO,CAAC,QAAQ,EAAW,EAAe,CAAC,oDACxE,CAGH,IAAI,EAEJ,GAAI,EAAY,CACd,IAAM,EAAkB,MAAM,EAC5B,EACA,EACD,CAED,EAAU,sBAAsB,EAAgB,KAAK,KAAK,GAAG,CAC7D,EAAe,EAGjB,MAAM,EACJ,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACD,EACD,CACH,CAIE,GAAS,GAAM,CAChB,GAA+B,EAChC"}
1
+ {"version":3,"file":"reviewDoc.mjs","names":[],"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 * as ANSIColors from '@intlayer/config/colors';\nimport {\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/allLocales';\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.system.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.system.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":"qwBAkDA,MAAa,EAAY,MAAO,CAC9B,aACA,UACA,sBACA,aACA,YACA,8BACA,gBACA,qBACA,uBACA,sBACA,eACA,gBACsB,CACtB,IAAM,EAAgB,EAAiB,EAAc,CACrD,EAAiB,EAAc,CAE/B,IAAM,EAAY,EAAa,EAAc,CAEvC,EAAW,MAAM,EAAQ,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,MAAM,EAAG,EAAY,CAC3C,OAAQ,EACT,CAAC,CAEF,GAAI,EAAY,CACd,IAAM,EAAkB,MAAM,EAAa,EAAW,CAElD,IAIF,EAAU,EAAQ,OAAQ,GACxB,EAAgB,KAAM,GAAY,EAAK,QAAQ,KAAK,CAAE,EAAK,GAAK,EAAQ,CACzE,EAML,EAAU,kBAAkB,EAAa,EAAW,GAAG,CACvD,EACE,aAAa,EAAe,EAAQ,OAAO,CAAC,cAAc,EAAa,EAAQ,CAAC,IACjF,CAED,EAAU,aAAa,EAAe,EAAQ,OAAO,CAAC,SAAS,CAC/D,EAAU,EAAQ,IAAK,GAAS,MAAM,EAAW,EAAK,CAAC,IAAI,CAAC,CAyE5D,MAAM,EAtEW,EAAQ,QAAS,GAChC,EAAQ,IAAK,GAAW,SAAY,CAClC,EACE,mBAAmB,EAAW,EAAQ,CAAC,MAAM,EAAa,EAAO,GAClE,CAED,IAAM,EAAuB,EAAK,EAAc,OAAO,QAAS,EAAQ,CAClE,EAAiB,EACrB,EACA,EACA,EACD,CAGD,GAAI,GAAgB,EAAW,EAAe,CAAE,CAC9C,IAAM,EAAe,EACnB,EAAc,OAAO,QACrB,EACD,CACD,EACE,GAAG,EAAS,IAAK,EAAW,OAAO,CAAC,QAAQ,EAAW,EAAa,CAAC,4BACtE,CACD,OAIF,GAAI,EAAW,EAAe,CAAE,CAC9B,IAAM,EAAuB,EAAuB,EAAgB,CAClE,uBACA,sBACD,CAAC,CAEF,GAAI,EAAqB,UAAW,CAClC,EAAU,EAAqB,QAAQ,CACvC,cAEO,GAAwB,IAEjC,EACE,GAAG,EAAS,IAAK,EAAW,OAAO,CAAC,QAAQ,EAAW,EAAe,CAAC,oDACxE,CAGH,IAAI,EAEJ,GAAI,EAAY,CACd,IAAM,EAAkB,MAAM,EAC5B,EACA,EACD,CAED,EAAU,sBAAsB,EAAgB,KAAK,KAAK,GAAG,CAC7D,EAAe,EAGjB,MAAM,EACJ,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACD,EACD,CACH,CAIE,GAAS,GAAM,CAChB,GAA+B,EAChC"}
@@ -1,2 +1,2 @@
1
- import{t as e}from"../_utils_asset-B187VPMw.mjs";import{sanitizeChunk as t,validateTranslation as n}from"../translateDoc/validation.mjs";import{mergeReviewedSegments as r}from"../translation-alignment/rebuildDocument.mjs";import{buildAlignmentPlan as i}from"../translation-alignment/pipeline.mjs";import{chunkInference as a}from"../utils/chunkInference.mjs";import{fixChunkStartEndChars as o}from"../utils/fixChunkStartEndChars.mjs";import{mkdirSync as s,writeFileSync as c}from"node:fs";import{dirname as l}from"node:path";import{formatLocale as u,formatPath as d}from"@intlayer/chokidar/utils";import{ANSIColors as f,colon as p,colorize as m,colorizeNumber as h,getAppLogger as g}from"@intlayer/config/logger";import{getConfiguration as _}from"@intlayer/config/node";import{retryManager as v}from"@intlayer/config/utils";import{readFile as y}from"node:fs/promises";import{getLocaleName as b}from"@intlayer/core/localization";import{ENGLISH as x}from"@intlayer/types/locales";const S=async(S,C,w,T,E,D,O,k,A,j)=>{let M=_(D),N=g(M),P=await y(S,`utf-8`),F=await y(C,`utf-8`).catch(()=>``),I=e(`./prompts/REVIEW_PROMPT.md`,`utf-8`).replaceAll(`{{localeName}}`,`${u(w,!1)}`).replaceAll(`{{baseLocaleName}}`,`${u(T,!1)}`).replace(`{{applicationContext}}`,E?.applicationContext??`-`).replace(`{{customInstructions}}`,O??`-`),L=[p(`${f.GREY_DARK}[${d(S)}${f.GREY_DARK}] `,{colSize:40}),`→ ${f.RESET}`].join(``),R=[p(`${f.GREY_DARK}[${d(S)}${f.GREY_DARK}][${u(w)}${f.GREY_DARK}] `,{colSize:40}),`→ ${f.RESET}`].join(``),{englishBlocks:z,frenchBlocks:B,plan:V,segmentsToReview:H}=i({englishText:P,frenchText:F,changedLines:k});if(N(`${L}Block-aware alignment complete. Total blocks: EN=${h(z.length)}, FR=${h(B.length)}`),N(`${L}Actions: reuse=${h(V.actions.filter(e=>e.kind===`reuse`).length)}, review=${h(V.actions.filter(e=>e.kind===`review`).length)}, new=${h(V.actions.filter(e=>e.kind===`insert_new`).length)}, delete=${h(V.actions.filter(e=>e.kind===`delete`).length)}`),H.length===0){N(`${L}No segments need review, reusing existing translation`),s(l(C),{recursive:!0}),c(C,r(V,B,new Map)),N(`${m(`✔`,f.GREEN)} File ${d(C)} updated successfully (no changes needed).`);return}N(`${L}Segments to review: ${h(H.length)}`);let U=new Map;for(let e of H){let r=H.indexOf(e)+1,i=e.englishBlock,s=()=>`**BLOCK ${r} of ${H.length}** is the base block in ${u(T,!1)} as reference.\n///chunksStart///\n`+i.content+`///chunksEnd///`,c=()=>`**BLOCK ${r} of ${H.length}** is the current block to review in ${u(w,!1)}.\n///chunksStart///\n`+(e.frenchBlockText??``)+`///chunksEnd///`,l=await v(async()=>{let e=await a([{role:`system`,content:I},{role:`system`,content:s()},{role:`system`,content:c()},{role:`system`,content:`The next user message will be the **BLOCK ${h(r)} of ${h(H.length)}** that should be translated in ${b(w,x)} (${w}).`},{role:`user`,content:i.content}],E,M,A,j);N(`${R}${h(e.tokenUsed)} tokens used - Block ${h(r)} of ${h(H.length)}`);let l=t(e?.fileContent,i.content);if(l=o(l,i.content),!n(i.content,l,N))throw Error(`Validation failed for chunk (structure or length mismatch). Retrying...`);return l})();U.set(e.actionIndex,l)}let W=r(V,B,U);s(l(C),{recursive:!0}),c(C,W),N(`${m(`✔`,f.GREEN)} File ${d(C)} created/updated successfully.`)};export{S as reviewFileBlockAware};
1
+ import{t as e}from"../_utils_asset-B187VPMw.mjs";import{sanitizeChunk as t,validateTranslation as n}from"../translateDoc/validation.mjs";import{mergeReviewedSegments as r}from"../translation-alignment/rebuildDocument.mjs";import{buildAlignmentPlan as i}from"../translation-alignment/pipeline.mjs";import{chunkInference as a}from"../utils/chunkInference.mjs";import{fixChunkStartEndChars as o}from"../utils/fixChunkStartEndChars.mjs";import{mkdirSync as s,writeFileSync as c}from"node:fs";import{dirname as l}from"node:path";import{formatLocale as u,formatPath as d}from"@intlayer/chokidar/utils";import*as f from"@intlayer/config/colors";import{colon as p,colorize as m,colorizeNumber as h,getAppLogger as g}from"@intlayer/config/logger";import{getConfiguration as _}from"@intlayer/config/node";import{retryManager as v}from"@intlayer/config/utils";import{readFile as y}from"node:fs/promises";import{getLocaleName as b}from"@intlayer/core/localization";import{ENGLISH as x}from"@intlayer/types/locales";const S=async(S,C,w,T,E,D,O,k,A,j)=>{let M=_(D),N=g(M),P=await y(S,`utf-8`),F=await y(C,`utf-8`).catch(()=>``),I=e(`./prompts/REVIEW_PROMPT.md`,`utf-8`).replaceAll(`{{localeName}}`,`${u(w,!1)}`).replaceAll(`{{baseLocaleName}}`,`${u(T,!1)}`).replace(`{{applicationContext}}`,E?.applicationContext??`-`).replace(`{{customInstructions}}`,O??`-`),L=[p(`${f.GREY_DARK}[${d(S)}${f.GREY_DARK}] `,{colSize:40}),`→ ${f.RESET}`].join(``),R=[p(`${f.GREY_DARK}[${d(S)}${f.GREY_DARK}][${u(w)}${f.GREY_DARK}] `,{colSize:40}),`→ ${f.RESET}`].join(``),{englishBlocks:z,frenchBlocks:B,plan:V,segmentsToReview:H}=i({englishText:P,frenchText:F,changedLines:k});if(N(`${L}Block-aware alignment complete. Total blocks: EN=${h(z.length)}, FR=${h(B.length)}`),N(`${L}Actions: reuse=${h(V.actions.filter(e=>e.kind===`reuse`).length)}, review=${h(V.actions.filter(e=>e.kind===`review`).length)}, new=${h(V.actions.filter(e=>e.kind===`insert_new`).length)}, delete=${h(V.actions.filter(e=>e.kind===`delete`).length)}`),H.length===0){N(`${L}No segments need review, reusing existing translation`),s(l(C),{recursive:!0}),c(C,r(V,B,new Map)),N(`${m(`✔`,f.GREEN)} File ${d(C)} updated successfully (no changes needed).`);return}N(`${L}Segments to review: ${h(H.length)}`);let U=new Map;for(let e of H){let r=H.indexOf(e)+1,i=e.englishBlock,s=()=>`**BLOCK ${r} of ${H.length}** is the base block in ${u(T,!1)} as reference.\n///chunksStart///\n`+i.content+`///chunksEnd///`,c=()=>`**BLOCK ${r} of ${H.length}** is the current block to review in ${u(w,!1)}.\n///chunksStart///\n`+(e.frenchBlockText??``)+`///chunksEnd///`,l=await v(async()=>{let e=await a([{role:`system`,content:I},{role:`system`,content:s()},{role:`system`,content:c()},{role:`system`,content:`The next user message will be the **BLOCK ${h(r)} of ${h(H.length)}** that should be translated in ${b(w,x)} (${w}).`},{role:`user`,content:i.content}],E,M,A,j);N(`${R}${h(e.tokenUsed)} tokens used - Block ${h(r)} of ${h(H.length)}`);let l=t(e?.fileContent,i.content);if(l=o(l,i.content),!n(i.content,l,N))throw Error(`Validation failed for chunk (structure or length mismatch). Retrying...`);return l})();U.set(e.actionIndex,l)}let W=r(V,B,U);s(l(C),{recursive:!0}),c(C,W),N(`${m(`✔`,f.GREEN)} File ${d(C)} created/updated successfully.`)};export{S as reviewFileBlockAware};
2
2
  //# sourceMappingURL=reviewDocBlockAware.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"reviewDocBlockAware.mjs","names":[],"sources":["../../../src/reviewDoc/reviewDocBlockAware.ts"],"sourcesContent":["import { mkdirSync, writeFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { readAsset } from 'utils:asset';\nimport type { AIConfig } from '@intlayer/ai';\nimport type { AIOptions } from '@intlayer/api';\nimport { formatLocale, formatPath } from '@intlayer/chokidar/utils';\nimport {\n ANSIColors,\n colon,\n colorize,\n colorizeNumber,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { retryManager } from '@intlayer/config/utils';\nimport { getLocaleName } from '@intlayer/core/localization';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport { ENGLISH } from '@intlayer/types/locales';\nimport { sanitizeChunk, validateTranslation } from '../translateDoc/validation';\nimport {\n buildAlignmentPlan,\n mergeReviewedSegments,\n} from '../translation-alignment/pipeline';\nimport { chunkInference } from '../utils/chunkInference';\nimport { fixChunkStartEndChars } from '../utils/fixChunkStartEndChars';\nimport type { AIClient } from '../utils/setupAI';\n\n/**\n * Review a file using block-aware alignment.\n * This approach:\n * 1. Segments both English and French documents into semantic blocks\n * 2. Aligns blocks using structure (special chars, numbers) and context\n * 3. Detects which blocks changed, were added, or deleted\n * 4. Only sends changed/new blocks to AI for translation\n * 5. Handles reordering automatically\n */\nexport const reviewFileBlockAware = async (\n baseFilePath: string,\n outputFilePath: string,\n locale: Locale,\n baseLocale: Locale,\n aiOptions?: AIOptions,\n configOptions?: GetConfigurationOptions,\n customInstructions?: string,\n changedLines?: number[],\n aiClient?: AIClient,\n aiConfig?: AIConfig\n) => {\n const configuration = getConfiguration(configOptions);\n const applicationLogger = getAppLogger(configuration);\n\n const englishText = await readFile(baseFilePath, 'utf-8');\n const frenchText = await readFile(outputFilePath, 'utf-8').catch(() => '');\n\n const basePrompt = readAsset('./prompts/REVIEW_PROMPT.md', 'utf-8')\n .replaceAll('{{localeName}}', `${formatLocale(locale, false)}`)\n .replaceAll('{{baseLocaleName}}', `${formatLocale(baseLocale, false)}`)\n .replace('{{applicationContext}}', aiOptions?.applicationContext ?? '-')\n .replace('{{customInstructions}}', customInstructions ?? '-');\n\n const filePrefixText = `${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}] `;\n const filePrefix = [\n colon(filePrefixText, { colSize: 40 }),\n `→ ${ANSIColors.RESET}`,\n ].join('');\n const prefixText = `${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}][${formatLocale(locale)}${ANSIColors.GREY_DARK}] `;\n const prefix = [\n colon(prefixText, { colSize: 40 }),\n `→ ${ANSIColors.RESET}`,\n ].join('');\n\n // Build block-aware alignment and plan\n const { englishBlocks, frenchBlocks, plan, segmentsToReview } =\n buildAlignmentPlan({\n englishText,\n frenchText,\n changedLines,\n });\n\n applicationLogger(\n `${filePrefix}Block-aware alignment complete. Total blocks: EN=${colorizeNumber(englishBlocks.length)}, FR=${colorizeNumber(frenchBlocks.length)}`\n );\n applicationLogger(\n `${filePrefix}Actions: reuse=${colorizeNumber(plan.actions.filter((a) => a.kind === 'reuse').length)}, review=${colorizeNumber(plan.actions.filter((a) => a.kind === 'review').length)}, new=${colorizeNumber(plan.actions.filter((a) => a.kind === 'insert_new').length)}, delete=${colorizeNumber(plan.actions.filter((a) => a.kind === 'delete').length)}`\n );\n\n if (segmentsToReview.length === 0) {\n applicationLogger(\n `${filePrefix}No segments need review, reusing existing translation`\n );\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(\n outputFilePath,\n mergeReviewedSegments(plan, frenchBlocks, new Map())\n );\n applicationLogger(\n `${colorize('✔', ANSIColors.GREEN)} File ${formatPath(outputFilePath)} updated successfully (no changes needed).`\n );\n return;\n }\n\n applicationLogger(\n `${filePrefix}Segments to review: ${colorizeNumber(segmentsToReview.length)}`\n );\n\n // Review segments that need AI translation\n const reviewedSegmentsMap = new Map<number, string>();\n\n for (const segment of segmentsToReview) {\n const segmentNumber = segmentsToReview.indexOf(segment) + 1;\n const englishBlock = segment.englishBlock;\n\n const getBaseChunkContextPrompt = () =>\n `**BLOCK ${segmentNumber} of ${segmentsToReview.length}** is the base block in ${formatLocale(baseLocale, false)} as reference.\\n` +\n `///chunksStart///\\n` +\n englishBlock.content +\n `///chunksEnd///`;\n\n const getFrenchChunkPrompt = () =>\n `**BLOCK ${segmentNumber} of ${segmentsToReview.length}** is the current block to review in ${formatLocale(locale, false)}.\\n` +\n `///chunksStart///\\n` +\n (segment.frenchBlockText ?? '') +\n `///chunksEnd///`;\n\n const reviewedChunkResult = await retryManager(async () => {\n const result = await chunkInference(\n [\n { role: 'system', content: basePrompt },\n { role: 'system', content: getBaseChunkContextPrompt() },\n { role: 'system', content: getFrenchChunkPrompt() },\n {\n role: 'system',\n content: `The next user message will be the **BLOCK ${colorizeNumber(segmentNumber)} of ${colorizeNumber(segmentsToReview.length)}** that should be translated in ${getLocaleName(locale, ENGLISH)} (${locale}).`,\n },\n { role: 'user', content: englishBlock.content },\n ],\n aiOptions,\n configuration,\n aiClient,\n aiConfig\n );\n\n applicationLogger(\n `${prefix}${colorizeNumber(result.tokenUsed)} tokens used - Block ${colorizeNumber(segmentNumber)} of ${colorizeNumber(segmentsToReview.length)}`\n );\n\n // Sanitize artifacts (e.g. Markdown code block wrappers)\n let processedChunk = sanitizeChunk(\n result?.fileContent,\n englishBlock.content\n );\n\n // Fix start/end characters\n processedChunk = fixChunkStartEndChars(\n processedChunk,\n englishBlock.content\n );\n\n // Validate Translation (YAML, Code fences, Length ratio)\n const isValid = validateTranslation(\n englishBlock.content,\n processedChunk,\n applicationLogger\n );\n\n if (!isValid) {\n throw new Error(\n 'Validation failed for chunk (structure or length mismatch). Retrying...'\n );\n }\n\n return processedChunk;\n })();\n\n reviewedSegmentsMap.set(segment.actionIndex, reviewedChunkResult);\n }\n\n // Merge reviewed segments back into final document\n const finalFrenchOutput = mergeReviewedSegments(\n plan,\n frenchBlocks,\n reviewedSegmentsMap\n );\n\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(outputFilePath, finalFrenchOutput);\n\n applicationLogger(\n `${colorize('✔', ANSIColors.GREEN)} File ${formatPath(outputFilePath)} created/updated successfully.`\n );\n};\n"],"mappings":"i9BAwCA,MAAa,EAAuB,MAClC,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,IACG,CACH,IAAM,EAAgB,EAAiB,EAAc,CAC/C,EAAoB,EAAa,EAAc,CAE/C,EAAc,MAAM,EAAS,EAAc,QAAQ,CACnD,EAAa,MAAM,EAAS,EAAgB,QAAQ,CAAC,UAAY,GAAG,CAEpE,EAAa,EAAU,6BAA8B,QAAQ,CAChE,WAAW,iBAAkB,GAAG,EAAa,EAAQ,GAAM,GAAG,CAC9D,WAAW,qBAAsB,GAAG,EAAa,EAAY,GAAM,GAAG,CACtE,QAAQ,yBAA0B,GAAW,oBAAsB,IAAI,CACvE,QAAQ,yBAA0B,GAAsB,IAAI,CAGzD,EAAa,CACjB,EAFqB,GAAG,EAAW,UAAU,GAAG,EAAW,EAAa,GAAG,EAAW,UAAU,IAE1E,CAAE,QAAS,GAAI,CAAC,CACtC,KAAK,EAAW,QACjB,CAAC,KAAK,GAAG,CAEJ,EAAS,CACb,EAFiB,GAAG,EAAW,UAAU,GAAG,EAAW,EAAa,GAAG,EAAW,UAAU,IAAI,EAAa,EAAO,GAAG,EAAW,UAAU,IAE1H,CAAE,QAAS,GAAI,CAAC,CAClC,KAAK,EAAW,QACjB,CAAC,KAAK,GAAG,CAGJ,CAAE,gBAAe,eAAc,OAAM,oBACzC,EAAmB,CACjB,cACA,aACA,eACD,CAAC,CASJ,GAPA,EACE,GAAG,EAAW,mDAAmD,EAAe,EAAc,OAAO,CAAC,OAAO,EAAe,EAAa,OAAO,GACjJ,CACD,EACE,GAAG,EAAW,iBAAiB,EAAe,EAAK,QAAQ,OAAQ,GAAM,EAAE,OAAS,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAe,EAAK,QAAQ,OAAQ,GAAM,EAAE,OAAS,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAe,EAAK,QAAQ,OAAQ,GAAM,EAAE,OAAS,aAAa,CAAC,OAAO,CAAC,WAAW,EAAe,EAAK,QAAQ,OAAQ,GAAM,EAAE,OAAS,SAAS,CAAC,OAAO,GAC5V,CAEG,EAAiB,SAAW,EAAG,CACjC,EACE,GAAG,EAAW,uDACf,CACD,EAAU,EAAQ,EAAe,CAAE,CAAE,UAAW,GAAM,CAAC,CACvD,EACE,EACA,EAAsB,EAAM,EAAc,IAAI,IAAM,CACrD,CACD,EACE,GAAG,EAAS,IAAK,EAAW,MAAM,CAAC,QAAQ,EAAW,EAAe,CAAC,4CACvE,CACD,OAGF,EACE,GAAG,EAAW,sBAAsB,EAAe,EAAiB,OAAO,GAC5E,CAGD,IAAM,EAAsB,IAAI,IAEhC,IAAK,IAAM,KAAW,EAAkB,CACtC,IAAM,EAAgB,EAAiB,QAAQ,EAAQ,CAAG,EACpD,EAAe,EAAQ,aAEvB,MACJ,WAAW,EAAc,MAAM,EAAiB,OAAO,0BAA0B,EAAa,EAAY,GAAM,CAAC,qCAEjH,EAAa,QACb,kBAEI,MACJ,WAAW,EAAc,MAAM,EAAiB,OAAO,uCAAuC,EAAa,EAAQ,GAAM,CAAC,yBAEzH,EAAQ,iBAAmB,IAC5B,kBAEI,EAAsB,MAAM,EAAa,SAAY,CACzD,IAAM,EAAS,MAAM,EACnB,CACE,CAAE,KAAM,SAAU,QAAS,EAAY,CACvC,CAAE,KAAM,SAAU,QAAS,GAA2B,CAAE,CACxD,CAAE,KAAM,SAAU,QAAS,GAAsB,CAAE,CACnD,CACE,KAAM,SACN,QAAS,6CAA6C,EAAe,EAAc,CAAC,MAAM,EAAe,EAAiB,OAAO,CAAC,kCAAkC,EAAc,EAAQ,EAAQ,CAAC,IAAI,EAAO,IAC/M,CACD,CAAE,KAAM,OAAQ,QAAS,EAAa,QAAS,CAChD,CACD,EACA,EACA,EACA,EACD,CAED,EACE,GAAG,IAAS,EAAe,EAAO,UAAU,CAAC,uBAAuB,EAAe,EAAc,CAAC,MAAM,EAAe,EAAiB,OAAO,GAChJ,CAGD,IAAI,EAAiB,EACnB,GAAQ,YACR,EAAa,QACd,CAeD,GAZA,EAAiB,EACf,EACA,EAAa,QACd,CASG,CANY,EACd,EAAa,QACb,EACA,EACD,CAGC,MAAU,MACR,0EACD,CAGH,OAAO,GACP,EAAE,CAEJ,EAAoB,IAAI,EAAQ,YAAa,EAAoB,CAInE,IAAM,EAAoB,EACxB,EACA,EACA,EACD,CAED,EAAU,EAAQ,EAAe,CAAE,CAAE,UAAW,GAAM,CAAC,CACvD,EAAc,EAAgB,EAAkB,CAEhD,EACE,GAAG,EAAS,IAAK,EAAW,MAAM,CAAC,QAAQ,EAAW,EAAe,CAAC,gCACvE"}
1
+ {"version":3,"file":"reviewDocBlockAware.mjs","names":[],"sources":["../../../src/reviewDoc/reviewDocBlockAware.ts"],"sourcesContent":["import { mkdirSync, writeFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { readAsset } from 'utils:asset';\nimport type { AIConfig } from '@intlayer/ai';\nimport type { AIOptions } from '@intlayer/api';\nimport { formatLocale, formatPath } from '@intlayer/chokidar/utils';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport {\n colon,\n colorize,\n colorizeNumber,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { retryManager } from '@intlayer/config/utils';\nimport { getLocaleName } from '@intlayer/core/localization';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport { ENGLISH } from '@intlayer/types/locales';\nimport { sanitizeChunk, validateTranslation } from '../translateDoc/validation';\nimport {\n buildAlignmentPlan,\n mergeReviewedSegments,\n} from '../translation-alignment/pipeline';\nimport { chunkInference } from '../utils/chunkInference';\nimport { fixChunkStartEndChars } from '../utils/fixChunkStartEndChars';\nimport type { AIClient } from '../utils/setupAI';\n\n/**\n * Review a file using block-aware alignment.\n * This approach:\n * 1. Segments both English and French documents into semantic blocks\n * 2. Aligns blocks using structure (special chars, numbers) and context\n * 3. Detects which blocks changed, were added, or deleted\n * 4. Only sends changed/new blocks to AI for translation\n * 5. Handles reordering automatically\n */\nexport const reviewFileBlockAware = async (\n baseFilePath: string,\n outputFilePath: string,\n locale: Locale,\n baseLocale: Locale,\n aiOptions?: AIOptions,\n configOptions?: GetConfigurationOptions,\n customInstructions?: string,\n changedLines?: number[],\n aiClient?: AIClient,\n aiConfig?: AIConfig\n) => {\n const configuration = getConfiguration(configOptions);\n const applicationLogger = getAppLogger(configuration);\n\n const englishText = await readFile(baseFilePath, 'utf-8');\n const frenchText = await readFile(outputFilePath, 'utf-8').catch(() => '');\n\n const basePrompt = readAsset('./prompts/REVIEW_PROMPT.md', 'utf-8')\n .replaceAll('{{localeName}}', `${formatLocale(locale, false)}`)\n .replaceAll('{{baseLocaleName}}', `${formatLocale(baseLocale, false)}`)\n .replace('{{applicationContext}}', aiOptions?.applicationContext ?? '-')\n .replace('{{customInstructions}}', customInstructions ?? '-');\n\n const filePrefixText = `${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}] `;\n const filePrefix = [\n colon(filePrefixText, { colSize: 40 }),\n `→ ${ANSIColors.RESET}`,\n ].join('');\n const prefixText = `${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}][${formatLocale(locale)}${ANSIColors.GREY_DARK}] `;\n const prefix = [\n colon(prefixText, { colSize: 40 }),\n `→ ${ANSIColors.RESET}`,\n ].join('');\n\n // Build block-aware alignment and plan\n const { englishBlocks, frenchBlocks, plan, segmentsToReview } =\n buildAlignmentPlan({\n englishText,\n frenchText,\n changedLines,\n });\n\n applicationLogger(\n `${filePrefix}Block-aware alignment complete. Total blocks: EN=${colorizeNumber(englishBlocks.length)}, FR=${colorizeNumber(frenchBlocks.length)}`\n );\n applicationLogger(\n `${filePrefix}Actions: reuse=${colorizeNumber(plan.actions.filter((a) => a.kind === 'reuse').length)}, review=${colorizeNumber(plan.actions.filter((a) => a.kind === 'review').length)}, new=${colorizeNumber(plan.actions.filter((a) => a.kind === 'insert_new').length)}, delete=${colorizeNumber(plan.actions.filter((a) => a.kind === 'delete').length)}`\n );\n\n if (segmentsToReview.length === 0) {\n applicationLogger(\n `${filePrefix}No segments need review, reusing existing translation`\n );\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(\n outputFilePath,\n mergeReviewedSegments(plan, frenchBlocks, new Map())\n );\n applicationLogger(\n `${colorize('✔', ANSIColors.GREEN)} File ${formatPath(outputFilePath)} updated successfully (no changes needed).`\n );\n return;\n }\n\n applicationLogger(\n `${filePrefix}Segments to review: ${colorizeNumber(segmentsToReview.length)}`\n );\n\n // Review segments that need AI translation\n const reviewedSegmentsMap = new Map<number, string>();\n\n for (const segment of segmentsToReview) {\n const segmentNumber = segmentsToReview.indexOf(segment) + 1;\n const englishBlock = segment.englishBlock;\n\n const getBaseChunkContextPrompt = () =>\n `**BLOCK ${segmentNumber} of ${segmentsToReview.length}** is the base block in ${formatLocale(baseLocale, false)} as reference.\\n` +\n `///chunksStart///\\n` +\n englishBlock.content +\n `///chunksEnd///`;\n\n const getFrenchChunkPrompt = () =>\n `**BLOCK ${segmentNumber} of ${segmentsToReview.length}** is the current block to review in ${formatLocale(locale, false)}.\\n` +\n `///chunksStart///\\n` +\n (segment.frenchBlockText ?? '') +\n `///chunksEnd///`;\n\n const reviewedChunkResult = await retryManager(async () => {\n const result = await chunkInference(\n [\n { role: 'system', content: basePrompt },\n { role: 'system', content: getBaseChunkContextPrompt() },\n { role: 'system', content: getFrenchChunkPrompt() },\n {\n role: 'system',\n content: `The next user message will be the **BLOCK ${colorizeNumber(segmentNumber)} of ${colorizeNumber(segmentsToReview.length)}** that should be translated in ${getLocaleName(locale, ENGLISH)} (${locale}).`,\n },\n { role: 'user', content: englishBlock.content },\n ],\n aiOptions,\n configuration,\n aiClient,\n aiConfig\n );\n\n applicationLogger(\n `${prefix}${colorizeNumber(result.tokenUsed)} tokens used - Block ${colorizeNumber(segmentNumber)} of ${colorizeNumber(segmentsToReview.length)}`\n );\n\n // Sanitize artifacts (e.g. Markdown code block wrappers)\n let processedChunk = sanitizeChunk(\n result?.fileContent,\n englishBlock.content\n );\n\n // Fix start/end characters\n processedChunk = fixChunkStartEndChars(\n processedChunk,\n englishBlock.content\n );\n\n // Validate Translation (YAML, Code fences, Length ratio)\n const isValid = validateTranslation(\n englishBlock.content,\n processedChunk,\n applicationLogger\n );\n\n if (!isValid) {\n throw new Error(\n 'Validation failed for chunk (structure or length mismatch). Retrying...'\n );\n }\n\n return processedChunk;\n })();\n\n reviewedSegmentsMap.set(segment.actionIndex, reviewedChunkResult);\n }\n\n // Merge reviewed segments back into final document\n const finalFrenchOutput = mergeReviewedSegments(\n plan,\n frenchBlocks,\n reviewedSegmentsMap\n );\n\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(outputFilePath, finalFrenchOutput);\n\n applicationLogger(\n `${colorize('✔', ANSIColors.GREEN)} File ${formatPath(outputFilePath)} created/updated successfully.`\n );\n};\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{listMissingTranslations as e}from"./listMissingTranslations.mjs";import{prepareIntlayer as t}from"@intlayer/chokidar/build";import{formatLocale as n,formatPath as r}from"@intlayer/chokidar/utils";import{ANSIColors as i,colon as a,colorize as o,colorizeKey as s,colorizeNumber as c,getAppLogger as l}from"@intlayer/config/logger";import{getConfiguration as u}from"@intlayer/config/node";const d=async d=>{let f=u(d?.configOptions),{locales:p,requiredLocales:m}=f.internationalization,h=l(f);d?.build===!0?await t(f,{forceRun:!0}):d?.build===void 0&&await t(f);let g=e(d?.configOptions),_=g.missingTranslations.map(e=>` - ${e.key}`).reduce((e,t)=>Math.max(e,t.length),0),v=g.missingTranslations.map(e=>n(e.locales,!1)).reduce((e,t)=>Math.max(e,t.length),0),y=g.missingTranslations.map(e=>[a(` - ${s(e.key)}`,{colSize:_,maxSize:40}),` - `,a(n(e.locales,i.RED),{colSize:v,maxSize:40}),e.filePath?` - ${r(e.filePath)}`:``,e.id?` - remote`:``].join(``));h(`Missing translations:`,{level:`info`}),y.forEach(e=>{h(e,{level:`info`})}),h(`Locales: ${n(p)}`),h(`Required locales: ${n(m??p)}`),h(`Missing locales: ${g.missingLocales.length===0?o(`-`,i.GREEN):n(g.missingLocales,i.RED)}`),h(`Missing required locales: ${g.missingRequiredLocales.length===0?o(`-`,i.GREEN):n(g.missingRequiredLocales,i.RED)}`),h(`Total missing locales: ${c(g.missingLocales.length,{one:i.RED,other:i.RED,zero:i.GREEN})}`),h(`Total missing required locales: ${c(g.missingRequiredLocales.length,{one:i.RED,other:i.RED,zero:i.GREEN})}`),g.missingRequiredLocales.length>0&&process.exit(1)};export{d as testMissingTranslations};
1
+ import{listMissingTranslations as e}from"./listMissingTranslations.mjs";import{prepareIntlayer as t}from"@intlayer/chokidar/build";import{formatLocale as n,formatPath as r}from"@intlayer/chokidar/utils";import*as i from"@intlayer/config/colors";import{colon as a,colorize as o,colorizeKey as s,colorizeNumber as c,getAppLogger as l}from"@intlayer/config/logger";import{getConfiguration as u}from"@intlayer/config/node";const d=async d=>{let f=u(d?.configOptions),{locales:p,requiredLocales:m}=f.internationalization,h=l(f);d?.build===!0?await t(f,{forceRun:!0}):d?.build===void 0&&await t(f);let g=e(d?.configOptions),_=g.missingTranslations.map(e=>` - ${e.key}`).reduce((e,t)=>Math.max(e,t.length),0),v=g.missingTranslations.map(e=>n(e.locales,!1)).reduce((e,t)=>Math.max(e,t.length),0),y=g.missingTranslations.map(e=>[a(` - ${s(e.key)}`,{colSize:_,maxSize:40}),` - `,a(n(e.locales,i.RED),{colSize:v,maxSize:40}),e.filePath?` - ${r(e.filePath)}`:``,e.id?` - remote`:``].join(``));h(`Missing translations:`,{level:`info`}),y.forEach(e=>{h(e,{level:`info`})}),h(`Locales: ${n(p)}`),h(`Required locales: ${n(m??p)}`),h(`Missing locales: ${g.missingLocales.length===0?o(`-`,i.GREEN):n(g.missingLocales,i.RED)}`),h(`Missing required locales: ${g.missingRequiredLocales.length===0?o(`-`,i.GREEN):n(g.missingRequiredLocales,i.RED)}`),h(`Total missing locales: ${c(g.missingLocales.length,{one:i.RED,other:i.RED,zero:i.GREEN})}`),h(`Total missing required locales: ${c(g.missingRequiredLocales.length,{one:i.RED,other:i.RED,zero:i.GREEN})}`),g.missingRequiredLocales.length>0&&process.exit(1)};export{d as testMissingTranslations};
2
2
  //# sourceMappingURL=test.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"test.mjs","names":[],"sources":["../../../src/test/test.ts"],"sourcesContent":["import { prepareIntlayer } from '@intlayer/chokidar/build';\nimport { formatLocale, formatPath } from '@intlayer/chokidar/utils';\nimport {\n ANSIColors,\n colon,\n colorize,\n colorizeKey,\n colorizeNumber,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { listMissingTranslations } from './listMissingTranslations';\n\ntype ListMissingTranslationsOptions = {\n configOptions?: GetConfigurationOptions;\n build?: boolean;\n};\n\nexport const testMissingTranslations = async (\n options?: ListMissingTranslationsOptions\n) => {\n const config = getConfiguration(options?.configOptions);\n const { locales, requiredLocales } = config.internationalization;\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 const result = listMissingTranslations(options?.configOptions);\n\n const maxKeyColSize = result.missingTranslations\n .map((t) => ` - ${t.key}`)\n .reduce((max, t) => Math.max(max, t.length), 0);\n const maxLocalesColSize = result.missingTranslations\n .map((t) => formatLocale(t.locales, false))\n .reduce((max, t) => Math.max(max, t.length), 0);\n\n const formattedMissingTranslations = result.missingTranslations.map(\n (translation) =>\n [\n colon(` - ${colorizeKey(translation.key)}`, {\n colSize: maxKeyColSize,\n maxSize: 40,\n }),\n ' - ',\n colon(formatLocale(translation.locales, ANSIColors.RED), {\n colSize: maxLocalesColSize,\n maxSize: 40,\n }),\n\n translation.filePath ? ` - ${formatPath(translation.filePath)}` : '',\n translation.id ? ' - remote' : '',\n ].join('')\n );\n\n appLogger(`Missing translations:`, {\n level: 'info',\n });\n\n formattedMissingTranslations.forEach((t) => {\n appLogger(t, {\n level: 'info',\n });\n });\n\n appLogger(`Locales: ${formatLocale(locales)}`);\n appLogger(`Required locales: ${formatLocale(requiredLocales ?? locales)}`);\n appLogger(\n `Missing locales: ${result.missingLocales.length === 0 ? colorize('-', ANSIColors.GREEN) : formatLocale(result.missingLocales, ANSIColors.RED)}`\n );\n\n appLogger(\n `Missing required locales: ${result.missingRequiredLocales.length === 0 ? colorize('-', ANSIColors.GREEN) : formatLocale(result.missingRequiredLocales, ANSIColors.RED)}`\n );\n appLogger(\n `Total missing locales: ${colorizeNumber(result.missingLocales.length, {\n one: ANSIColors.RED,\n other: ANSIColors.RED,\n zero: ANSIColors.GREEN,\n })}`\n );\n appLogger(\n `Total missing required locales: ${colorizeNumber(\n result.missingRequiredLocales.length,\n {\n one: ANSIColors.RED,\n other: ANSIColors.RED,\n zero: ANSIColors.GREEN,\n }\n )}`\n );\n\n if (result.missingRequiredLocales.length > 0) {\n process.exit(1);\n }\n};\n"],"mappings":"yYAqBA,MAAa,EAA0B,KACrC,IACG,CACH,IAAM,EAAS,EAAiB,GAAS,cAAc,CACjD,CAAE,UAAS,mBAAoB,EAAO,qBAEtC,EAAY,EAAa,EAAO,CAElC,GAAS,QAAU,GACrB,MAAM,EAAgB,EAAQ,CAAE,SAAU,GAAM,CAAC,CACjC,GAAS,QAAU,QACnC,MAAM,EAAgB,EAAO,CAG/B,IAAM,EAAS,EAAwB,GAAS,cAAc,CAExD,EAAgB,EAAO,oBAC1B,IAAK,GAAM,MAAM,EAAE,MAAM,CACzB,QAAQ,EAAK,IAAM,KAAK,IAAI,EAAK,EAAE,OAAO,CAAE,EAAE,CAC3C,EAAoB,EAAO,oBAC9B,IAAK,GAAM,EAAa,EAAE,QAAS,GAAM,CAAC,CAC1C,QAAQ,EAAK,IAAM,KAAK,IAAI,EAAK,EAAE,OAAO,CAAE,EAAE,CAE3C,EAA+B,EAAO,oBAAoB,IAC7D,GACC,CACE,EAAM,MAAM,EAAY,EAAY,IAAI,GAAI,CAC1C,QAAS,EACT,QAAS,GACV,CAAC,CACF,MACA,EAAM,EAAa,EAAY,QAAS,EAAW,IAAI,CAAE,CACvD,QAAS,EACT,QAAS,GACV,CAAC,CAEF,EAAY,SAAW,MAAM,EAAW,EAAY,SAAS,GAAK,GAClE,EAAY,GAAK,YAAc,GAChC,CAAC,KAAK,GAAG,CACb,CAED,EAAU,wBAAyB,CACjC,MAAO,OACR,CAAC,CAEF,EAA6B,QAAS,GAAM,CAC1C,EAAU,EAAG,CACX,MAAO,OACR,CAAC,EACF,CAEF,EAAU,YAAY,EAAa,EAAQ,GAAG,CAC9C,EAAU,qBAAqB,EAAa,GAAmB,EAAQ,GAAG,CAC1E,EACE,oBAAoB,EAAO,eAAe,SAAW,EAAI,EAAS,IAAK,EAAW,MAAM,CAAG,EAAa,EAAO,eAAgB,EAAW,IAAI,GAC/I,CAED,EACE,6BAA6B,EAAO,uBAAuB,SAAW,EAAI,EAAS,IAAK,EAAW,MAAM,CAAG,EAAa,EAAO,uBAAwB,EAAW,IAAI,GACxK,CACD,EACE,0BAA0B,EAAe,EAAO,eAAe,OAAQ,CACrE,IAAK,EAAW,IAChB,MAAO,EAAW,IAClB,KAAM,EAAW,MAClB,CAAC,GACH,CACD,EACE,mCAAmC,EACjC,EAAO,uBAAuB,OAC9B,CACE,IAAK,EAAW,IAChB,MAAO,EAAW,IAClB,KAAM,EAAW,MAClB,CACF,GACF,CAEG,EAAO,uBAAuB,OAAS,GACzC,QAAQ,KAAK,EAAE"}
1
+ {"version":3,"file":"test.mjs","names":[],"sources":["../../../src/test/test.ts"],"sourcesContent":["import { prepareIntlayer } from '@intlayer/chokidar/build';\nimport { formatLocale, formatPath } from '@intlayer/chokidar/utils';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport {\n colon,\n colorize,\n colorizeKey,\n colorizeNumber,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { listMissingTranslations } from './listMissingTranslations';\n\ntype ListMissingTranslationsOptions = {\n configOptions?: GetConfigurationOptions;\n build?: boolean;\n};\n\nexport const testMissingTranslations = async (\n options?: ListMissingTranslationsOptions\n) => {\n const config = getConfiguration(options?.configOptions);\n const { locales, requiredLocales } = config.internationalization;\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 const result = listMissingTranslations(options?.configOptions);\n\n const maxKeyColSize = result.missingTranslations\n .map((t) => ` - ${t.key}`)\n .reduce((max, t) => Math.max(max, t.length), 0);\n const maxLocalesColSize = result.missingTranslations\n .map((t) => formatLocale(t.locales, false))\n .reduce((max, t) => Math.max(max, t.length), 0);\n\n const formattedMissingTranslations = result.missingTranslations.map(\n (translation) =>\n [\n colon(` - ${colorizeKey(translation.key)}`, {\n colSize: maxKeyColSize,\n maxSize: 40,\n }),\n ' - ',\n colon(formatLocale(translation.locales, ANSIColors.RED), {\n colSize: maxLocalesColSize,\n maxSize: 40,\n }),\n\n translation.filePath ? ` - ${formatPath(translation.filePath)}` : '',\n translation.id ? ' - remote' : '',\n ].join('')\n );\n\n appLogger(`Missing translations:`, {\n level: 'info',\n });\n\n formattedMissingTranslations.forEach((t) => {\n appLogger(t, {\n level: 'info',\n });\n });\n\n appLogger(`Locales: ${formatLocale(locales)}`);\n appLogger(`Required locales: ${formatLocale(requiredLocales ?? locales)}`);\n appLogger(\n `Missing locales: ${result.missingLocales.length === 0 ? colorize('-', ANSIColors.GREEN) : formatLocale(result.missingLocales, ANSIColors.RED)}`\n );\n\n appLogger(\n `Missing required locales: ${result.missingRequiredLocales.length === 0 ? colorize('-', ANSIColors.GREEN) : formatLocale(result.missingRequiredLocales, ANSIColors.RED)}`\n );\n appLogger(\n `Total missing locales: ${colorizeNumber(result.missingLocales.length, {\n one: ANSIColors.RED,\n other: ANSIColors.RED,\n zero: ANSIColors.GREEN,\n })}`\n );\n appLogger(\n `Total missing required locales: ${colorizeNumber(\n result.missingRequiredLocales.length,\n {\n one: ANSIColors.RED,\n other: ANSIColors.RED,\n zero: ANSIColors.GREEN,\n }\n )}`\n );\n\n if (result.missingRequiredLocales.length > 0) {\n process.exit(1);\n }\n};\n"],"mappings":"maAqBA,MAAa,EAA0B,KACrC,IACG,CACH,IAAM,EAAS,EAAiB,GAAS,cAAc,CACjD,CAAE,UAAS,mBAAoB,EAAO,qBAEtC,EAAY,EAAa,EAAO,CAElC,GAAS,QAAU,GACrB,MAAM,EAAgB,EAAQ,CAAE,SAAU,GAAM,CAAC,CACjC,GAAS,QAAU,QACnC,MAAM,EAAgB,EAAO,CAG/B,IAAM,EAAS,EAAwB,GAAS,cAAc,CAExD,EAAgB,EAAO,oBAC1B,IAAK,GAAM,MAAM,EAAE,MAAM,CACzB,QAAQ,EAAK,IAAM,KAAK,IAAI,EAAK,EAAE,OAAO,CAAE,EAAE,CAC3C,EAAoB,EAAO,oBAC9B,IAAK,GAAM,EAAa,EAAE,QAAS,GAAM,CAAC,CAC1C,QAAQ,EAAK,IAAM,KAAK,IAAI,EAAK,EAAE,OAAO,CAAE,EAAE,CAE3C,EAA+B,EAAO,oBAAoB,IAC7D,GACC,CACE,EAAM,MAAM,EAAY,EAAY,IAAI,GAAI,CAC1C,QAAS,EACT,QAAS,GACV,CAAC,CACF,MACA,EAAM,EAAa,EAAY,QAAS,EAAW,IAAI,CAAE,CACvD,QAAS,EACT,QAAS,GACV,CAAC,CAEF,EAAY,SAAW,MAAM,EAAW,EAAY,SAAS,GAAK,GAClE,EAAY,GAAK,YAAc,GAChC,CAAC,KAAK,GAAG,CACb,CAED,EAAU,wBAAyB,CACjC,MAAO,OACR,CAAC,CAEF,EAA6B,QAAS,GAAM,CAC1C,EAAU,EAAG,CACX,MAAO,OACR,CAAC,EACF,CAEF,EAAU,YAAY,EAAa,EAAQ,GAAG,CAC9C,EAAU,qBAAqB,EAAa,GAAmB,EAAQ,GAAG,CAC1E,EACE,oBAAoB,EAAO,eAAe,SAAW,EAAI,EAAS,IAAK,EAAW,MAAM,CAAG,EAAa,EAAO,eAAgB,EAAW,IAAI,GAC/I,CAED,EACE,6BAA6B,EAAO,uBAAuB,SAAW,EAAI,EAAS,IAAK,EAAW,MAAM,CAAG,EAAa,EAAO,uBAAwB,EAAW,IAAI,GACxK,CACD,EACE,0BAA0B,EAAe,EAAO,eAAe,OAAQ,CACrE,IAAK,EAAW,IAChB,MAAO,EAAW,IAClB,KAAM,EAAW,MAClB,CAAC,GACH,CACD,EACE,mCAAmC,EACjC,EAAO,uBAAuB,OAC9B,CACE,IAAK,EAAW,IAChB,MAAO,EAAW,IAClB,KAAM,EAAW,MAClB,CACF,GACF,CAEG,EAAO,uBAAuB,OAAS,GACzC,QAAQ,KAAK,EAAE"}
@@ -1,2 +1,2 @@
1
- import{setupAI as e}from"../utils/setupAI.mjs";import{checkFileModifiedRange as t}from"../utils/checkFileModifiedRange.mjs";import{getOutputFilePath as n}from"../utils/getOutputFilePath.mjs";import"../_utils_asset-B187VPMw.mjs";import{translateFile as r}from"./translateFile.mjs";import{existsSync as i,mkdirSync as a,writeFileSync as o}from"node:fs";import{dirname as s,join as c}from"node:path";import{listGitFiles as l,logConfigDetails as u}from"@intlayer/chokidar/cli";import{pLimit as d,parallelize as f}from"@intlayer/chokidar/utils";import{ANSIColors as p,colorize as m,colorizeNumber as h,getAppLogger as g}from"@intlayer/config/logger";import{getConfiguration as _}from"@intlayer/config/node";import v from"fast-glob";import{performance as y}from"node:perf_hooks";const b=async({docPattern:b,locales:x,excludedGlobPattern:S,baseLocale:C,aiOptions:w,nbSimultaneousFileProcessed:T=20,configOptions:E,customInstructions:D,skipIfModifiedBefore:O,skipIfModifiedAfter:k,skipIfExists:A,gitOptions:j,flushStrategy:M=`incremental`})=>{let N=_(E);u(E);let P=g(N),F=T,I=d(F),L=await v(b,{ignore:S}),R=await e(N,w);if(!R?.hasAIAccess)return;let{aiClient:z,aiConfig:B}=R;if(j){let e=await l(j);e&&(L=L.filter(t=>e.some(e=>c(process.cwd(),t)===e)))}let V=y.now();P(`Translating ${h(L.length)} files to ${h(x.length)} locales. \nGlobal Concurrency: ${h(F)} chunks in parallel.`);let H={count:0,maxErrors:5,shouldStop:!1};await f(L.flatMap(e=>x.map(l=>async()=>{if(H.shouldStop)return;let u=c(N.system.baseDir,e),d=n(u,l,C);if(A&&i(d))return;M===`incremental`&&!i(d)&&(a(s(d),{recursive:!0}),o(d,``));let f=t(d,{skipIfModifiedBefore:O,skipIfModifiedAfter:k});if(f.isSkipped){P(f.message);return}await r({baseFilePath:u,outputFilePath:d,locale:l,baseLocale:C,configuration:N,errorState:H,aiOptions:w,customInstructions:D,aiClient:z,aiConfig:B,flushStrategy:M,limit:I})})),e=>e(),50);let U=((y.now()-V)/1e3).toFixed(2);H.count>0?P(`Finished with ${H.count} errors in ${U}s.`):P(`${m(`✔`,p.GREEN)} Batch completed successfully in ${h(U)}s.`)};export{b as translateDoc};
1
+ import{setupAI as e}from"../utils/setupAI.mjs";import{checkFileModifiedRange as t}from"../utils/checkFileModifiedRange.mjs";import{getOutputFilePath as n}from"../utils/getOutputFilePath.mjs";import"../_utils_asset-B187VPMw.mjs";import{translateFile as r}from"./translateFile.mjs";import{existsSync as i,mkdirSync as a,writeFileSync as o}from"node:fs";import{dirname as s,join as c}from"node:path";import{listGitFiles as l,logConfigDetails as u}from"@intlayer/chokidar/cli";import{pLimit as d,parallelize as f}from"@intlayer/chokidar/utils";import*as p from"@intlayer/config/colors";import{colorize as m,colorizeNumber as h,getAppLogger as g}from"@intlayer/config/logger";import{getConfiguration as _}from"@intlayer/config/node";import v from"fast-glob";import{performance as y}from"node:perf_hooks";const b=async({docPattern:b,locales:x,excludedGlobPattern:S,baseLocale:C,aiOptions:w,nbSimultaneousFileProcessed:T=20,configOptions:E,customInstructions:D,skipIfModifiedBefore:O,skipIfModifiedAfter:k,skipIfExists:A,gitOptions:j,flushStrategy:M=`incremental`})=>{let N=_(E);u(E);let P=g(N),F=T,I=d(F),L=await v(b,{ignore:S}),R=await e(N,w);if(!R?.hasAIAccess)return;let{aiClient:z,aiConfig:B}=R;if(j){let e=await l(j);e&&(L=L.filter(t=>e.some(e=>c(process.cwd(),t)===e)))}let V=y.now();P(`Translating ${h(L.length)} files to ${h(x.length)} locales. \nGlobal Concurrency: ${h(F)} chunks in parallel.`);let H={count:0,maxErrors:5,shouldStop:!1};await f(L.flatMap(e=>x.map(l=>async()=>{if(H.shouldStop)return;let u=c(N.system.baseDir,e),d=n(u,l,C);if(A&&i(d))return;M===`incremental`&&!i(d)&&(a(s(d),{recursive:!0}),o(d,``));let f=t(d,{skipIfModifiedBefore:O,skipIfModifiedAfter:k});if(f.isSkipped){P(f.message);return}await r({baseFilePath:u,outputFilePath:d,locale:l,baseLocale:C,configuration:N,errorState:H,aiOptions:w,customInstructions:D,aiClient:z,aiConfig:B,flushStrategy:M,limit:I})})),e=>e(),50);let U=((y.now()-V)/1e3).toFixed(2);H.count>0?P(`Finished with ${H.count} errors in ${U}s.`):P(`${m(`✔`,p.GREEN)} Batch completed successfully in ${h(U)}s.`)};export{b as translateDoc};
2
2
  //# sourceMappingURL=translateDoc.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"translateDoc.mjs","names":[],"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/allLocales';\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.system.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":"qwBAoBA,MAAa,EAAe,MAAO,CACjC,aACA,UACA,sBACA,aACA,YACA,8BAA8B,GAC9B,gBACA,qBACA,uBACA,sBACA,eACA,aACA,gBAAgB,iBACS,CACzB,IAAM,EAAgB,EAAiB,EAAc,CACrD,EAAiB,EAAc,CAE/B,IAAM,EAAY,EAAa,EAAc,CAKvC,EAAsB,EACtB,EAAqB,EAAO,EAAoB,CAElD,EAAoB,MAAM,EAAG,EAAY,CAC3C,OAAQ,EACT,CAAC,CAEI,EAAW,MAAM,EAAQ,EAAe,EAAU,CACxD,GAAI,CAAC,GAAU,YAAa,OAC5B,GAAM,CAAE,WAAU,YAAa,EAE/B,GAAI,EAAY,CACd,IAAM,EAAkB,MAAM,EAAa,EAAW,CAClD,IACF,EAAU,EAAQ,OAAQ,GACxB,EAAgB,KAAM,GAAY,EAAK,QAAQ,KAAK,CAAE,EAAK,GAAK,EAAQ,CACzE,EAIL,IAAM,EAAiB,EAAY,KAAK,CAExC,EACE,eAAe,EAAe,EAAQ,OAAO,CAAC,YAAY,EAAe,EAAQ,OAAO,CAAC,kCAChE,EAAe,EAAoB,CAAC,sBAC9D,CAED,IAAM,EAAyB,CAC7B,MAAO,EACP,UAAW,EACX,WAAY,GACb,CAyDD,MAAM,EArDW,EAAQ,QAAS,GAChC,EAAQ,IAAK,GAAW,SAAY,CAClC,GAAI,EAAW,WAAY,OAE3B,IAAM,EAAuB,EAAK,EAAc,OAAO,QAAS,EAAQ,CAClE,EAAiB,EACrB,EACA,EACA,EACD,CAGD,GAAI,GAAgB,EAAW,EAAe,CAAE,OAE5C,IAAkB,eAAiB,CAAC,EAAW,EAAe,GAChE,EAAU,EAAQ,EAAe,CAAE,CAAE,UAAW,GAAM,CAAC,CACvD,EAAc,EAAgB,GAAG,EAGnC,IAAM,EAAuB,EAAuB,EAAgB,CAClE,uBACA,sBACD,CAAC,CAEF,GAAI,EAAqB,UAAW,CAClC,EAAU,EAAqB,QAAQ,CACvC,OAIF,MAAM,EAAc,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,IADe,EAAY,KAAK,CACC,GAAkB,KAAM,QAAQ,EAAE,CAErE,EAAW,MAAQ,EACrB,EAAU,iBAAiB,EAAW,MAAM,aAAa,EAAc,IAAI,CAE3E,EACE,GAAG,EAAS,IAAK,EAAW,MAAM,CAAC,mCAAmC,EAAe,EAAc,CAAC,IACrG"}
1
+ {"version":3,"file":"translateDoc.mjs","names":[],"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 * as ANSIColors from '@intlayer/config/colors';\nimport {\n colorize,\n colorizeNumber,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport type { Locale } from '@intlayer/types/allLocales';\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.system.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":"+xBAoBA,MAAa,EAAe,MAAO,CACjC,aACA,UACA,sBACA,aACA,YACA,8BAA8B,GAC9B,gBACA,qBACA,uBACA,sBACA,eACA,aACA,gBAAgB,iBACS,CACzB,IAAM,EAAgB,EAAiB,EAAc,CACrD,EAAiB,EAAc,CAE/B,IAAM,EAAY,EAAa,EAAc,CAKvC,EAAsB,EACtB,EAAqB,EAAO,EAAoB,CAElD,EAAoB,MAAM,EAAG,EAAY,CAC3C,OAAQ,EACT,CAAC,CAEI,EAAW,MAAM,EAAQ,EAAe,EAAU,CACxD,GAAI,CAAC,GAAU,YAAa,OAC5B,GAAM,CAAE,WAAU,YAAa,EAE/B,GAAI,EAAY,CACd,IAAM,EAAkB,MAAM,EAAa,EAAW,CAClD,IACF,EAAU,EAAQ,OAAQ,GACxB,EAAgB,KAAM,GAAY,EAAK,QAAQ,KAAK,CAAE,EAAK,GAAK,EAAQ,CACzE,EAIL,IAAM,EAAiB,EAAY,KAAK,CAExC,EACE,eAAe,EAAe,EAAQ,OAAO,CAAC,YAAY,EAAe,EAAQ,OAAO,CAAC,kCAChE,EAAe,EAAoB,CAAC,sBAC9D,CAED,IAAM,EAAyB,CAC7B,MAAO,EACP,UAAW,EACX,WAAY,GACb,CAyDD,MAAM,EArDW,EAAQ,QAAS,GAChC,EAAQ,IAAK,GAAW,SAAY,CAClC,GAAI,EAAW,WAAY,OAE3B,IAAM,EAAuB,EAAK,EAAc,OAAO,QAAS,EAAQ,CAClE,EAAiB,EACrB,EACA,EACA,EACD,CAGD,GAAI,GAAgB,EAAW,EAAe,CAAE,OAE5C,IAAkB,eAAiB,CAAC,EAAW,EAAe,GAChE,EAAU,EAAQ,EAAe,CAAE,CAAE,UAAW,GAAM,CAAC,CACvD,EAAc,EAAgB,GAAG,EAGnC,IAAM,EAAuB,EAAuB,EAAgB,CAClE,uBACA,sBACD,CAAC,CAEF,GAAI,EAAqB,UAAW,CAClC,EAAU,EAAqB,QAAQ,CACvC,OAIF,MAAM,EAAc,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,IADe,EAAY,KAAK,CACC,GAAkB,KAAM,QAAQ,EAAE,CAErE,EAAW,MAAQ,EACrB,EAAU,iBAAiB,EAAW,MAAM,aAAa,EAAc,IAAI,CAE3E,EACE,GAAG,EAAS,IAAK,EAAW,MAAM,CAAC,mCAAmC,EAAe,EAAc,CAAC,IACrG"}
@@ -1,3 +1,3 @@
1
- import{t as e}from"../_utils_asset-B187VPMw.mjs";import{sanitizeChunk as t,validateTranslation as n}from"./validation.mjs";import{chunkInference as r}from"../utils/chunkInference.mjs";import{fixChunkStartEndChars as i}from"../utils/fixChunkStartEndChars.mjs";import{chunkText as a}from"../utils/calculateChunks.mjs";import{mkdirSync as o,writeFileSync as s}from"node:fs";import{dirname as c,relative as l}from"node:path";import{formatLocale as u,formatPath as d}from"@intlayer/chokidar/utils";import{ANSIColors as f,colon as p,colorize as m,colorizeNumber as h,getAppLogger as g}from"@intlayer/config/logger";import{retryManager as _}from"@intlayer/config/utils";import{readFile as v}from"node:fs/promises";import{performance as y}from"node:perf_hooks";const b=async({baseFilePath:b,outputFilePath:x,locale:S,baseLocale:C,configuration:w,errorState:T,aiOptions:E,customInstructions:D,aiClient:O,aiConfig:k,flushStrategy:A=`incremental`,onChunkReceive:j,limit:M})=>{if(T.shouldStop)return null;let N=g(w,{config:{prefix:``}}),P=y.now();try{let F=a(await v(b,`utf-8`)),I=F.length,L=`${p(`${f.GREY_DARK}[${d(b)}${f.GREY_DARK}] `,{colSize:40})}${f.RESET}`,R=`${p(`${f.GREY_DARK}[${d(b)}${f.GREY_DARK}][${u(S)}${f.GREY_DARK}] `,{colSize:40})}${f.RESET}`;N(`${L}Split into ${h(I)} chunks. Queuing...`);let z=e(`./prompts/TRANSLATE_PROMPT.md`,`utf-8`).replaceAll(`{{localeName}}`,`${u(S,!1)}`).replaceAll(`{{baseLocaleName}}`,`${u(C,!1)}`).replace(`{{applicationContext}}`,E?.applicationContext??`-`).replace(`{{customInstructions}}`,D??`-`),B=Array(I).fill(``),V=M??(e=>e()),H=F.map((e,a)=>V(async()=>{if(T.shouldStop)return null;let l=g(w,{config:{prefix:`${R} ${f.GREY_DARK}[${a+1}/${I}] ${f.RESET}`}}),u=y.now(),d=a===0,p=e.content,m=()=>">>> CONTEXT: PREVIOUS SOURCE CONTENT <<<\n```\n"+(F[a-1]?.content??``)+"\n```\n>>> END PREVIOUS CONTEXT <<<",v=()=>">>> CONTEXT: NEXT CONTENT <<<\n```\n"+(F[a+1]?.content??``)+"\n```\n>>> END NEXT CONTEXT <<<";l(`Process started`);let{content:b,tokens:S}=await _(async()=>{let e=await r([{role:`system`,content:z},...F[a+1]?[{role:`system`,content:v()}]:[],...d?[]:[{role:`system`,content:m()}],{role:`system`,content:[`You are translating TARGET CHUNK (${a+1}/${I}).`,`Translate ONLY the target chunk. Preserve frontmatter/code exactly.`].join(`
1
+ import{t as e}from"../_utils_asset-B187VPMw.mjs";import{sanitizeChunk as t,validateTranslation as n}from"./validation.mjs";import{chunkInference as r}from"../utils/chunkInference.mjs";import{fixChunkStartEndChars as i}from"../utils/fixChunkStartEndChars.mjs";import{chunkText as a}from"../utils/calculateChunks.mjs";import{mkdirSync as o,writeFileSync as s}from"node:fs";import{dirname as c,relative as l}from"node:path";import{formatLocale as u,formatPath as d}from"@intlayer/chokidar/utils";import*as f from"@intlayer/config/colors";import{colon as p,colorize as m,colorizeNumber as h,getAppLogger as g}from"@intlayer/config/logger";import{retryManager as _}from"@intlayer/config/utils";import{readFile as v}from"node:fs/promises";import{performance as y}from"node:perf_hooks";const b=async({baseFilePath:b,outputFilePath:x,locale:S,baseLocale:C,configuration:w,errorState:T,aiOptions:E,customInstructions:D,aiClient:O,aiConfig:k,flushStrategy:A=`incremental`,onChunkReceive:j,limit:M})=>{if(T.shouldStop)return null;let N=g(w,{config:{prefix:``}}),P=y.now();try{let F=a(await v(b,`utf-8`)),I=F.length,L=`${p(`${f.GREY_DARK}[${d(b)}${f.GREY_DARK}] `,{colSize:40})}${f.RESET}`,R=`${p(`${f.GREY_DARK}[${d(b)}${f.GREY_DARK}][${u(S)}${f.GREY_DARK}] `,{colSize:40})}${f.RESET}`;N(`${L}Split into ${h(I)} chunks. Queuing...`);let z=e(`./prompts/TRANSLATE_PROMPT.md`,`utf-8`).replaceAll(`{{localeName}}`,`${u(S,!1)}`).replaceAll(`{{baseLocaleName}}`,`${u(C,!1)}`).replace(`{{applicationContext}}`,E?.applicationContext??`-`).replace(`{{customInstructions}}`,D??`-`),B=Array(I).fill(``),V=M??(e=>e()),H=F.map((e,a)=>V(async()=>{if(T.shouldStop)return null;let l=g(w,{config:{prefix:`${R} ${f.GREY_DARK}[${a+1}/${I}] ${f.RESET}`}}),u=y.now(),d=a===0,p=e.content,m=()=>">>> CONTEXT: PREVIOUS SOURCE CONTENT <<<\n```\n"+(F[a-1]?.content??``)+"\n```\n>>> END PREVIOUS CONTEXT <<<",v=()=>">>> CONTEXT: NEXT CONTENT <<<\n```\n"+(F[a+1]?.content??``)+"\n```\n>>> END NEXT CONTEXT <<<";l(`Process started`);let{content:b,tokens:S}=await _(async()=>{let e=await r([{role:`system`,content:z},...F[a+1]?[{role:`system`,content:v()}]:[],...d?[]:[{role:`system`,content:m()}],{role:`system`,content:[`You are translating TARGET CHUNK (${a+1}/${I}).`,`Translate ONLY the target chunk. Preserve frontmatter/code exactly.`].join(`
2
2
  `)},{role:`user`,content:`>>> TARGET CHUNK START <<<\n${p}\n>>> TARGET CHUNK END <<<`}],E,w,O,k),o=t(e?.fileContent,p);if(o=i(o,p),!n(p,o,l))throw Error(`Validation failed for chunk ${a+1}/${I}`);return{content:o,tokens:e.tokenUsed}})(),C=(y.now()-u).toFixed(0);if(B[a]=b,j&&j(b,a,I),A===`incremental`&&B.slice(0,a+1).every(e=>e&&e!==``)){let e=0;for(;e<I&&B[e]&&B[e]!==``;)e++;let t=B.slice(0,e).join(``);o(c(x),{recursive:!0}),s(x,t)}l([`${h(S)} tokens used `,`${f.GREY_DARK}in ${h(C)}ms${f.RESET}`].join(``))}));await Promise.all(H);let U=B.join(``);(A===`end`||A===`incremental`)&&(o(c(x),{recursive:!0}),s(x,U));let W=((y.now()-P)/1e3).toFixed(2),G=l(w.system.baseDir,x);return N(`${m(`✔`,f.GREEN)} File ${d(G)} completed in ${h(W)}s.`),U}catch(e){T.count++;let t=e?.message??JSON.stringify(e);return N(`${m(`✖`,f.RED)} Error: ${t}`),T.count>=T.maxErrors&&(T.shouldStop=!0),null}};export{b as translateFile};
3
3
  //# sourceMappingURL=translateFile.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"translateFile.mjs","names":[],"sources":["../../../src/translateDoc/translateFile.ts"],"sourcesContent":["import { mkdirSync, writeFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { dirname, relative } from 'node:path';\nimport { performance } from 'node:perf_hooks';\nimport { readAsset } from 'utils:asset';\nimport { formatLocale, formatPath } from '@intlayer/chokidar/utils';\nimport {\n ANSIColors,\n colon,\n colorize,\n colorizeNumber,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport { retryManager } from '@intlayer/config/utils';\nimport { chunkText } from '../utils/calculateChunks';\nimport { chunkInference } from '../utils/chunkInference';\nimport { fixChunkStartEndChars } from '../utils/fixChunkStartEndChars';\nimport type { TranslateFileOptions } from './types';\nimport { sanitizeChunk, validateTranslation } from './validation';\n\nexport const translateFile = async ({\n baseFilePath,\n outputFilePath,\n locale,\n baseLocale,\n configuration,\n errorState,\n aiOptions,\n customInstructions,\n aiClient,\n aiConfig,\n flushStrategy = 'incremental',\n onChunkReceive,\n limit, // The Global Limiter\n}: TranslateFileOptions): Promise<string | null> => {\n if (errorState.shouldStop) return null;\n\n const appLogger = getAppLogger(configuration, { config: { prefix: '' } });\n const fileStartTime = performance.now();\n\n try {\n const fileContent = await readFile(baseFilePath, 'utf-8');\n const chunks = chunkText(fileContent);\n const totalChunks = chunks.length;\n\n const filePrefixText = `${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}] `;\n const filePrefix = `${colon(filePrefixText, { colSize: 40 })}${ANSIColors.RESET}`;\n const prefixText = `${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}][${formatLocale(locale)}${ANSIColors.GREY_DARK}] `;\n const prefix = `${colon(prefixText, { colSize: 40 })}${ANSIColors.RESET}`;\n\n appLogger(\n `${filePrefix}Split into ${colorizeNumber(totalChunks)} chunks. Queuing...`\n );\n\n const basePrompt = readAsset('./prompts/TRANSLATE_PROMPT.md', 'utf-8')\n .replaceAll('{{localeName}}', `${formatLocale(locale, false)}`)\n .replaceAll('{{baseLocaleName}}', `${formatLocale(baseLocale, false)}`)\n .replace('{{applicationContext}}', aiOptions?.applicationContext ?? '-')\n .replace('{{customInstructions}}', customInstructions ?? '-');\n\n const translatedParts: string[] = new Array(totalChunks).fill('');\n\n // Fallback if no limiter is provided (runs immediately)\n const runTask = limit ?? ((fn) => fn());\n\n // MAP CHUNKS TO GLOBAL TASKS\n // This pushes ALL chunks for this file into the Global Queue immediately.\n // They will execute whenever the global concurrency slots open up.\n const tasks = chunks.map((chunk, i) =>\n runTask(async () => {\n if (errorState.shouldStop) return null;\n\n const chunkLogger = getAppLogger(configuration, {\n config: {\n prefix: `${prefix} ${ANSIColors.GREY_DARK}[${i + 1}/${totalChunks}] ${ANSIColors.RESET}`,\n },\n });\n\n const chunkStartTime = performance.now();\n const isFirstChunk = i === 0;\n const fileToTranslateCurrentChunk = chunk.content;\n\n // Context Preparation\n const getPrevChunkPrompt = () =>\n `>>> CONTEXT: PREVIOUS SOURCE CONTENT <<<\\n\\`\\`\\`\\n` +\n (chunks[i - 1]?.content ?? '') +\n `\\n\\`\\`\\`\\n>>> END PREVIOUS CONTEXT <<<`;\n\n const getBaseChunkContextPrompt = () =>\n `>>> CONTEXT: NEXT CONTENT <<<\\n\\`\\`\\`\\n` +\n (chunks[i + 1]?.content ?? '') +\n `\\n\\`\\`\\`\\n>>> END NEXT CONTEXT <<<`;\n\n chunkLogger('Process started');\n\n const chunkTranslation = retryManager(async () => {\n const result = await chunkInference(\n [\n { role: 'system', content: basePrompt },\n ...(chunks[i + 1]\n ? [\n {\n role: 'system',\n content: getBaseChunkContextPrompt(),\n } as const,\n ]\n : []),\n ...(isFirstChunk\n ? []\n : [{ role: 'system', content: getPrevChunkPrompt() } as const]),\n {\n role: 'system',\n content: [\n `You are translating TARGET CHUNK (${i + 1}/${totalChunks}).`,\n `Translate ONLY the target chunk. Preserve frontmatter/code exactly.`,\n ].join('\\n'),\n },\n {\n role: 'user',\n content: `>>> TARGET CHUNK START <<<\\n${fileToTranslateCurrentChunk}\\n>>> TARGET CHUNK END <<<`,\n },\n ],\n aiOptions,\n configuration,\n aiClient,\n aiConfig\n );\n\n let processedChunk = sanitizeChunk(\n result?.fileContent,\n fileToTranslateCurrentChunk\n );\n processedChunk = fixChunkStartEndChars(\n processedChunk,\n fileToTranslateCurrentChunk\n );\n\n const isValid = validateTranslation(\n fileToTranslateCurrentChunk,\n processedChunk,\n chunkLogger\n );\n\n if (!isValid) {\n // Throwing an error here signals retryManager to try again\n throw new Error(\n `Validation failed for chunk ${i + 1}/${totalChunks}`\n );\n }\n\n return { content: processedChunk, tokens: result.tokenUsed };\n });\n\n const { content: translatedChunk, tokens } = await chunkTranslation();\n const chunkEndTime = performance.now();\n const chunkDuration = (chunkEndTime - chunkStartTime).toFixed(0);\n\n // Store Result\n translatedParts[i] = translatedChunk;\n\n if (onChunkReceive) {\n onChunkReceive(translatedChunk, i, totalChunks);\n }\n\n // Incremental Flush Strategy\n if (flushStrategy === 'incremental') {\n const isContiguous = translatedParts\n .slice(0, i + 1)\n .every((p) => p && p !== '');\n\n if (isContiguous) {\n let endIdx = 0;\n while (\n endIdx < totalChunks &&\n translatedParts[endIdx] &&\n translatedParts[endIdx] !== ''\n ) {\n endIdx++;\n }\n const currentContent = translatedParts.slice(0, endIdx).join('');\n // Write asynchronously/sync is fine here as node handles file locks reasonably well for single process\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(outputFilePath, currentContent);\n }\n }\n\n chunkLogger(\n [\n `${colorizeNumber(tokens)} tokens used `,\n `${ANSIColors.GREY_DARK}in ${colorizeNumber(chunkDuration)}ms${ANSIColors.RESET}`,\n ].join('')\n );\n })\n );\n\n // Wait for all chunks for this specific file/locale to finish\n await Promise.all(tasks);\n\n // Final Flush\n const fullContent = translatedParts.join('');\n if (flushStrategy === 'end' || flushStrategy === 'incremental') {\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(outputFilePath, fullContent);\n }\n\n const fileEndTime = performance.now();\n const totalDuration = ((fileEndTime - fileStartTime) / 1000).toFixed(2);\n const relativePath = relative(configuration.system.baseDir, outputFilePath);\n\n appLogger(\n `${colorize('✔', ANSIColors.GREEN)} File ${formatPath(relativePath)} completed in ${colorizeNumber(totalDuration)}s.`\n );\n\n return fullContent;\n } catch (error: any) {\n errorState.count++;\n const errorMessage = error?.message ?? JSON.stringify(error);\n appLogger(`${colorize('✖', ANSIColors.RED)} Error: ${errorMessage}`);\n if (errorState.count >= errorState.maxErrors) errorState.shouldStop = true;\n return null;\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"translateFile.mjs","names":[],"sources":["../../../src/translateDoc/translateFile.ts"],"sourcesContent":["import { mkdirSync, writeFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { dirname, relative } from 'node:path';\nimport { performance } from 'node:perf_hooks';\nimport { readAsset } from 'utils:asset';\nimport { formatLocale, formatPath } from '@intlayer/chokidar/utils';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport {\n colon,\n colorize,\n colorizeNumber,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport { retryManager } from '@intlayer/config/utils';\nimport { chunkText } from '../utils/calculateChunks';\nimport { chunkInference } from '../utils/chunkInference';\nimport { fixChunkStartEndChars } from '../utils/fixChunkStartEndChars';\nimport type { TranslateFileOptions } from './types';\nimport { sanitizeChunk, validateTranslation } from './validation';\n\nexport const translateFile = async ({\n baseFilePath,\n outputFilePath,\n locale,\n baseLocale,\n configuration,\n errorState,\n aiOptions,\n customInstructions,\n aiClient,\n aiConfig,\n flushStrategy = 'incremental',\n onChunkReceive,\n limit, // The Global Limiter\n}: TranslateFileOptions): Promise<string | null> => {\n if (errorState.shouldStop) return null;\n\n const appLogger = getAppLogger(configuration, { config: { prefix: '' } });\n const fileStartTime = performance.now();\n\n try {\n const fileContent = await readFile(baseFilePath, 'utf-8');\n const chunks = chunkText(fileContent);\n const totalChunks = chunks.length;\n\n const filePrefixText = `${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}] `;\n const filePrefix = `${colon(filePrefixText, { colSize: 40 })}${ANSIColors.RESET}`;\n const prefixText = `${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}][${formatLocale(locale)}${ANSIColors.GREY_DARK}] `;\n const prefix = `${colon(prefixText, { colSize: 40 })}${ANSIColors.RESET}`;\n\n appLogger(\n `${filePrefix}Split into ${colorizeNumber(totalChunks)} chunks. Queuing...`\n );\n\n const basePrompt = readAsset('./prompts/TRANSLATE_PROMPT.md', 'utf-8')\n .replaceAll('{{localeName}}', `${formatLocale(locale, false)}`)\n .replaceAll('{{baseLocaleName}}', `${formatLocale(baseLocale, false)}`)\n .replace('{{applicationContext}}', aiOptions?.applicationContext ?? '-')\n .replace('{{customInstructions}}', customInstructions ?? '-');\n\n const translatedParts: string[] = new Array(totalChunks).fill('');\n\n // Fallback if no limiter is provided (runs immediately)\n const runTask = limit ?? ((fn) => fn());\n\n // MAP CHUNKS TO GLOBAL TASKS\n // This pushes ALL chunks for this file into the Global Queue immediately.\n // They will execute whenever the global concurrency slots open up.\n const tasks = chunks.map((chunk, i) =>\n runTask(async () => {\n if (errorState.shouldStop) return null;\n\n const chunkLogger = getAppLogger(configuration, {\n config: {\n prefix: `${prefix} ${ANSIColors.GREY_DARK}[${i + 1}/${totalChunks}] ${ANSIColors.RESET}`,\n },\n });\n\n const chunkStartTime = performance.now();\n const isFirstChunk = i === 0;\n const fileToTranslateCurrentChunk = chunk.content;\n\n // Context Preparation\n const getPrevChunkPrompt = () =>\n `>>> CONTEXT: PREVIOUS SOURCE CONTENT <<<\\n\\`\\`\\`\\n` +\n (chunks[i - 1]?.content ?? '') +\n `\\n\\`\\`\\`\\n>>> END PREVIOUS CONTEXT <<<`;\n\n const getBaseChunkContextPrompt = () =>\n `>>> CONTEXT: NEXT CONTENT <<<\\n\\`\\`\\`\\n` +\n (chunks[i + 1]?.content ?? '') +\n `\\n\\`\\`\\`\\n>>> END NEXT CONTEXT <<<`;\n\n chunkLogger('Process started');\n\n const chunkTranslation = retryManager(async () => {\n const result = await chunkInference(\n [\n { role: 'system', content: basePrompt },\n ...(chunks[i + 1]\n ? [\n {\n role: 'system',\n content: getBaseChunkContextPrompt(),\n } as const,\n ]\n : []),\n ...(isFirstChunk\n ? []\n : [{ role: 'system', content: getPrevChunkPrompt() } as const]),\n {\n role: 'system',\n content: [\n `You are translating TARGET CHUNK (${i + 1}/${totalChunks}).`,\n `Translate ONLY the target chunk. Preserve frontmatter/code exactly.`,\n ].join('\\n'),\n },\n {\n role: 'user',\n content: `>>> TARGET CHUNK START <<<\\n${fileToTranslateCurrentChunk}\\n>>> TARGET CHUNK END <<<`,\n },\n ],\n aiOptions,\n configuration,\n aiClient,\n aiConfig\n );\n\n let processedChunk = sanitizeChunk(\n result?.fileContent,\n fileToTranslateCurrentChunk\n );\n processedChunk = fixChunkStartEndChars(\n processedChunk,\n fileToTranslateCurrentChunk\n );\n\n const isValid = validateTranslation(\n fileToTranslateCurrentChunk,\n processedChunk,\n chunkLogger\n );\n\n if (!isValid) {\n // Throwing an error here signals retryManager to try again\n throw new Error(\n `Validation failed for chunk ${i + 1}/${totalChunks}`\n );\n }\n\n return { content: processedChunk, tokens: result.tokenUsed };\n });\n\n const { content: translatedChunk, tokens } = await chunkTranslation();\n const chunkEndTime = performance.now();\n const chunkDuration = (chunkEndTime - chunkStartTime).toFixed(0);\n\n // Store Result\n translatedParts[i] = translatedChunk;\n\n if (onChunkReceive) {\n onChunkReceive(translatedChunk, i, totalChunks);\n }\n\n // Incremental Flush Strategy\n if (flushStrategy === 'incremental') {\n const isContiguous = translatedParts\n .slice(0, i + 1)\n .every((p) => p && p !== '');\n\n if (isContiguous) {\n let endIdx = 0;\n while (\n endIdx < totalChunks &&\n translatedParts[endIdx] &&\n translatedParts[endIdx] !== ''\n ) {\n endIdx++;\n }\n const currentContent = translatedParts.slice(0, endIdx).join('');\n // Write asynchronously/sync is fine here as node handles file locks reasonably well for single process\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(outputFilePath, currentContent);\n }\n }\n\n chunkLogger(\n [\n `${colorizeNumber(tokens)} tokens used `,\n `${ANSIColors.GREY_DARK}in ${colorizeNumber(chunkDuration)}ms${ANSIColors.RESET}`,\n ].join('')\n );\n })\n );\n\n // Wait for all chunks for this specific file/locale to finish\n await Promise.all(tasks);\n\n // Final Flush\n const fullContent = translatedParts.join('');\n if (flushStrategy === 'end' || flushStrategy === 'incremental') {\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(outputFilePath, fullContent);\n }\n\n const fileEndTime = performance.now();\n const totalDuration = ((fileEndTime - fileStartTime) / 1000).toFixed(2);\n const relativePath = relative(configuration.system.baseDir, outputFilePath);\n\n appLogger(\n `${colorize('✔', ANSIColors.GREEN)} File ${formatPath(relativePath)} completed in ${colorizeNumber(totalDuration)}s.`\n );\n\n return fullContent;\n } catch (error: any) {\n errorState.count++;\n const errorMessage = error?.message ?? JSON.stringify(error);\n appLogger(`${colorize('✖', ANSIColors.RED)} Error: ${errorMessage}`);\n if (errorState.count >= errorState.maxErrors) errorState.shouldStop = true;\n return null;\n }\n};\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{checkConfigConsistency as e}from"./checkConfigConsistency.mjs";import{ANSIColors as t,colorize as n,getAppLogger as r}from"@intlayer/config/logger";import{getIntlayerAPIProxy as i}from"@intlayer/api";import{extractErrorMessage as a}from"@intlayer/config/utils";const o=async(o,s=!0)=>{let c=r(o);if(!(o.editor.clientId&&o.editor.clientSecret))return c([`CMS auth not provided. You can either retreive the CMS access key on`,n(`https://intlayer.org/dahboard`,t.GREY),n(`(see doc:`,t.GREY_DARK),n(`https://intlayer.org/doc/concept/cms`,t.GREY),n(`)`,t.GREY_DARK),`.`],{level:`error`}),!1;let l=i(void 0,o);try{let r=(await l.oAuth.getOAuth2AccessToken()).data?.project;if(!r)return c(`Project not found`),!0;if(r.configuration&&s)try{let t=r.configuration;if(t.ai&&`apiKeyConfigured`in t.ai){let{apiKeyConfigured:e,...n}=t.ai;t={...t,ai:n}}e(t,o)}catch{c([`Remote configuration is not up to date. The project configuration does not match the local configuration.`,`You can push the configuration by running`,n(`npx intlayer configuration push`,t.CYAN),n(`(see doc:`,t.GREY_DARK),n(`https://intlayer.org/doc/concept/cli/push`,t.GREY),n(`)`,t.GREY_DARK),`.`],{level:`warn`})}}catch(e){return c(a(e),{level:`error`}),!1}return!0},s=async(e,i,a=!0)=>{let s=r(e),c=!!(e.editor.clientId&&e.editor.clientSecret),l=e.ai?.provider===`ollama`||i?.provider===`ollama`;return e.ai?.apiKey||i?.apiKey||l?!0:c?await o(e,a):(s([`AI options or API key not provided. You can either retreive the CMS access key on`,n(`https://intlayer.org/dahboard`,t.GREY),n(`(see doc:`,t.GREY_DARK),n(`https://intlayer.org/doc/concept/cms`,t.GREY),n(`)`,t.GREY_DARK),`. Alternatively, you can add your own OpenAI API key in the settings`,n(`(see doc:`,t.GREY_DARK),n(`https://intlayer.org/doc/concept/configuration`,t.GREY),n(`)`,t.GREY_DARK),`.`],{level:`error`}),!1)};export{s as checkAIAccess,o as checkCMSAuth};
1
+ import{checkConfigConsistency as e}from"./checkConfigConsistency.mjs";import*as t from"@intlayer/config/colors";import{colorize as n,getAppLogger as r}from"@intlayer/config/logger";import{getIntlayerAPIProxy as i}from"@intlayer/api";import{extractErrorMessage as a}from"@intlayer/config/utils";const o=async(o,s=!0)=>{let c=r(o);if(!(o.editor.clientId&&o.editor.clientSecret))return c([`CMS auth not provided. You can either retreive the CMS access key on`,n(`https://intlayer.org/dahboard`,t.GREY),n(`(see doc:`,t.GREY_DARK),n(`https://intlayer.org/doc/concept/cms`,t.GREY),n(`)`,t.GREY_DARK),`.`],{level:`error`}),!1;let l=i(void 0,o);try{let r=(await l.oAuth.getOAuth2AccessToken()).data?.project;if(!r)return c(`Project not found`),!0;if(r.configuration&&s)try{let t=r.configuration;if(t.ai&&`apiKeyConfigured`in t.ai){let{apiKeyConfigured:e,...n}=t.ai;t={...t,ai:n}}e(t,o)}catch{c([`Remote configuration is not up to date. The project configuration does not match the local configuration.`,`You can push the configuration by running`,n(`npx intlayer configuration push`,t.CYAN),n(`(see doc:`,t.GREY_DARK),n(`https://intlayer.org/doc/concept/cli/push`,t.GREY),n(`)`,t.GREY_DARK),`.`],{level:`warn`})}}catch(e){return c(a(e),{level:`error`}),!1}return!0},s=async(e,i,a=!0)=>{let s=r(e),c=!!(e.editor.clientId&&e.editor.clientSecret),l=e.ai?.provider===`ollama`||i?.provider===`ollama`;return e.ai?.apiKey||i?.apiKey||l?!0:c?await o(e,a):(s([`AI options or API key not provided. You can either retreive the CMS access key on`,n(`https://intlayer.org/dahboard`,t.GREY),n(`(see doc:`,t.GREY_DARK),n(`https://intlayer.org/doc/concept/cms`,t.GREY),n(`)`,t.GREY_DARK),`. Alternatively, you can add your own OpenAI API key in the settings`,n(`(see doc:`,t.GREY_DARK),n(`https://intlayer.org/doc/concept/configuration`,t.GREY),n(`)`,t.GREY_DARK),`.`],{level:`error`}),!1)};export{s as checkAIAccess,o as checkCMSAuth};
2
2
  //# sourceMappingURL=checkAccess.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"checkAccess.mjs","names":[],"sources":["../../../src/utils/checkAccess.ts"],"sourcesContent":["import type { AIOptions } from '@intlayer/api';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport { ANSIColors, colorize, getAppLogger } from '@intlayer/config/logger';\nimport { extractErrorMessage } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { checkConfigConsistency } from './checkConfigConsistency';\n\nexport const checkCMSAuth = async (\n configuration: IntlayerConfig,\n shouldCheckConfigConsistency: boolean = true\n): Promise<boolean> => {\n const appLogger = getAppLogger(configuration);\n\n const hasCMSAuth =\n configuration.editor.clientId && configuration.editor.clientSecret;\n if (!hasCMSAuth) {\n appLogger(\n [\n 'CMS auth not provided. You can either retreive the CMS access key on',\n colorize('https://intlayer.org/dahboard', ANSIColors.GREY),\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize('https://intlayer.org/doc/concept/cms', ANSIColors.GREY),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n {\n level: 'error',\n }\n );\n\n return false;\n }\n const intlayerAPI = getIntlayerAPIProxy(undefined, configuration);\n\n try {\n const result = await intlayerAPI.oAuth.getOAuth2AccessToken();\n\n const project = result.data?.project;\n\n if (!project) {\n appLogger('Project not found');\n\n return true;\n }\n\n if (project.configuration && shouldCheckConfigConsistency) {\n try {\n let remoteConfigToCheck = project.configuration;\n\n // Remove server-side computed flags (apiKeyConfigured)\n // We use destructuring + spread to avoid the 'delete' operator (performance)\n if (\n remoteConfigToCheck.ai &&\n 'apiKeyConfigured' in remoteConfigToCheck.ai\n ) {\n const { apiKeyConfigured, ...restAi } = remoteConfigToCheck.ai as any;\n\n remoteConfigToCheck = {\n ...remoteConfigToCheck,\n ai: restAi,\n };\n }\n\n // Recursively check if project.configuration (subset) matches configuration (superset)\n checkConfigConsistency(remoteConfigToCheck, configuration);\n } catch {\n appLogger(\n [\n 'Remote configuration is not up to date. The project configuration does not match the local configuration.',\n 'You can push the configuration by running',\n colorize('npx intlayer configuration push', ANSIColors.CYAN),\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize(\n 'https://intlayer.org/doc/concept/cli/push',\n ANSIColors.GREY\n ),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n {\n level: 'warn',\n }\n );\n }\n }\n } catch (error) {\n const message = extractErrorMessage(error);\n\n appLogger(message, {\n level: 'error',\n });\n return false;\n }\n\n return true;\n};\n\nexport const checkAIAccess = async (\n configuration: IntlayerConfig,\n aiOptions?: AIOptions,\n shouldCheckConfigConsistency: boolean = true\n): Promise<boolean> => {\n const appLogger = getAppLogger(configuration);\n\n const hasCMSAuth = Boolean(\n configuration.editor.clientId && configuration.editor.clientSecret\n );\n const isOllama =\n configuration.ai?.provider === 'ollama' || aiOptions?.provider === 'ollama';\n const hasHisOwnAIAPIKey = Boolean(\n configuration.ai?.apiKey || aiOptions?.apiKey\n );\n\n if (hasHisOwnAIAPIKey || isOllama) {\n return true;\n }\n\n // User need to provide either his own AI API key or the CMS auth\n if (!hasCMSAuth) {\n appLogger(\n [\n 'AI options or API key not provided. You can either retreive the CMS access key on',\n colorize('https://intlayer.org/dahboard', ANSIColors.GREY),\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize('https://intlayer.org/doc/concept/cms', ANSIColors.GREY),\n colorize(')', ANSIColors.GREY_DARK),\n '. Alternatively, you can add your own OpenAI API key in the settings',\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize(\n 'https://intlayer.org/doc/concept/configuration',\n ANSIColors.GREY\n ),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n {\n level: 'error',\n }\n );\n\n return false;\n }\n\n // If the user do not have his own AI API key, we need to check the CMS auth\n return await checkCMSAuth(configuration, shouldCheckConfigConsistency);\n};\n"],"mappings":"4QAOA,MAAa,EAAe,MAC1B,EACA,EAAwC,KACnB,CACrB,IAAM,EAAY,EAAa,EAAc,CAI7C,GAAI,EADF,EAAc,OAAO,UAAY,EAAc,OAAO,cAgBtD,OAdA,EACE,CACE,uEACA,EAAS,gCAAiC,EAAW,KAAK,CAC1D,EAAS,YAAa,EAAW,UAAU,CAC3C,EAAS,uCAAwC,EAAW,KAAK,CACjE,EAAS,IAAK,EAAW,UAAU,CACnC,IACD,CACD,CACE,MAAO,QACR,CACF,CAEM,GAET,IAAM,EAAc,EAAoB,IAAA,GAAW,EAAc,CAEjE,GAAI,CAGF,IAAM,GAFS,MAAM,EAAY,MAAM,sBAAsB,EAEtC,MAAM,QAE7B,GAAI,CAAC,EAGH,OAFA,EAAU,oBAAoB,CAEvB,GAGT,GAAI,EAAQ,eAAiB,EAC3B,GAAI,CACF,IAAI,EAAsB,EAAQ,cAIlC,GACE,EAAoB,IACpB,qBAAsB,EAAoB,GAC1C,CACA,GAAM,CAAE,mBAAkB,GAAG,GAAW,EAAoB,GAE5D,EAAsB,CACpB,GAAG,EACH,GAAI,EACL,CAIH,EAAuB,EAAqB,EAAc,MACpD,CACN,EACE,CACE,4GACA,4CACA,EAAS,kCAAmC,EAAW,KAAK,CAC5D,EAAS,YAAa,EAAW,UAAU,CAC3C,EACE,4CACA,EAAW,KACZ,CACD,EAAS,IAAK,EAAW,UAAU,CACnC,IACD,CACD,CACE,MAAO,OACR,CACF,QAGE,EAAO,CAMd,OAHA,EAFgB,EAAoB,EAAM,CAEvB,CACjB,MAAO,QACR,CAAC,CACK,GAGT,MAAO,IAGI,EAAgB,MAC3B,EACA,EACA,EAAwC,KACnB,CACrB,IAAM,EAAY,EAAa,EAAc,CAEvC,EAAa,GACjB,EAAc,OAAO,UAAY,EAAc,OAAO,cAElD,EACJ,EAAc,IAAI,WAAa,UAAY,GAAW,WAAa,SAoCrE,OAlCE,EAAc,IAAI,QAAU,GAAW,QAGhB,EAChB,GAIJ,EA0BE,MAAM,EAAa,EAAe,EAA6B,EAzBpE,EACE,CACE,oFACA,EAAS,gCAAiC,EAAW,KAAK,CAC1D,EAAS,YAAa,EAAW,UAAU,CAC3C,EAAS,uCAAwC,EAAW,KAAK,CACjE,EAAS,IAAK,EAAW,UAAU,CACnC,uEACA,EAAS,YAAa,EAAW,UAAU,CAC3C,EACE,iDACA,EAAW,KACZ,CACD,EAAS,IAAK,EAAW,UAAU,CACnC,IACD,CACD,CACE,MAAO,QACR,CACF,CAEM"}
1
+ {"version":3,"file":"checkAccess.mjs","names":[],"sources":["../../../src/utils/checkAccess.ts"],"sourcesContent":["import type { AIOptions } from '@intlayer/api';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport { extractErrorMessage } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { checkConfigConsistency } from './checkConfigConsistency';\n\nexport const checkCMSAuth = async (\n configuration: IntlayerConfig,\n shouldCheckConfigConsistency: boolean = true\n): Promise<boolean> => {\n const appLogger = getAppLogger(configuration);\n\n const hasCMSAuth =\n configuration.editor.clientId && configuration.editor.clientSecret;\n if (!hasCMSAuth) {\n appLogger(\n [\n 'CMS auth not provided. You can either retreive the CMS access key on',\n colorize('https://intlayer.org/dahboard', ANSIColors.GREY),\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize('https://intlayer.org/doc/concept/cms', ANSIColors.GREY),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n {\n level: 'error',\n }\n );\n\n return false;\n }\n const intlayerAPI = getIntlayerAPIProxy(undefined, configuration);\n\n try {\n const result = await intlayerAPI.oAuth.getOAuth2AccessToken();\n\n const project = result.data?.project;\n\n if (!project) {\n appLogger('Project not found');\n\n return true;\n }\n\n if (project.configuration && shouldCheckConfigConsistency) {\n try {\n let remoteConfigToCheck = project.configuration;\n\n // Remove server-side computed flags (apiKeyConfigured)\n // We use destructuring + spread to avoid the 'delete' operator (performance)\n if (\n remoteConfigToCheck.ai &&\n 'apiKeyConfigured' in remoteConfigToCheck.ai\n ) {\n const { apiKeyConfigured, ...restAi } = remoteConfigToCheck.ai as any;\n\n remoteConfigToCheck = {\n ...remoteConfigToCheck,\n ai: restAi,\n };\n }\n\n // Recursively check if project.configuration (subset) matches configuration (superset)\n checkConfigConsistency(remoteConfigToCheck, configuration);\n } catch {\n appLogger(\n [\n 'Remote configuration is not up to date. The project configuration does not match the local configuration.',\n 'You can push the configuration by running',\n colorize('npx intlayer configuration push', ANSIColors.CYAN),\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize(\n 'https://intlayer.org/doc/concept/cli/push',\n ANSIColors.GREY\n ),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n {\n level: 'warn',\n }\n );\n }\n }\n } catch (error) {\n const message = extractErrorMessage(error);\n\n appLogger(message, {\n level: 'error',\n });\n return false;\n }\n\n return true;\n};\n\nexport const checkAIAccess = async (\n configuration: IntlayerConfig,\n aiOptions?: AIOptions,\n shouldCheckConfigConsistency: boolean = true\n): Promise<boolean> => {\n const appLogger = getAppLogger(configuration);\n\n const hasCMSAuth = Boolean(\n configuration.editor.clientId && configuration.editor.clientSecret\n );\n const isOllama =\n configuration.ai?.provider === 'ollama' || aiOptions?.provider === 'ollama';\n const hasHisOwnAIAPIKey = Boolean(\n configuration.ai?.apiKey || aiOptions?.apiKey\n );\n\n if (hasHisOwnAIAPIKey || isOllama) {\n return true;\n }\n\n // User need to provide either his own AI API key or the CMS auth\n if (!hasCMSAuth) {\n appLogger(\n [\n 'AI options or API key not provided. You can either retreive the CMS access key on',\n colorize('https://intlayer.org/dahboard', ANSIColors.GREY),\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize('https://intlayer.org/doc/concept/cms', ANSIColors.GREY),\n colorize(')', ANSIColors.GREY_DARK),\n '. Alternatively, you can add your own OpenAI API key in the settings',\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize(\n 'https://intlayer.org/doc/concept/configuration',\n ANSIColors.GREY\n ),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n {\n level: 'error',\n }\n );\n\n return false;\n }\n\n // If the user do not have his own AI API key, we need to check the CMS auth\n return await checkCMSAuth(configuration, shouldCheckConfigConsistency);\n};\n"],"mappings":"sSAQA,MAAa,EAAe,MAC1B,EACA,EAAwC,KACnB,CACrB,IAAM,EAAY,EAAa,EAAc,CAI7C,GAAI,EADF,EAAc,OAAO,UAAY,EAAc,OAAO,cAgBtD,OAdA,EACE,CACE,uEACA,EAAS,gCAAiC,EAAW,KAAK,CAC1D,EAAS,YAAa,EAAW,UAAU,CAC3C,EAAS,uCAAwC,EAAW,KAAK,CACjE,EAAS,IAAK,EAAW,UAAU,CACnC,IACD,CACD,CACE,MAAO,QACR,CACF,CAEM,GAET,IAAM,EAAc,EAAoB,IAAA,GAAW,EAAc,CAEjE,GAAI,CAGF,IAAM,GAFS,MAAM,EAAY,MAAM,sBAAsB,EAEtC,MAAM,QAE7B,GAAI,CAAC,EAGH,OAFA,EAAU,oBAAoB,CAEvB,GAGT,GAAI,EAAQ,eAAiB,EAC3B,GAAI,CACF,IAAI,EAAsB,EAAQ,cAIlC,GACE,EAAoB,IACpB,qBAAsB,EAAoB,GAC1C,CACA,GAAM,CAAE,mBAAkB,GAAG,GAAW,EAAoB,GAE5D,EAAsB,CACpB,GAAG,EACH,GAAI,EACL,CAIH,EAAuB,EAAqB,EAAc,MACpD,CACN,EACE,CACE,4GACA,4CACA,EAAS,kCAAmC,EAAW,KAAK,CAC5D,EAAS,YAAa,EAAW,UAAU,CAC3C,EACE,4CACA,EAAW,KACZ,CACD,EAAS,IAAK,EAAW,UAAU,CACnC,IACD,CACD,CACE,MAAO,OACR,CACF,QAGE,EAAO,CAMd,OAHA,EAFgB,EAAoB,EAAM,CAEvB,CACjB,MAAO,QACR,CAAC,CACK,GAGT,MAAO,IAGI,EAAgB,MAC3B,EACA,EACA,EAAwC,KACnB,CACrB,IAAM,EAAY,EAAa,EAAc,CAEvC,EAAa,GACjB,EAAc,OAAO,UAAY,EAAc,OAAO,cAElD,EACJ,EAAc,IAAI,WAAa,UAAY,GAAW,WAAa,SAoCrE,OAlCE,EAAc,IAAI,QAAU,GAAW,QAGhB,EAChB,GAIJ,EA0BE,MAAM,EAAa,EAAe,EAA6B,EAzBpE,EACE,CACE,oFACA,EAAS,gCAAiC,EAAW,KAAK,CAC1D,EAAS,YAAa,EAAW,UAAU,CAC3C,EAAS,uCAAwC,EAAW,KAAK,CACjE,EAAS,IAAK,EAAW,UAAU,CACnC,uEACA,EAAS,YAAa,EAAW,UAAU,CAC3C,EACE,iDACA,EAAW,KACZ,CACD,EAAS,IAAK,EAAW,UAAU,CACnC,IACD,CACD,CACE,MAAO,QACR,CACF,CAEM"}
@@ -1,2 +1,2 @@
1
- import{checkAIAccess as e}from"./checkAccess.mjs";import{ANSIColors as t,colorize as n,getAppLogger as r}from"@intlayer/config/logger";globalThis.AI_SDK_LOG_WARNINGS=!1;const i=(e,r)=>{r([n(`Provider:`,t.GREY_DARK),n(e?.provider??`(default)`,t.BLUE),n(`- Model:`,t.GREY_DARK),n(e?.model??`(default)`,t.BLUE),n(`- API Key:`,t.GREY_DARK),n(e?.apiKey?`✓`:`(not set)`,t.BLUE)])},a=async(a,o)=>{let s=r(a);if(o?.apiKey||o?.provider===`ollama`||a.ai?.apiKey||a.ai?.provider===`ollama`){let r;try{r=await import(`@intlayer/ai`)}catch{s([n(`Using your API key, you can install the`,t.GREY),n(`@intlayer/ai`,t.GREY_LIGHT),n(`package to run the process locally, with no dependency on the Intlayer server`,t.GREY)],{level:`warn`});let r=await e(a,o);return i(o??{},s),{isCustomAI:!1,hasAIAccess:r}}s([n(`@intlayer/ai`,t.GREY_LIGHT),n(`found - Run process locally`,t.GREY_DARK)]);let c=await r.getAIConfig({userOptions:o,accessType:[`public`]});return i(o??{},s),{aiClient:r,aiConfig:c,isCustomAI:!0,hasAIAccess:!0}}let c=await e(a,o);return i(o??{},s),{isCustomAI:!1,hasAIAccess:c}};export{a as setupAI};
1
+ import{checkAIAccess as e}from"./checkAccess.mjs";import*as t from"@intlayer/config/colors";import{colorize as n,getAppLogger as r}from"@intlayer/config/logger";globalThis.AI_SDK_LOG_WARNINGS=!1;const i=(e,r)=>{r([n(`Provider:`,t.GREY_DARK),n(e?.provider??`(default)`,t.BLUE),n(`- Model:`,t.GREY_DARK),n(e?.model??`(default)`,t.BLUE),n(`- API Key:`,t.GREY_DARK),n(e?.apiKey?`✓`:`(not set)`,t.BLUE)])},a=async(a,o)=>{let s=r(a);if(o?.apiKey||o?.provider===`ollama`||a.ai?.apiKey||a.ai?.provider===`ollama`){let r;try{r=await import(`@intlayer/ai`)}catch{s([n(`Using your API key, you can install the`,t.GREY),n(`@intlayer/ai`,t.GREY_LIGHT),n(`package to run the process locally, with no dependency on the Intlayer server`,t.GREY)],{level:`warn`});let r=await e(a,o);return i(o??{},s),{isCustomAI:!1,hasAIAccess:r}}s([n(`@intlayer/ai`,t.GREY_LIGHT),n(`found - Run process locally`,t.GREY_DARK)]);let c=await r.getAIConfig({userOptions:o,accessType:[`public`]});return i(o??{},s),{aiClient:r,aiConfig:c,isCustomAI:!0,hasAIAccess:!0}}let c=await e(a,o);return i(o??{},s),{isCustomAI:!1,hasAIAccess:c}};export{a as setupAI};
2
2
  //# sourceMappingURL=setupAI.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"setupAI.mjs","names":[],"sources":["../../../src/utils/setupAI.ts"],"sourcesContent":["import type { AIConfig, AIOptions } from '@intlayer/ai';\nimport {\n ANSIColors,\n colorize,\n getAppLogger,\n type logger,\n} from '@intlayer/config/logger';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { checkAIAccess } from './checkAccess';\n\nexport type AIClient = typeof import('@intlayer/ai');\n\ntype SetupAIResult = {\n aiClient?: AIClient;\n aiConfig?: AIConfig;\n isCustomAI: boolean;\n hasAIAccess: boolean;\n};\n\n// Disable warnings from the AI SDK\nglobalThis.AI_SDK_LOG_WARNINGS = false;\n\nconst logAIConfig = (aiOptions: AIOptions, appLogger: typeof logger) => {\n appLogger([\n colorize('Provider:', ANSIColors.GREY_DARK),\n colorize(aiOptions?.provider ?? '(default)', ANSIColors.BLUE),\n colorize('- Model:', ANSIColors.GREY_DARK),\n colorize(aiOptions?.model ?? '(default)', ANSIColors.BLUE),\n colorize('- API Key:', ANSIColors.GREY_DARK),\n colorize(aiOptions?.apiKey ? '✓' : '(not set)', ANSIColors.BLUE),\n ]);\n};\n\n/**\n * Checks if the @intlayer/ai package is available and configured when an API key is provided.\n * If API key is present but package is missing, logs a warning.\n * Also checks if the user has access to AI (either via local key or CMS auth).\n */\nexport const setupAI = async (\n configuration: IntlayerConfig,\n aiOptions?: AIOptions\n): Promise<SetupAIResult | undefined> => {\n const appLogger = getAppLogger(configuration);\n\n const isLocalAI =\n aiOptions?.apiKey ||\n aiOptions?.provider === 'ollama' ||\n configuration.ai?.apiKey ||\n configuration.ai?.provider === 'ollama';\n\n if (isLocalAI) {\n // Try to import the AI package for local AI usage\n let aiClient: AIClient | undefined;\n\n try {\n aiClient = await import('@intlayer/ai');\n } catch {\n // Package not installed - log warning and fall back to backend\n appLogger(\n [\n colorize('Using your API key, you can install the', ANSIColors.GREY),\n colorize('@intlayer/ai', ANSIColors.GREY_LIGHT),\n colorize(\n 'package to run the process locally, with no dependency on the Intlayer server',\n ANSIColors.GREY\n ),\n ],\n {\n level: 'warn',\n }\n );\n\n // Fall back to backend API check\n const hasAIAccess = await checkAIAccess(configuration, aiOptions);\n logAIConfig(aiOptions ?? {}, appLogger);\n return {\n isCustomAI: false,\n hasAIAccess,\n };\n }\n\n // Package found - now configure it (errors here should propagate, not fall back)\n appLogger([\n colorize('@intlayer/ai', ANSIColors.GREY_LIGHT),\n colorize('found - Run process locally', ANSIColors.GREY_DARK),\n ]);\n\n const aiConfig = await aiClient.getAIConfig({\n userOptions: aiOptions,\n accessType: ['public'],\n });\n\n logAIConfig(aiOptions ?? {}, appLogger);\n\n return {\n aiClient,\n aiConfig,\n isCustomAI: true,\n hasAIAccess: true, // Local AI always has access\n };\n }\n\n // No local AI configured - use backend API\n const hasAIAccess = await checkAIAccess(configuration, aiOptions);\n logAIConfig(aiOptions ?? {}, appLogger);\n\n return {\n isCustomAI: false,\n hasAIAccess,\n };\n};\n"],"mappings":"uIAoBA,WAAW,oBAAsB,GAEjC,MAAM,GAAe,EAAsB,IAA6B,CACtE,EAAU,CACR,EAAS,YAAa,EAAW,UAAU,CAC3C,EAAS,GAAW,UAAY,YAAa,EAAW,KAAK,CAC7D,EAAS,WAAY,EAAW,UAAU,CAC1C,EAAS,GAAW,OAAS,YAAa,EAAW,KAAK,CAC1D,EAAS,aAAc,EAAW,UAAU,CAC5C,EAAS,GAAW,OAAS,IAAM,YAAa,EAAW,KAAK,CACjE,CAAC,EAQS,EAAU,MACrB,EACA,IACuC,CACvC,IAAM,EAAY,EAAa,EAAc,CAQ7C,GALE,GAAW,QACX,GAAW,WAAa,UACxB,EAAc,IAAI,QAClB,EAAc,IAAI,WAAa,SAElB,CAEb,IAAI,EAEJ,GAAI,CACF,EAAW,MAAM,OAAO,qBAClB,CAEN,EACE,CACE,EAAS,0CAA2C,EAAW,KAAK,CACpE,EAAS,eAAgB,EAAW,WAAW,CAC/C,EACE,gFACA,EAAW,KACZ,CACF,CACD,CACE,MAAO,OACR,CACF,CAGD,IAAM,EAAc,MAAM,EAAc,EAAe,EAAU,CAEjE,OADA,EAAY,GAAa,EAAE,CAAE,EAAU,CAChC,CACL,WAAY,GACZ,cACD,CAIH,EAAU,CACR,EAAS,eAAgB,EAAW,WAAW,CAC/C,EAAS,8BAA+B,EAAW,UAAU,CAC9D,CAAC,CAEF,IAAM,EAAW,MAAM,EAAS,YAAY,CAC1C,YAAa,EACb,WAAY,CAAC,SAAS,CACvB,CAAC,CAIF,OAFA,EAAY,GAAa,EAAE,CAAE,EAAU,CAEhC,CACL,WACA,WACA,WAAY,GACZ,YAAa,GACd,CAIH,IAAM,EAAc,MAAM,EAAc,EAAe,EAAU,CAGjE,OAFA,EAAY,GAAa,EAAE,CAAE,EAAU,CAEhC,CACL,WAAY,GACZ,cACD"}
1
+ {"version":3,"file":"setupAI.mjs","names":[],"sources":["../../../src/utils/setupAI.ts"],"sourcesContent":["import type { AIConfig, AIOptions } from '@intlayer/ai';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger, type logger } from '@intlayer/config/logger';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { checkAIAccess } from './checkAccess';\n\nexport type AIClient = typeof import('@intlayer/ai');\n\ntype SetupAIResult = {\n aiClient?: AIClient;\n aiConfig?: AIConfig;\n isCustomAI: boolean;\n hasAIAccess: boolean;\n};\n\n// Disable warnings from the AI SDK\nglobalThis.AI_SDK_LOG_WARNINGS = false;\n\nconst logAIConfig = (aiOptions: AIOptions, appLogger: typeof logger) => {\n appLogger([\n colorize('Provider:', ANSIColors.GREY_DARK),\n colorize(aiOptions?.provider ?? '(default)', ANSIColors.BLUE),\n colorize('- Model:', ANSIColors.GREY_DARK),\n colorize(aiOptions?.model ?? '(default)', ANSIColors.BLUE),\n colorize('- API Key:', ANSIColors.GREY_DARK),\n colorize(aiOptions?.apiKey ? '✓' : '(not set)', ANSIColors.BLUE),\n ]);\n};\n\n/**\n * Checks if the @intlayer/ai package is available and configured when an API key is provided.\n * If API key is present but package is missing, logs a warning.\n * Also checks if the user has access to AI (either via local key or CMS auth).\n */\nexport const setupAI = async (\n configuration: IntlayerConfig,\n aiOptions?: AIOptions\n): Promise<SetupAIResult | undefined> => {\n const appLogger = getAppLogger(configuration);\n\n const isLocalAI =\n aiOptions?.apiKey ||\n aiOptions?.provider === 'ollama' ||\n configuration.ai?.apiKey ||\n configuration.ai?.provider === 'ollama';\n\n if (isLocalAI) {\n // Try to import the AI package for local AI usage\n let aiClient: AIClient | undefined;\n\n try {\n aiClient = await import('@intlayer/ai');\n } catch {\n // Package not installed - log warning and fall back to backend\n appLogger(\n [\n colorize('Using your API key, you can install the', ANSIColors.GREY),\n colorize('@intlayer/ai', ANSIColors.GREY_LIGHT),\n colorize(\n 'package to run the process locally, with no dependency on the Intlayer server',\n ANSIColors.GREY\n ),\n ],\n {\n level: 'warn',\n }\n );\n\n // Fall back to backend API check\n const hasAIAccess = await checkAIAccess(configuration, aiOptions);\n logAIConfig(aiOptions ?? {}, appLogger);\n return {\n isCustomAI: false,\n hasAIAccess,\n };\n }\n\n // Package found - now configure it (errors here should propagate, not fall back)\n appLogger([\n colorize('@intlayer/ai', ANSIColors.GREY_LIGHT),\n colorize('found - Run process locally', ANSIColors.GREY_DARK),\n ]);\n\n const aiConfig = await aiClient.getAIConfig({\n userOptions: aiOptions,\n accessType: ['public'],\n });\n\n logAIConfig(aiOptions ?? {}, appLogger);\n\n return {\n aiClient,\n aiConfig,\n isCustomAI: true,\n hasAIAccess: true, // Local AI always has access\n };\n }\n\n // No local AI configured - use backend API\n const hasAIAccess = await checkAIAccess(configuration, aiOptions);\n logAIConfig(aiOptions ?? {}, appLogger);\n\n return {\n isCustomAI: false,\n hasAIAccess,\n };\n};\n"],"mappings":"iKAgBA,WAAW,oBAAsB,GAEjC,MAAM,GAAe,EAAsB,IAA6B,CACtE,EAAU,CACR,EAAS,YAAa,EAAW,UAAU,CAC3C,EAAS,GAAW,UAAY,YAAa,EAAW,KAAK,CAC7D,EAAS,WAAY,EAAW,UAAU,CAC1C,EAAS,GAAW,OAAS,YAAa,EAAW,KAAK,CAC1D,EAAS,aAAc,EAAW,UAAU,CAC5C,EAAS,GAAW,OAAS,IAAM,YAAa,EAAW,KAAK,CACjE,CAAC,EAQS,EAAU,MACrB,EACA,IACuC,CACvC,IAAM,EAAY,EAAa,EAAc,CAQ7C,GALE,GAAW,QACX,GAAW,WAAa,UACxB,EAAc,IAAI,QAClB,EAAc,IAAI,WAAa,SAElB,CAEb,IAAI,EAEJ,GAAI,CACF,EAAW,MAAM,OAAO,qBAClB,CAEN,EACE,CACE,EAAS,0CAA2C,EAAW,KAAK,CACpE,EAAS,eAAgB,EAAW,WAAW,CAC/C,EACE,gFACA,EAAW,KACZ,CACF,CACD,CACE,MAAO,OACR,CACF,CAGD,IAAM,EAAc,MAAM,EAAc,EAAe,EAAU,CAEjE,OADA,EAAY,GAAa,EAAE,CAAE,EAAU,CAChC,CACL,WAAY,GACZ,cACD,CAIH,EAAU,CACR,EAAS,eAAgB,EAAW,WAAW,CAC/C,EAAS,8BAA+B,EAAW,UAAU,CAC9D,CAAC,CAEF,IAAM,EAAW,MAAM,EAAS,YAAY,CAC1C,YAAa,EACb,WAAY,CAAC,SAAS,CACvB,CAAC,CAIF,OAFA,EAAY,GAAa,EAAE,CAAE,EAAU,CAEhC,CACL,WACA,WACA,WAAY,GACZ,YAAa,GACd,CAIH,IAAM,EAAc,MAAM,EAAc,EAAe,EAAU,CAGjE,OAFA,EAAY,GAAa,EAAE,CAAE,EAAU,CAEhC,CACL,WAAY,GACZ,cACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"login.d.ts","names":[],"sources":["../../../src/auth/login.ts"],"mappings":";;;KAeK,YAAA;EACH,MAAA;EACA,aAAA,GAAgB,uBAAA;AAAA;AAAA,cAGL,KAAA,GAAe,OAAA,EAAS,YAAA,KAAY,OAAA"}
1
+ {"version":3,"file":"login.d.ts","names":[],"sources":["../../../src/auth/login.ts"],"mappings":";;;KAWK,YAAA;EACH,MAAA;EACA,aAAA,GAAgB,uBAAA;AAAA;AAAA,cAGL,KAAA,GAAe,OAAA,EAAS,YAAA,KAAY,OAAA"}