@intlayer/cli 9.3.0 → 9.3.2

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 (136) hide show
  1. package/README.md +24 -24
  2. package/dist/cjs/IntlayerEventListener.cjs +2 -6
  3. package/dist/cjs/IntlayerEventListener.cjs.map +1 -1
  4. package/dist/cjs/_virtual/_rolldown/runtime.cjs +1 -2
  5. package/dist/cjs/_virtual/_utils_asset.cjs +2 -1
  6. package/dist/cjs/auth/login.cjs.map +1 -1
  7. package/dist/cjs/auth/sessionToken.cjs.map +1 -1
  8. package/dist/cjs/build.cjs.map +1 -1
  9. package/dist/cjs/bundle.cjs +0 -1
  10. package/dist/cjs/bundle.cjs.map +1 -1
  11. package/dist/cjs/ci.cjs.map +1 -1
  12. package/dist/cjs/cli.cjs.map +1 -1
  13. package/dist/cjs/config.cjs.map +1 -1
  14. package/dist/cjs/editor.cjs +3 -1
  15. package/dist/cjs/editor.cjs.map +1 -1
  16. package/dist/cjs/extract.cjs.map +1 -1
  17. package/dist/cjs/fill/deepMergeContent.cjs +0 -1
  18. package/dist/cjs/fill/deepMergeContent.cjs.map +1 -1
  19. package/dist/cjs/fill/extractTranslatableContent.cjs +0 -1
  20. package/dist/cjs/fill/extractTranslatableContent.cjs.map +1 -1
  21. package/dist/cjs/fill/fill.cjs +4 -2
  22. package/dist/cjs/fill/fill.cjs.map +1 -1
  23. package/dist/cjs/fill/formatAutoFilledFilePath.cjs +4 -1
  24. package/dist/cjs/fill/formatAutoFilledFilePath.cjs.map +1 -1
  25. package/dist/cjs/fill/formatFillData.cjs.map +1 -1
  26. package/dist/cjs/fill/getAvailableLocalesInDictionary.cjs +0 -1
  27. package/dist/cjs/fill/getAvailableLocalesInDictionary.cjs.map +1 -1
  28. package/dist/cjs/fill/getFilterMissingContentPerLocale.cjs +0 -1
  29. package/dist/cjs/fill/getFilterMissingContentPerLocale.cjs.map +1 -1
  30. package/dist/cjs/fill/listTranslationsTasks.cjs.map +1 -1
  31. package/dist/cjs/fill/translateDictionary.cjs +7 -6
  32. package/dist/cjs/fill/translateDictionary.cjs.map +1 -1
  33. package/dist/cjs/fill/writeFill.cjs.map +1 -1
  34. package/dist/cjs/getTargetDictionary.cjs.map +1 -1
  35. package/dist/cjs/init.cjs +14 -133
  36. package/dist/cjs/init.cjs.map +1 -1
  37. package/dist/cjs/initBuildOptimization.cjs.map +1 -1
  38. package/dist/cjs/initCompiler.cjs.map +1 -1
  39. package/dist/cjs/initMCP.cjs.map +1 -1
  40. package/dist/cjs/initSkills.cjs +2 -1
  41. package/dist/cjs/initSkills.cjs.map +1 -1
  42. package/dist/cjs/listContentDeclaration.cjs +2 -1
  43. package/dist/cjs/listContentDeclaration.cjs.map +1 -1
  44. package/dist/cjs/listProjects.cjs.map +1 -1
  45. package/dist/cjs/liveSync.cjs.map +1 -1
  46. package/dist/cjs/pull.cjs.map +1 -1
  47. package/dist/cjs/push/pullLog.cjs.map +1 -1
  48. package/dist/cjs/push/push.cjs +2 -1
  49. package/dist/cjs/push/push.cjs.map +1 -1
  50. package/dist/cjs/pushConfig.cjs.map +1 -1
  51. package/dist/cjs/pushLog.cjs.map +1 -1
  52. package/dist/cjs/reviewDoc/reviewDoc.cjs +4 -2
  53. package/dist/cjs/reviewDoc/reviewDoc.cjs.map +1 -1
  54. package/dist/cjs/reviewDoc/reviewDocBlockAware.cjs +4 -2
  55. package/dist/cjs/reviewDoc/reviewDocBlockAware.cjs.map +1 -1
  56. package/dist/cjs/reviewDoc/reviewDocLog.cjs +6 -3
  57. package/dist/cjs/reviewDoc/reviewDocLog.cjs.map +1 -1
  58. package/dist/cjs/reviewDoc/reviewDocProgress.cjs.map +1 -1
  59. package/dist/cjs/reviewDoc/reviewDocSynthesis.cjs.map +1 -1
  60. package/dist/cjs/scan.cjs +2 -1
  61. package/dist/cjs/scan.cjs.map +1 -1
  62. package/dist/cjs/searchDoc.cjs.map +1 -1
  63. package/dist/cjs/test/listMissingTranslations.cjs.map +1 -1
  64. package/dist/cjs/test/test.cjs.map +1 -1
  65. package/dist/cjs/translateDoc/translateDoc.cjs +3 -2
  66. package/dist/cjs/translateDoc/translateDoc.cjs.map +1 -1
  67. package/dist/cjs/translateDoc/translateFile.cjs +6 -3
  68. package/dist/cjs/translateDoc/translateFile.cjs.map +1 -1
  69. package/dist/cjs/translateDoc/validation.cjs +0 -1
  70. package/dist/cjs/translateDoc/validation.cjs.map +1 -1
  71. package/dist/cjs/utils/calculateChunks.cjs.map +1 -1
  72. package/dist/cjs/utils/checkAccess.cjs.map +1 -1
  73. package/dist/cjs/utils/checkConfigConsistency.cjs +0 -1
  74. package/dist/cjs/utils/checkConfigConsistency.cjs.map +1 -1
  75. package/dist/cjs/utils/checkFileModifiedRange.cjs +3 -2
  76. package/dist/cjs/utils/checkFileModifiedRange.cjs.map +1 -1
  77. package/dist/cjs/utils/checkLastUpdateTime.cjs.map +1 -1
  78. package/dist/cjs/utils/chunkInference.cjs.map +1 -1
  79. package/dist/cjs/utils/fixChunkStartEndChars.cjs +0 -1
  80. package/dist/cjs/utils/fixChunkStartEndChars.cjs.map +1 -1
  81. package/dist/cjs/utils/formatLineRanges.cjs +0 -1
  82. package/dist/cjs/utils/formatLineRanges.cjs.map +1 -1
  83. package/dist/cjs/utils/formatTimeDiff.cjs +0 -1
  84. package/dist/cjs/utils/formatTimeDiff.cjs.map +1 -1
  85. package/dist/cjs/utils/getIsFileUpdatedRecently.cjs.map +1 -1
  86. package/dist/cjs/utils/getOutputFilePath.cjs +2 -1
  87. package/dist/cjs/utils/getOutputFilePath.cjs.map +1 -1
  88. package/dist/cjs/utils/getParentPackageJSON.cjs.map +1 -1
  89. package/dist/cjs/utils/listSpecialChars.cjs +0 -1
  90. package/dist/cjs/utils/listSpecialChars.cjs.map +1 -1
  91. package/dist/cjs/utils/mapChunksBetweenFiles.cjs.map +1 -1
  92. package/dist/cjs/utils/openBrowser.cjs.map +1 -1
  93. package/dist/cjs/utils/selectCmsEnvironment.cjs +2 -1
  94. package/dist/cjs/utils/selectCmsEnvironment.cjs.map +1 -1
  95. package/dist/cjs/utils/setupAI.cjs.map +1 -1
  96. package/dist/cjs/watch.cjs.map +1 -1
  97. package/dist/esm/IntlayerEventListener.mjs +2 -6
  98. package/dist/esm/IntlayerEventListener.mjs.map +1 -1
  99. package/dist/esm/_virtual/_utils_asset.mjs +2 -1
  100. package/dist/esm/cli.mjs.map +1 -1
  101. package/dist/esm/editor.mjs +3 -1
  102. package/dist/esm/editor.mjs.map +1 -1
  103. package/dist/esm/fill/fill.mjs +4 -2
  104. package/dist/esm/fill/fill.mjs.map +1 -1
  105. package/dist/esm/fill/formatAutoFilledFilePath.mjs +4 -1
  106. package/dist/esm/fill/formatAutoFilledFilePath.mjs.map +1 -1
  107. package/dist/esm/fill/translateDictionary.mjs +7 -6
  108. package/dist/esm/fill/translateDictionary.mjs.map +1 -1
  109. package/dist/esm/init.mjs +15 -134
  110. package/dist/esm/init.mjs.map +1 -1
  111. package/dist/esm/initSkills.mjs +2 -1
  112. package/dist/esm/initSkills.mjs.map +1 -1
  113. package/dist/esm/listContentDeclaration.mjs +2 -1
  114. package/dist/esm/listContentDeclaration.mjs.map +1 -1
  115. package/dist/esm/push/push.mjs +2 -1
  116. package/dist/esm/push/push.mjs.map +1 -1
  117. package/dist/esm/reviewDoc/reviewDoc.mjs +4 -2
  118. package/dist/esm/reviewDoc/reviewDoc.mjs.map +1 -1
  119. package/dist/esm/reviewDoc/reviewDocBlockAware.mjs +4 -2
  120. package/dist/esm/reviewDoc/reviewDocBlockAware.mjs.map +1 -1
  121. package/dist/esm/reviewDoc/reviewDocLog.mjs +6 -3
  122. package/dist/esm/reviewDoc/reviewDocLog.mjs.map +1 -1
  123. package/dist/esm/scan.mjs +2 -1
  124. package/dist/esm/scan.mjs.map +1 -1
  125. package/dist/esm/translateDoc/translateDoc.mjs +3 -2
  126. package/dist/esm/translateDoc/translateDoc.mjs.map +1 -1
  127. package/dist/esm/translateDoc/translateFile.mjs +6 -3
  128. package/dist/esm/translateDoc/translateFile.mjs.map +1 -1
  129. package/dist/esm/utils/checkFileModifiedRange.mjs +3 -2
  130. package/dist/esm/utils/checkFileModifiedRange.mjs.map +1 -1
  131. package/dist/esm/utils/getOutputFilePath.mjs +2 -1
  132. package/dist/esm/utils/getOutputFilePath.mjs.map +1 -1
  133. package/dist/esm/utils/selectCmsEnvironment.mjs +2 -1
  134. package/dist/esm/utils/selectCmsEnvironment.mjs.map +1 -1
  135. package/dist/types/init.d.ts.map +1 -1
  136. package/package.json +12 -12
package/README.md CHANGED
@@ -47,27 +47,27 @@ With **per-locale content files**, **TypeScript autocompletion**, **tree-shakabl
47
47
 
48
48
  ## Keys benefits of Intlayer:
49
49
 
50
- | Feature | Description |
51
- | --------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
52
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/frameworks.png?raw=true" alt="Feature" width="700"> | **Cross-Frameworks Support**<br><br>Intlayer is compatible with all major frameworks and libraries, including Next.js, React, Vite, Vue.js, Nuxt, Preact, Express, and more. |
53
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/javascript_content_management.jpg?raw=true" alt="Feature" width="700"> | **JavaScript-Powered Content Management**<br><br>Harness the flexibility of JavaScript to define and manage your content efficiently. <br><br> - [Content declaration](https://intlayer.org/doc/concept/content) |
54
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/per_locale_content_declaration_file.png?raw=true" alt="Feature" width="700"> | **Per-Locale Content Declaration File**<br><br>Speed up your development by declaring your content once, before auto generation.<br><br> - [Per-Locale Content Declaration File](https://intlayer.org/doc/concept/per-locale-file) |
55
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/compiler.jpg?raw=true" alt="Feature" width="700"> | **Compiler**<br><br>The Intlayer Compiler extract automatically the content from the components and generate the dictionary files.<br><br> - [Compiler](https://intlayer.org/doc/compiler) |
56
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/autocompletion.png?raw=true" alt="Feature" width="700"> | **Type-Safe Environment**<br><br>Leverage TypeScript to ensure your content definitions and code are error-free, while also benefiting from IDE autocompletion.<br><br> - [TypeScript configuration](https://intlayer.org/doc/environment/vite-and-react#configure-typescript) |
57
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/config_file.png?raw=true" alt="Feature" width="700"> | **Simplified Setup**<br><br>Get up and running quickly with minimal configuration. Adjust settings for internationalization, routing, AI, build, and content handling with ease. <br><br> - [Explore Next.js integration](https://intlayer.org/doc/environment/nextjs) |
58
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/content_retrieval.png?raw=true" alt="Feature" width="700"> | **Simplified Content Retrieval**<br><br>No need to call your `t` function for each piece of content. Retrieve all your content directly using a single hook.<br><br> - [React integration](https://intlayer.org/doc/environment/create-react-app) |
59
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/server_component.png?raw=true" alt="Feature" width="700"> | **Consistent Server Component Implementation**<br><br>Perfectly suited for Next.js server components, use the same implementation for both client and server components, no need to pass your `t` function across each server component. <br><br> - [Server Components](https://intlayer.org/doc/environment/nextjs#step-7-utilize-content-in-your-code) |
60
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/file_tree.png?raw=true" alt="Feature" width="700"> | **Organized Codebase**<br><br>Keep your codebase more organized: 1 component = 1 dictionary in the same folder. Translations close to their respective components, enhance maintainability and clarity. <br><br> - [How Intlayer works](https://intlayer.org/doc/concept/how-works-intlayer) |
61
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/url_routing.png?raw=true" alt="Feature" width="700"> | **Enhanced Routing**<br><br>Full support of app routing, adapting seamlessly to complex application structures, for Next.js, React, Vite, Vue.js, etc.<br><br> - [Explore Next.js integration](https://intlayer.org/doc/environment/nextjs) |
62
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/markdown.png?raw=true" alt="Feature" width="700"> | **Markdown Support**<br><br>Import and interpret, locale files and remote Markdown for multilingual content like privacy policies, documentation, etc. Interpret and make Markdown metadata accessible in your code.<br><br> - [Content files](https://intlayer.org/doc/concept/content/file) |
63
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/visual_editor.png?raw=true" alt="Feature" width="700"> | **Free Visual Editor & CMS**<br><br>A free visual editor and CMS are available for content writers, removing the need for a localization platform. Keep your content synchronized using Git, or externalize it totally or partially with the CMS.<br><br> - [Intlayer Editor](https://intlayer.org/doc/concept/editor) <br> - [Intlayer CMS](https://intlayer.org/doc/concept/cms) |
64
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/bundle.png?raw=true" alt="Feature" width="700"> | **Tree-shakable Content**<br><br>Tree-shakable content, reducing the size of the final bundle. Loads content per component, excluding any unused content from your bundle. Supports lazy loading to enhance app loading efficiency. <br><br> - [App build optimization](https://intlayer.org/doc/concept/how-works-intlayer#app-build-optimization) |
65
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/static_rendering.png?raw=true" alt="Feature" width="700"> | **Static Rendering**<br><br>Doesn't block Static Rendering. <br><br> - [Next.js integration](https://intlayer.org/doc/environment/nextjs) |
66
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/AI_translation.png?raw=true" alt="Feature" width="700"> | **AI-Powered Translation**<br><br>Transform your website into 231 languages with just one click using Intlayer's advanced AI-powered translation tools using your own AI provider / API key. <br><br> - [CI/CD integration](https://intlayer.org/doc/concept/ci-cd) <br> - [Intlayer CLI](https://intlayer.org/doc/concept/cli) <br> - [Auto fill](https://intlayer.org/doc/concept/auto-fill) |
67
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/mcp.png?raw=true" alt="Feature" width="700"> | **MCP Server Integration**<br><br>Provides an MCP (Model Context Protocol) server for IDE automation, enabling seamless content management and i18n workflows directly within your development environment. <br><br> - [MCP Server](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/mcp_server.md) |
68
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/vscode_extension.png?raw=true" alt="Feature" width="700"> | **VSCode Extension**<br><br>Intlayer provides a VSCode extension to help you manage your content and translations, building your dictionaries, translating your content, and more. <br><br> - [VSCode Extension](https://intlayer.org/doc/vs-code-extension) |
69
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/interoperability.png?raw=true" alt="Feature" width="700"> | **Interoperability**<br><br>Allow interoperability with react-i18next, next-i18next, next-intl, react-intl, vue-i18n. <br><br> - [Intlayer and react-intl](https://intlayer.org/blog/intlayer-with-react-intl) <br> - [Intlayer and next-intl](https://intlayer.org/blog/intlayer-with-next-intl) <br> - [Intlayer and next-i18next](https://intlayer.org/blog/intlayer-with-next-i18next) <br> - [Intlayer and vue-i18n](https://intlayer.org/blog/intlayer-with-vue-i18n) |
70
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/benchmark.png?raw=true" alt="Feature" width="700"> | **Performances & Benchmark**<br><br>Uses advanced tree-shaking and dynamic loading to boost performances and keep the solution as light as possible. <br><br> - [Performances & Benchmark](https://intlayer.org/doc/benchmark) |
50
+ | Feature | Description |
51
+ | --------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
52
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/frameworks.png?raw=true" alt="Feature" width="700"> | **Cross-Frameworks Support**<br><br>Intlayer is compatible with all major frameworks and libraries, including Next.js, React, Vite, Vue.js, Nuxt, Preact, Express, and more. |
53
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/javascript_content_management.jpg?raw=true" alt="Feature" width="700"> | **JavaScript-Powered Content Management**<br><br>Harness the flexibility of JavaScript to define and manage your content efficiently. <br><br> - [Content declaration](https://intlayer.org/doc/concept/content) |
54
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/per_locale_content_declaration_file.png?raw=true" alt="Feature" width="700"> | **Per-Locale Content Declaration File**<br><br>Speed up your development by declaring your content once, before auto generation.<br><br> - [Per-Locale Content Declaration File](https://intlayer.org/doc/concept/per-locale-file) |
55
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/compiler.jpg?raw=true" alt="Feature" width="700"> | **Compiler**<br><br>The Intlayer Compiler extract automatically the content from the components and generate the dictionary files.<br><br> - [Compiler](https://intlayer.org/doc/compiler) |
56
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/autocompletion.png?raw=true" alt="Feature" width="700"> | **Type-Safe Environment**<br><br>Leverage TypeScript to ensure your content definitions and code are error-free, while also benefiting from IDE autocompletion.<br><br> - [TypeScript configuration](https://intlayer.org/doc/environment/vite-and-react#configure-typescript) |
57
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/config_file.png?raw=true" alt="Feature" width="700"> | **Simplified Setup**<br><br>Get up and running quickly with minimal configuration. Adjust settings for internationalization, routing, AI, build, and content handling with ease. <br><br> - [Explore Next.js integration](https://intlayer.org/doc/environment/nextjs) |
58
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/content_retrieval.png?raw=true" alt="Feature" width="700"> | **Simplified Content Retrieval**<br><br>No need to call your `t` function for each piece of content. Retrieve all your content directly using a single hook.<br><br> - [React integration](https://intlayer.org/doc/environment/create-react-app) |
59
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/server_component.png?raw=true" alt="Feature" width="700"> | **Consistent Server Component Implementation**<br><br>Perfectly suited for Next.js server components, use the same implementation for both client and server components, no need to pass your `t` function across each server component. <br><br> - [Server Components](https://intlayer.org/doc/environment/nextjs#step-7-utilize-content-in-your-code) |
60
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/file_tree.png?raw=true" alt="Feature" width="700"> | **Organized Codebase**<br><br>Keep your codebase more organized: 1 component = 1 dictionary in the same folder. Translations close to their respective components, enhance maintainability and clarity. <br><br> - [How Intlayer works](https://intlayer.org/doc/concept/how-works-intlayer) |
61
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/url_routing.png?raw=true" alt="Feature" width="700"> | **Enhanced Routing**<br><br>Full support of app routing, adapting seamlessly to complex application structures, for Next.js, React, Vite, Vue.js, etc.<br><br> - [Explore Next.js integration](https://intlayer.org/doc/environment/nextjs) |
62
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/markdown.png?raw=true" alt="Feature" width="700"> | **Markdown Support**<br><br>Import and interpret, locale files and remote Markdown for multilingual content like privacy policies, documentation, etc. Interpret and make Markdown metadata accessible in your code.<br><br> - [Content files](https://intlayer.org/doc/concept/content/file) |
63
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/visual_editor.png?raw=true" alt="Feature" width="700"> | **Free Visual Editor & CMS**<br><br>A free visual editor and CMS are available for content writers, removing the need for a localization platform. Keep your content synchronized using Git, or externalize it totally or partially with the CMS.<br><br> - [Intlayer Editor](https://intlayer.org/doc/concept/editor) <br> - [Intlayer CMS](https://intlayer.org/doc/concept/cms) |
64
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/bundle.png?raw=true" alt="Feature" width="700"> | **Tree-shakable Content**<br><br>Tree-shakable content, reducing the size of the final bundle. Loads content per component, excluding any unused content from your bundle. Supports lazy loading to enhance app loading efficiency. <br><br> - [App build optimization](https://intlayer.org/doc/concept/how-works-intlayer#app-build-optimization) |
65
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/static_rendering.png?raw=true" alt="Feature" width="700"> | **Static Rendering**<br><br>Doesn't block Static Rendering. <br><br> - [Next.js integration](https://intlayer.org/doc/environment/nextjs) |
66
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/AI_translation.png?raw=true" alt="Feature" width="700"> | **AI-Powered Translation**<br><br>Transform your website into 231 languages with just one click using Intlayer's advanced AI-powered translation tools using your own AI provider / API key. <br><br> - [CI/CD integration](https://intlayer.org/doc/concept/ci-cd) <br> - [Intlayer CLI](https://intlayer.org/doc/concept/cli) <br> - [Auto fill](https://intlayer.org/doc/concept/auto-fill) |
67
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/mcp.png?raw=true" alt="Feature" width="700"> | **MCP Server Integration**<br><br>Provides an MCP (Model Context Protocol) server for IDE automation, enabling seamless content management and i18n workflows directly within your development environment. <br><br> - [MCP Server](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/mcp_server.md) |
68
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/vscode_extension.png?raw=true" alt="Feature" width="700"> | **VSCode Extension**<br><br>Intlayer provides a VSCode extension to help you manage your content and translations, building your dictionaries, translating your content, and more. <br><br> - [VSCode Extension](https://intlayer.org/doc/vs-code-extension) |
69
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/interoperability.png?raw=true" alt="Feature" width="700"> | **Interoperability**<br><br>Allow interoperability with react-i18next, next-i18next, next-intl, react-intl, vue-i18n. <br><br> - [Intlayer and react-intl](https://intlayer.org/blog/intlayer-with-react-intl) <br> - [Intlayer and next-intl](https://intlayer.org/blog/intlayer-with-next-intl) <br> - [Intlayer and next-i18next](https://intlayer.org/blog/intlayer-with-next-i18next) <br> - [Intlayer and vue-i18n](https://intlayer.org/blog/intlayer-with-vue-i18n) <br> - [Intlayer compat adapters](https://intlayer.org/doc/compatibility) |
70
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/benchmark.png?raw=true" alt="Feature" width="700"> | **Performances & Benchmark**<br><br>Uses advanced tree-shaking and dynamic loading to boost performances and keep the solution as light as possible. <br><br> - [Performances & Benchmark](https://intlayer.org/doc/benchmark) |
71
71
 
72
72
  ---
73
73
 
@@ -162,7 +162,7 @@ Explore our comprehensive documentation to get started with Intlayer and learn h
162
162
  <li><a href="https://intlayer.org/doc/concept/cms" rel=''>Intlayer CMS</a></li>
163
163
  <li><a href="https://intlayer.org/doc/concept/content" rel=''>Dictionary</a>
164
164
  <ul>
165
- <li><a href="https://intlayer.org/doc/concept/content/per-locale-file" rel=''>Per-Locale Content Declaration File</a></li>
165
+ <li><a href="https://intlayer.org/doc/concept/per-locale-file" rel=''>Per-Locale Content Declaration File</a></li>
166
166
  <li><a href="https://intlayer.org/doc/concept/content/translation" rel=''>Translation</a></li>
167
167
  <li><a href="https://intlayer.org/doc/concept/content/enumeration" rel=''>Enumeration</a></li>
168
168
  <li><a href="https://intlayer.org/doc/concept/content/condition" rel=''>Condition</a></li>
@@ -183,7 +183,7 @@ Explore our comprehensive documentation to get started with Intlayer and learn h
183
183
  <ul>
184
184
  <li><a href="https://intlayer.org/doc/environment/nextjs/15" rel=''>Next.js 15</a></li>
185
185
  <li><a href="https://intlayer.org/doc/environment/nextjs/14" rel=''>Next.js 14 (App Router)</a></li>
186
- <li><a href="https://intlayer.org/doc/environment/nextjs/next-with-Page-Router" rel=''>Next.js Page Router</a></li>
186
+ <li><a href="https://intlayer.org/doc/environment/nextjs/next-with-page-router" rel=''>Next.js Page Router</a></li>
187
187
  <li><a href="https://intlayer.org/doc/environment/nextjs/compiler" rel=''>Next.js using Compiler</a></li>
188
188
  </ul>
189
189
  </li>
@@ -212,7 +212,7 @@ Explore our comprehensive documentation to get started with Intlayer and learn h
212
212
  <li><a href="https://intlayer.org/doc/environment/sveltekit" rel=''>SvelteKit</a></li>
213
213
  <li><a href="https://intlayer.org/doc/environment/vite-and-preact" rel=''>Vite + Preact</a></li>
214
214
  <li><a href="https://intlayer.org/doc/environment/vite-and-vue" rel=''>Vite + Vue</a></li>
215
- <li><a href="https://intlayer.org/doc/environment/vite-and-nuxt" rel=''>Vite + Nuxt</a></li>
215
+ <li><a href="https://intlayer.org/doc/environment/nuxt-and-vue" rel=''>Vite + Nuxt</a></li>
216
216
  <li><a href="https://intlayer.org/doc/environment/vite-and-solid" rel=''>Vite + Solid</a></li>
217
217
  <li><a href="https://intlayer.org/doc/environment/angular" rel=''>Angular</a></li>
218
218
  <li>
@@ -151,14 +151,10 @@ var IntlayerEventListener = class {
151
151
  case "DELETED":
152
152
  await this.onDictionaryDeleted?.(dataEl.data);
153
153
  break;
154
- default:
155
- this.appLogger(["Unhandled dictionary status:", dataEl.status], { level: "error" });
156
- break;
154
+ default: this.appLogger(["Unhandled dictionary status:", dataEl.status], { level: "error" });
157
155
  }
158
156
  break;
159
- default:
160
- this.appLogger(["Unknown object type:", dataEl.object], { level: "error" });
161
- break;
157
+ default: this.appLogger(["Unknown object type:", dataEl.object], { level: "error" });
162
158
  }
163
159
  } catch (error) {
164
160
  this.appLogger(["Error processing dictionary update:", error], { level: "error" });
@@ -1 +1 @@
1
- {"version":3,"file":"IntlayerEventListener.cjs","names":["EventSource"],"sources":["../../src/IntlayerEventListener.ts"],"sourcesContent":["import { getIntlayerAPIProxy } from '@intlayer/api';\n// @ts-ignore: @intlayer/backend is not built yet\nimport type { DictionaryAPI, MessageEventData } from '@intlayer/backend';\nimport { editor, log } from '@intlayer/config/built';\nimport { getAppLogger } from '@intlayer/config/logger';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { EventSource } from 'eventsource';\n\nexport type IntlayerMessageEvent = MessageEvent;\n\n/**\n * IntlayerEventListener class to listen for dictionary changes via SSE (Server-Sent Events).\n *\n * Usage example:\n *\n * import { buildIntlayerDictionary } from './transpiler/declaration_file_to_dictionary/intlayer_dictionary';\n * import { IntlayerEventListener } from '@intlayer/api';\n *\n * export const checkDictionaryChanges = async () => {\n * // Instantiate the listener\n * const eventListener = new IntlayerEventListener();\n *\n * // Set up your callbacks\n * eventListener.onDictionaryChange = async (dictionary) => {\n * await buildIntlayerDictionary(dictionary);\n * };\n *\n * // Initialize the listener\n * await eventListener.initialize();\n *\n * // Optionally, clean up later when you’re done\n * // eventListener.cleanup();\n * };\n */\nexport class IntlayerEventListener {\n private appLogger = getAppLogger({ log });\n\n private eventSource: EventSource | null = null;\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 5;\n private reconnectDelay = 1000; // Start with 1 second\n private isManuallyDisconnected = false;\n private reconnectTimeout: NodeJS.Timeout | null = null;\n\n /**\n * Callback triggered when a Dictionary is ADDED.\n */\n public onDictionaryAdded?: (dictionary: DictionaryAPI) => any;\n\n /**\n * Callback triggered when a Dictionary is UPDATED.\n */\n public onDictionaryChange?: (dictionary: DictionaryAPI) => any;\n\n /**\n * Callback triggered when a Dictionary is DELETED.\n */\n public onDictionaryDeleted?: (dictionary: DictionaryAPI) => any;\n\n /**\n * Callback triggered when connection is established or re-established.\n */\n public onConnectionOpen?: () => any;\n\n /**\n * Callback triggered when connection encounters an error.\n */\n public onConnectionError?: (error: Event) => any;\n\n constructor(\n private intlayerConfig: Pick<IntlayerConfig, 'log' | 'editor'> = {\n log,\n editor,\n }\n ) {\n this.appLogger = getAppLogger(this.intlayerConfig);\n }\n\n /**\n * Initializes the EventSource connection using the given intlayerConfig\n * (or the default config if none was provided).\n */\n public async initialize(): Promise<void> {\n this.isManuallyDisconnected = false;\n await this.connect();\n }\n\n /**\n * Establishes the EventSource connection with automatic reconnection support.\n */\n private async connect(): Promise<void> {\n try {\n const backendURL = this.intlayerConfig.editor.backendURL;\n\n // Retrieve the access token via proxy\n const accessToken = await getIntlayerAPIProxy(\n undefined,\n this.intlayerConfig\n ).oAuth.getOAuth2AccessToken();\n\n if (!accessToken) {\n throw new Error('Failed to retrieve access token');\n }\n\n const API_ROUTE = `${backendURL}/api/event-listener`;\n\n // Close existing connection if any\n if (this.eventSource) {\n this.eventSource.close();\n }\n\n this.eventSource = new EventSource(API_ROUTE, {\n fetch: (input, init) =>\n fetch(input, {\n ...init,\n headers: {\n ...(init?.headers ?? {}),\n Authorization: `Bearer ${accessToken.data?.accessToken}`,\n },\n }),\n });\n\n this.eventSource.onopen = () => {\n this.reconnectAttempts = 0;\n this.reconnectDelay = 1000; // Reset delay\n this.onConnectionOpen?.();\n };\n\n this.eventSource.onmessage = (event) => this.handleMessage(event);\n this.eventSource.onerror = (event) => this.handleError(event);\n } catch (_error) {\n this.appLogger('Failed to establish EventSource connection:', {\n level: 'error',\n });\n this.scheduleReconnect();\n }\n }\n\n /**\n * Cleans up (closes) the EventSource connection.\n */\n public cleanup(): void {\n this.isManuallyDisconnected = true;\n\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n\n if (this.eventSource) {\n this.eventSource.close();\n this.eventSource = null;\n }\n }\n\n /**\n * Schedules a reconnection attempt with exponential backoff.\n */\n private scheduleReconnect(): void {\n if (\n this.isManuallyDisconnected ||\n this.reconnectAttempts >= this.maxReconnectAttempts\n ) {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n this.appLogger(\n [\n `Max reconnection attempts (${this.maxReconnectAttempts}) reached. Giving up.`,\n ],\n {\n level: 'error',\n }\n );\n }\n return;\n }\n\n this.reconnectAttempts++;\n const delay = this.reconnectDelay * 2 ** (this.reconnectAttempts - 1); // Exponential backoff\n\n this.appLogger(\n `Scheduling reconnection attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts} in ${delay}ms`\n );\n\n this.reconnectTimeout = setTimeout(async () => {\n if (!this.isManuallyDisconnected) {\n await this.connect();\n }\n }, delay);\n }\n\n /**\n * Handles incoming SSE messages, parses the event data,\n * and invokes the appropriate callback.\n */\n private async handleMessage(event: IntlayerMessageEvent): Promise<void> {\n try {\n const { data } = event;\n\n const dataJSON: MessageEventData[] = JSON.parse(data);\n\n for (const dataEl of dataJSON) {\n switch (dataEl.object) {\n case 'DICTIONARY':\n switch (dataEl.status) {\n case 'ADDED':\n await this.onDictionaryAdded?.(dataEl.data);\n break;\n case 'UPDATED':\n await this.onDictionaryChange?.(dataEl.data);\n break;\n case 'DELETED':\n await this.onDictionaryDeleted?.(dataEl.data);\n break;\n default:\n this.appLogger(\n ['Unhandled dictionary status:', dataEl.status],\n {\n level: 'error',\n }\n );\n break;\n }\n break;\n default:\n this.appLogger(['Unknown object type:', dataEl.object], {\n level: 'error',\n });\n break;\n }\n }\n } catch (error) {\n this.appLogger(['Error processing dictionary update:', error], {\n level: 'error',\n });\n }\n }\n\n /**\n * Handles any SSE errors and attempts reconnection if appropriate.\n */\n private handleError(event: Event): void {\n const errorEvent = event as any;\n\n // Log detailed error information\n this.appLogger(\n [\n 'EventSource error:',\n {\n type: errorEvent.type,\n message: errorEvent.message,\n code: errorEvent.code,\n readyState: this.eventSource?.readyState,\n url: this.eventSource?.url,\n },\n ],\n {\n level: 'error',\n }\n );\n\n // Notify error callback\n this.onConnectionError?.(event);\n\n // Check if this is a connection close error\n const isConnectionClosed =\n errorEvent.type === 'error' &&\n (errorEvent.message?.includes('terminated') ||\n errorEvent.message?.includes('closed') ||\n this.eventSource?.readyState === EventSource.CLOSED);\n\n if (isConnectionClosed && !this.isManuallyDisconnected) {\n this.appLogger(\n 'Connection was terminated by server, attempting to reconnect...'\n );\n this.scheduleReconnect();\n } else {\n // For other types of errors, close the connection\n this.cleanup();\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"IntlayerEventListener.cjs","names":["getAppLogger","getIntlayerAPIProxy","EventSource"],"sources":["../../src/IntlayerEventListener.ts"],"sourcesContent":["import { getIntlayerAPIProxy } from '@intlayer/api';\n// @ts-ignore: @intlayer/backend is not built yet\nimport type { DictionaryAPI, MessageEventData } from '@intlayer/backend';\nimport { editor, log } from '@intlayer/config/built';\nimport { getAppLogger } from '@intlayer/config/logger';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { EventSource } from 'eventsource';\n\nexport type IntlayerMessageEvent = MessageEvent;\n\n/**\n * IntlayerEventListener class to listen for dictionary changes via SSE (Server-Sent Events).\n *\n * Usage example:\n *\n * import { buildIntlayerDictionary } from './transpiler/declaration_file_to_dictionary/intlayer_dictionary';\n * import { IntlayerEventListener } from '@intlayer/api';\n *\n * export const checkDictionaryChanges = async () => {\n * // Instantiate the listener\n * const eventListener = new IntlayerEventListener();\n *\n * // Set up your callbacks\n * eventListener.onDictionaryChange = async (dictionary) => {\n * await buildIntlayerDictionary(dictionary);\n * };\n *\n * // Initialize the listener\n * await eventListener.initialize();\n *\n * // Optionally, clean up later when you’re done\n * // eventListener.cleanup();\n * };\n */\nexport class IntlayerEventListener {\n private appLogger = getAppLogger({ log });\n\n private eventSource: EventSource | null = null;\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 5;\n private reconnectDelay = 1000; // Start with 1 second\n private isManuallyDisconnected = false;\n private reconnectTimeout: NodeJS.Timeout | null = null;\n\n /**\n * Callback triggered when a Dictionary is ADDED.\n */\n public onDictionaryAdded?: (dictionary: DictionaryAPI) => any;\n\n /**\n * Callback triggered when a Dictionary is UPDATED.\n */\n public onDictionaryChange?: (dictionary: DictionaryAPI) => any;\n\n /**\n * Callback triggered when a Dictionary is DELETED.\n */\n public onDictionaryDeleted?: (dictionary: DictionaryAPI) => any;\n\n /**\n * Callback triggered when connection is established or re-established.\n */\n public onConnectionOpen?: () => any;\n\n /**\n * Callback triggered when connection encounters an error.\n */\n public onConnectionError?: (error: Event) => any;\n\n constructor(\n private intlayerConfig: Pick<IntlayerConfig, 'log' | 'editor'> = {\n log,\n editor,\n }\n ) {\n this.appLogger = getAppLogger(this.intlayerConfig);\n }\n\n /**\n * Initializes the EventSource connection using the given intlayerConfig\n * (or the default config if none was provided).\n */\n public async initialize(): Promise<void> {\n this.isManuallyDisconnected = false;\n await this.connect();\n }\n\n /**\n * Establishes the EventSource connection with automatic reconnection support.\n */\n private async connect(): Promise<void> {\n try {\n const backendURL = this.intlayerConfig.editor.backendURL;\n\n // Retrieve the access token via proxy\n const accessToken = await getIntlayerAPIProxy(\n undefined,\n this.intlayerConfig\n ).oAuth.getOAuth2AccessToken();\n\n if (!accessToken) {\n throw new Error('Failed to retrieve access token');\n }\n\n const API_ROUTE = `${backendURL}/api/event-listener`;\n\n // Close existing connection if any\n if (this.eventSource) {\n this.eventSource.close();\n }\n\n this.eventSource = new EventSource(API_ROUTE, {\n fetch: (input, init) =>\n fetch(input, {\n ...init,\n headers: {\n ...(init?.headers ?? {}),\n Authorization: `Bearer ${accessToken.data?.accessToken}`,\n },\n }),\n });\n\n this.eventSource.onopen = () => {\n this.reconnectAttempts = 0;\n this.reconnectDelay = 1000; // Reset delay\n this.onConnectionOpen?.();\n };\n\n this.eventSource.onmessage = (event) => this.handleMessage(event);\n this.eventSource.onerror = (event) => this.handleError(event);\n } catch (_error) {\n this.appLogger('Failed to establish EventSource connection:', {\n level: 'error',\n });\n this.scheduleReconnect();\n }\n }\n\n /**\n * Cleans up (closes) the EventSource connection.\n */\n public cleanup(): void {\n this.isManuallyDisconnected = true;\n\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n\n if (this.eventSource) {\n this.eventSource.close();\n this.eventSource = null;\n }\n }\n\n /**\n * Schedules a reconnection attempt with exponential backoff.\n */\n private scheduleReconnect(): void {\n if (\n this.isManuallyDisconnected ||\n this.reconnectAttempts >= this.maxReconnectAttempts\n ) {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n this.appLogger(\n [\n `Max reconnection attempts (${this.maxReconnectAttempts}) reached. Giving up.`,\n ],\n {\n level: 'error',\n }\n );\n }\n return;\n }\n\n this.reconnectAttempts++;\n const delay = this.reconnectDelay * 2 ** (this.reconnectAttempts - 1); // Exponential backoff\n\n this.appLogger(\n `Scheduling reconnection attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts} in ${delay}ms`\n );\n\n this.reconnectTimeout = setTimeout(async () => {\n if (!this.isManuallyDisconnected) {\n await this.connect();\n }\n }, delay);\n }\n\n /**\n * Handles incoming SSE messages, parses the event data,\n * and invokes the appropriate callback.\n */\n private async handleMessage(event: IntlayerMessageEvent): Promise<void> {\n try {\n const { data } = event;\n\n const dataJSON: MessageEventData[] = JSON.parse(data);\n\n for (const dataEl of dataJSON) {\n switch (dataEl.object) {\n case 'DICTIONARY':\n switch (dataEl.status) {\n case 'ADDED':\n await this.onDictionaryAdded?.(dataEl.data);\n break;\n case 'UPDATED':\n await this.onDictionaryChange?.(dataEl.data);\n break;\n case 'DELETED':\n await this.onDictionaryDeleted?.(dataEl.data);\n break;\n default:\n this.appLogger(\n ['Unhandled dictionary status:', dataEl.status],\n {\n level: 'error',\n }\n );\n break;\n }\n break;\n default:\n this.appLogger(['Unknown object type:', dataEl.object], {\n level: 'error',\n });\n break;\n }\n }\n } catch (error) {\n this.appLogger(['Error processing dictionary update:', error], {\n level: 'error',\n });\n }\n }\n\n /**\n * Handles any SSE errors and attempts reconnection if appropriate.\n */\n private handleError(event: Event): void {\n const errorEvent = event as any;\n\n // Log detailed error information\n this.appLogger(\n [\n 'EventSource error:',\n {\n type: errorEvent.type,\n message: errorEvent.message,\n code: errorEvent.code,\n readyState: this.eventSource?.readyState,\n url: this.eventSource?.url,\n },\n ],\n {\n level: 'error',\n }\n );\n\n // Notify error callback\n this.onConnectionError?.(event);\n\n // Check if this is a connection close error\n const isConnectionClosed =\n errorEvent.type === 'error' &&\n (errorEvent.message?.includes('terminated') ||\n errorEvent.message?.includes('closed') ||\n this.eventSource?.readyState === EventSource.CLOSED);\n\n if (isConnectionClosed && !this.isManuallyDisconnected) {\n this.appLogger(\n 'Connection was terminated by server, attempting to reconnect...'\n );\n this.scheduleReconnect();\n } else {\n // For other types of errors, close the connection\n this.cleanup();\n }\n }\n}\n"],"mappings":"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"}
@@ -19,11 +19,10 @@ var __copyProps = (to, from, except, desc) => {
19
19
  }
20
20
  return to;
21
21
  };
22
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
23
23
  value: mod,
24
24
  enumerable: true
25
25
  }) : target, mod));
26
26
 
27
27
  //#endregion
28
-
29
28
  exports.__toESM = __toESM;
@@ -70,10 +70,11 @@ const readAsset = (relPath, encoding = "utf8") => {
70
70
  const distRoot = findDistRoot(here) ?? (0, node_path.resolve)(here, "..", "..", "dist");
71
71
  const assetsRoot = (0, node_path.join)(distRoot, "assets");
72
72
  const tried = [];
73
+ const callerDirAbs = getCallerDir();
73
74
  /**
74
75
  * Transform dist/(esm|cjs)/... and _virtual/ prefix to clean subpath (Windows-safe)
75
76
  */
76
- const callerSubpath = (0, node_path.relative)(distRoot, getCallerDir()).split("\\").join("/").replace(/^(?:dist\/)?(?:esm|cjs)\//, "").replace(/^_virtual\//, "");
77
+ const callerSubpath = (0, node_path.relative)(distRoot, callerDirAbs).split("\\").join("/").replace(/^(?:dist\/)?(?:esm|cjs)\//, "").replace(/^_virtual\//, "");
77
78
  if (relPath.startsWith("./") || relPath.startsWith("../")) {
78
79
  const fromCallerAbs = (0, node_path.resolve)(assetsRoot, callerSubpath, relPath);
79
80
  tried.push(fromCallerAbs);
@@ -1 +1 @@
1
- {"version":3,"file":"login.cjs","names":["http","URL","ANSIColors","writeCliSessionToken"],"sources":["../../../src/auth/login.ts"],"sourcesContent":["import http from 'node:http';\nimport { URL } from 'node:url';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { logConfigDetails } from '@intlayer/engine/cli';\nimport { openBrowser } from '../utils/openBrowser';\nimport { writeCliSessionToken } from './sessionToken';\n\nconst buildSuccessHtml = (message: string): string => `\n <!DOCTYPE html>\n <html lang=\"en\" data-theme=\"dark\">\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Intlayer CLI Login</title>\n <style>\n :root {\n --color-background: rgba(23, 23, 23);\n --color-card: rgba(39, 39, 39);\n --color-text: rgba(255, 245, 237);\n --color-neutral: rgba(93, 93, 93);\n --font-sans: \"Inter\", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n }\n * { box-sizing: border-box; }\n body {\n font-family: var(--font-sans);\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n margin: 0;\n padding: 1rem;\n background-color: var(--color-background);\n color: var(--color-text);\n }\n .container {\n text-align: center;\n padding: 2rem;\n border-radius: 1rem;\n background-color: var(--color-card);\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n max-width: 400px;\n width: 100%;\n }\n h1 { margin: 0 0 1rem 0; font-size: 1.5rem; font-weight: 700; color: var(--color-text); }\n p { color: var(--color-neutral); font-size: 0.8rem; margin: 0 0 1.5rem 0; line-height: 1.5; }\n </style>\n </head>\n <body>\n <div class=\"container\">\n <h1>Login Successful</h1>\n <p>${message}</p>\n </div>\n <script>\n window.close();\n setTimeout(() => { window.close(); }, 1000);\n </script>\n </body>\n </html>\n`;\n\ntype LoginOptions = {\n cmsUrl?: string;\n configOptions?: GetConfigurationOptions;\n /**\n * When false, do not call process.exit(0) after a successful login so the\n * caller can continue (e.g. retry a command inline, or finish an interactive\n * setup). Defaults to true to preserve existing standalone-login behaviour.\n */\n exitAfter?: boolean;\n /**\n * Invoked with the access-key credentials when the login flow returns a\n * `clientId` / `clientSecret` pair. When provided, the caller takes ownership\n * of persisting the credentials (e.g. writing them to `.env` and enabling the\n * editor in the config), so the default manual-setup instructions are skipped.\n */\n onCredentials?: (credentials: {\n clientId: string;\n clientSecret: string;\n }) => void | Promise<void>;\n};\n\nexport const login = async (options: LoginOptions = {}) => {\n const configuration = getConfiguration(options.configOptions);\n logConfigDetails(options?.configOptions);\n\n const logger = getAppLogger(configuration);\n\n const cmsUrl = options.cmsUrl ?? configuration.editor.cmsURL;\n\n return new Promise<void>((resolve) => {\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url ?? '', `http://${req.headers.host}`);\n\n // Set CORS headers\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (url.pathname === '/callback') {\n const sessionToken = url.searchParams.get('sessionToken');\n const sessionExpiresAt = url.searchParams.get('expiresAt');\n const clientId = url.searchParams.get('clientId');\n const clientSecret = url.searchParams.get('clientSecret');\n\n if (sessionToken && sessionExpiresAt) {\n logger('');\n logger(\n `Log in successful. ${colorize('2h', ANSIColors.BLUE)} session token received.`\n );\n logger('');\n\n logger(\n colorize(\n `Token expires at: ${new Date(sessionExpiresAt).toLocaleString()}`,\n ANSIColors.GREY\n )\n );\n\n await writeCliSessionToken(\n configuration,\n sessionToken,\n new Date(sessionExpiresAt)\n );\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(\n buildSuccessHtml(\n 'Your 2h session token has been stored. You can now close this tab and return to your terminal.'\n )\n );\n\n server.close(() => {\n resolve();\n if (options.exitAfter !== false) {\n process.exit(0);\n }\n });\n return;\n }\n\n if (clientId && clientSecret) {\n if (options.onCredentials) {\n // The caller persists the credentials itself (e.g. interactive\n // init writing `.env` and enabling the editor in the config).\n logger('');\n logger('Log in successful. Configuring Intlayer CMS...');\n\n try {\n await options.onCredentials({ clientId, clientSecret });\n } catch (error) {\n logger(\n `Failed to persist the CMS credentials automatically: ${\n error instanceof Error ? error.message : String(error)\n }`,\n { level: 'error' }\n );\n }\n } else {\n logger('');\n logger('Log in successful. Client ID and Client Secret received.');\n\n logger('');\n logger([\n '1. Insert the Client ID and Client Secret in your',\n colorizePath('.env'),\n 'file:',\n ]);\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n logger(\n [\n colorize('INTLAYER_CLIENT_ID=', ANSIColors.GREY_LIGHT),\n colorize(clientId, ANSIColors.BLUE),\n ].join('')\n );\n logger(\n [\n colorize('INTLAYER_CLIENT_SECRET=', ANSIColors.GREY_LIGHT),\n colorize(clientSecret, ANSIColors.BLUE),\n ].join('')\n );\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n logger('');\n logger('2. Insert in your Intlayer configuration file:');\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n [\n `${ANSIColors.GREY_LIGHT}{`,\n ` editor: {`,\n ` enabled: true,`,\n ` cmsURL: '${colorizePath(cmsUrl!, undefined, ANSIColors.GREY_LIGHT)}',`,\n ` clientId: '${colorize('process.env.INTLAYER_CLIENT_ID', ANSIColors.BLUE, ANSIColors.GREY_LIGHT)}',`,\n ` clientSecret: '${colorize('process.env.INTLAYER_CLIENT_SECRET', ANSIColors.BLUE, ANSIColors.GREY_LIGHT)}',`,\n ` },`,\n `}`,\n ].forEach((line) => {\n logger(line);\n });\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(\n buildSuccessHtml(\n 'You have successfully logged in to Intlayer CLI. You can now close this tab and return to your terminal.'\n )\n );\n\n server.close(() => {\n resolve();\n if (options.exitAfter !== false) {\n process.exit(0);\n }\n });\n } else {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end('Missing parameters');\n }\n } else {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not found');\n }\n });\n\n server.listen(0, () => {\n const address = server.address();\n const port = typeof address === 'object' && address ? address.port : 0;\n const state = Math.random().toString(36).substring(7);\n\n const websiteUrl =\n cmsUrl ?? process.env.INTLAYER_SITE_URL ?? 'http://localhost:3000';\n const backendUrl = configuration.editor.backendURL ?? '';\n const loginUrl = `${websiteUrl}/auth/cli-login?port=${port}&state=${state}&backendUrl=${encodeURIComponent(backendUrl)}`;\n\n logger('Opening browser for login...');\n logger(`If browser does not open, visit: ${colorizePath(loginUrl)}`);\n\n openBrowser(loginUrl);\n });\n });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"login.cjs","names":["getConfiguration","getAppLogger","http","URL","colorize","ANSIColors","writeCliSessionToken","colorizePath"],"sources":["../../../src/auth/login.ts"],"sourcesContent":["import http from 'node:http';\nimport { URL } from 'node:url';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { logConfigDetails } from '@intlayer/engine/cli';\nimport { openBrowser } from '../utils/openBrowser';\nimport { writeCliSessionToken } from './sessionToken';\n\nconst buildSuccessHtml = (message: string): string => `\n <!DOCTYPE html>\n <html lang=\"en\" data-theme=\"dark\">\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Intlayer CLI Login</title>\n <style>\n :root {\n --color-background: rgba(23, 23, 23);\n --color-card: rgba(39, 39, 39);\n --color-text: rgba(255, 245, 237);\n --color-neutral: rgba(93, 93, 93);\n --font-sans: \"Inter\", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n }\n * { box-sizing: border-box; }\n body {\n font-family: var(--font-sans);\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n margin: 0;\n padding: 1rem;\n background-color: var(--color-background);\n color: var(--color-text);\n }\n .container {\n text-align: center;\n padding: 2rem;\n border-radius: 1rem;\n background-color: var(--color-card);\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n max-width: 400px;\n width: 100%;\n }\n h1 { margin: 0 0 1rem 0; font-size: 1.5rem; font-weight: 700; color: var(--color-text); }\n p { color: var(--color-neutral); font-size: 0.8rem; margin: 0 0 1.5rem 0; line-height: 1.5; }\n </style>\n </head>\n <body>\n <div class=\"container\">\n <h1>Login Successful</h1>\n <p>${message}</p>\n </div>\n <script>\n window.close();\n setTimeout(() => { window.close(); }, 1000);\n </script>\n </body>\n </html>\n`;\n\ntype LoginOptions = {\n cmsUrl?: string;\n configOptions?: GetConfigurationOptions;\n /**\n * When false, do not call process.exit(0) after a successful login so the\n * caller can continue (e.g. retry a command inline, or finish an interactive\n * setup). Defaults to true to preserve existing standalone-login behaviour.\n */\n exitAfter?: boolean;\n /**\n * Invoked with the access-key credentials when the login flow returns a\n * `clientId` / `clientSecret` pair. When provided, the caller takes ownership\n * of persisting the credentials (e.g. writing them to `.env` and enabling the\n * editor in the config), so the default manual-setup instructions are skipped.\n */\n onCredentials?: (credentials: {\n clientId: string;\n clientSecret: string;\n }) => void | Promise<void>;\n};\n\nexport const login = async (options: LoginOptions = {}) => {\n const configuration = getConfiguration(options.configOptions);\n logConfigDetails(options?.configOptions);\n\n const logger = getAppLogger(configuration);\n\n const cmsUrl = options.cmsUrl ?? configuration.editor.cmsURL;\n\n return new Promise<void>((resolve) => {\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url ?? '', `http://${req.headers.host}`);\n\n // Set CORS headers\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (url.pathname === '/callback') {\n const sessionToken = url.searchParams.get('sessionToken');\n const sessionExpiresAt = url.searchParams.get('expiresAt');\n const clientId = url.searchParams.get('clientId');\n const clientSecret = url.searchParams.get('clientSecret');\n\n if (sessionToken && sessionExpiresAt) {\n logger('');\n logger(\n `Log in successful. ${colorize('2h', ANSIColors.BLUE)} session token received.`\n );\n logger('');\n\n logger(\n colorize(\n `Token expires at: ${new Date(sessionExpiresAt).toLocaleString()}`,\n ANSIColors.GREY\n )\n );\n\n await writeCliSessionToken(\n configuration,\n sessionToken,\n new Date(sessionExpiresAt)\n );\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(\n buildSuccessHtml(\n 'Your 2h session token has been stored. You can now close this tab and return to your terminal.'\n )\n );\n\n server.close(() => {\n resolve();\n if (options.exitAfter !== false) {\n process.exit(0);\n }\n });\n return;\n }\n\n if (clientId && clientSecret) {\n if (options.onCredentials) {\n // The caller persists the credentials itself (e.g. interactive\n // init writing `.env` and enabling the editor in the config).\n logger('');\n logger('Log in successful. Configuring Intlayer CMS...');\n\n try {\n await options.onCredentials({ clientId, clientSecret });\n } catch (error) {\n logger(\n `Failed to persist the CMS credentials automatically: ${\n error instanceof Error ? error.message : String(error)\n }`,\n { level: 'error' }\n );\n }\n } else {\n logger('');\n logger('Log in successful. Client ID and Client Secret received.');\n\n logger('');\n logger([\n '1. Insert the Client ID and Client Secret in your',\n colorizePath('.env'),\n 'file:',\n ]);\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n logger(\n [\n colorize('INTLAYER_CLIENT_ID=', ANSIColors.GREY_LIGHT),\n colorize(clientId, ANSIColors.BLUE),\n ].join('')\n );\n logger(\n [\n colorize('INTLAYER_CLIENT_SECRET=', ANSIColors.GREY_LIGHT),\n colorize(clientSecret, ANSIColors.BLUE),\n ].join('')\n );\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n logger('');\n logger('2. Insert in your Intlayer configuration file:');\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n [\n `${ANSIColors.GREY_LIGHT}{`,\n ` editor: {`,\n ` enabled: true,`,\n ` cmsURL: '${colorizePath(cmsUrl!, undefined, ANSIColors.GREY_LIGHT)}',`,\n ` clientId: '${colorize('process.env.INTLAYER_CLIENT_ID', ANSIColors.BLUE, ANSIColors.GREY_LIGHT)}',`,\n ` clientSecret: '${colorize('process.env.INTLAYER_CLIENT_SECRET', ANSIColors.BLUE, ANSIColors.GREY_LIGHT)}',`,\n ` },`,\n `}`,\n ].forEach((line) => {\n logger(line);\n });\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(\n buildSuccessHtml(\n 'You have successfully logged in to Intlayer CLI. You can now close this tab and return to your terminal.'\n )\n );\n\n server.close(() => {\n resolve();\n if (options.exitAfter !== false) {\n process.exit(0);\n }\n });\n } else {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end('Missing parameters');\n }\n } else {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not found');\n }\n });\n\n server.listen(0, () => {\n const address = server.address();\n const port = typeof address === 'object' && address ? address.port : 0;\n const state = Math.random().toString(36).substring(7);\n\n const websiteUrl =\n cmsUrl ?? process.env.INTLAYER_SITE_URL ?? 'http://localhost:3000';\n const backendUrl = configuration.editor.backendURL ?? '';\n const loginUrl = `${websiteUrl}/auth/cli-login?port=${port}&state=${state}&backendUrl=${encodeURIComponent(backendUrl)}`;\n\n logger('Opening browser for login...');\n logger(`If browser does not open, visit: ${colorizePath(loginUrl)}`);\n\n openBrowser(loginUrl);\n });\n });\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"sessionToken.cjs","names":[],"sources":["../../../src/auth/sessionToken.ts"],"sourcesContent":["import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { IntlayerConfig } from '@intlayer/types/config';\n\nconst SESSION_FILE_NAME = 'cli-session.json';\n\nexport type CliSessionData = {\n token: string;\n expiresAt: string; // ISO date string\n};\n\nconst getSessionFilePath = (config: IntlayerConfig): string =>\n join(config.system.tempDir, SESSION_FILE_NAME);\n\nexport const writeCliSessionToken = async (\n config: IntlayerConfig,\n token: string,\n expiresAt: Date\n): Promise<void> => {\n const filePath = getSessionFilePath(config);\n\n await mkdir(config.system.tempDir, { recursive: true });\n\n const data: CliSessionData = { token, expiresAt: expiresAt.toISOString() };\n await writeFile(filePath, JSON.stringify(data, null, 2), { mode: 0o600 });\n};\n\nexport const readCliSessionToken = async (\n config: IntlayerConfig\n): Promise<CliSessionData | null> => {\n const filePath = getSessionFilePath(config);\n\n try {\n const raw = await readFile(filePath, 'utf8');\n const data: CliSessionData = JSON.parse(raw);\n\n if (!data.token || !data.expiresAt) {\n return null;\n }\n\n if (new Date() >= new Date(data.expiresAt)) {\n await clearCliSessionToken(config);\n return null;\n }\n\n return data;\n } catch {\n return null;\n }\n};\n\nexport const clearCliSessionToken = async (\n config: IntlayerConfig\n): Promise<void> => {\n try {\n await unlink(getSessionFilePath(config));\n } catch {\n // Ignore errors (file may not exist)\n }\n};\n"],"mappings":";;;;;AAIA,MAAM,oBAAoB;AAO1B,MAAM,sBAAsB,+BACrB,OAAO,OAAO,SAAS,iBAAiB;AAE/C,MAAa,uBAAuB,OAClC,QACA,OACA,cACkB;CAClB,MAAM,WAAW,mBAAmB,MAAM;CAE1C,kCAAY,OAAO,OAAO,SAAS,EAAE,WAAW,KAAK,CAAC;CAEtD,MAAM,OAAuB;EAAE;EAAO,WAAW,UAAU,YAAY;CAAE;CACzE,sCAAgB,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;AAC1E;AAEA,MAAa,sBAAsB,OACjC,WACmC;CACnC,MAAM,WAAW,mBAAmB,MAAM;CAE1C,IAAI;EACF,MAAM,MAAM,qCAAe,UAAU,MAAM;EAC3C,MAAM,OAAuB,KAAK,MAAM,GAAG;EAE3C,IAAI,CAAC,KAAK,SAAS,CAAC,KAAK,WACvB,OAAO;EAGT,oBAAI,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,SAAS,GAAG;GAC1C,MAAM,qBAAqB,MAAM;GACjC,OAAO;EACT;EAEA,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAa,uBAAuB,OAClC,WACkB;CAClB,IAAI;EACF,mCAAa,mBAAmB,MAAM,CAAC;CACzC,QAAQ,CAER;AACF"}
1
+ {"version":3,"file":"sessionToken.cjs","names":["join","mkdir","writeFile","readFile","unlink"],"sources":["../../../src/auth/sessionToken.ts"],"sourcesContent":["import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { IntlayerConfig } from '@intlayer/types/config';\n\nconst SESSION_FILE_NAME = 'cli-session.json';\n\nexport type CliSessionData = {\n token: string;\n expiresAt: string; // ISO date string\n};\n\nconst getSessionFilePath = (config: IntlayerConfig): string =>\n join(config.system.tempDir, SESSION_FILE_NAME);\n\nexport const writeCliSessionToken = async (\n config: IntlayerConfig,\n token: string,\n expiresAt: Date\n): Promise<void> => {\n const filePath = getSessionFilePath(config);\n\n await mkdir(config.system.tempDir, { recursive: true });\n\n const data: CliSessionData = { token, expiresAt: expiresAt.toISOString() };\n await writeFile(filePath, JSON.stringify(data, null, 2), { mode: 0o600 });\n};\n\nexport const readCliSessionToken = async (\n config: IntlayerConfig\n): Promise<CliSessionData | null> => {\n const filePath = getSessionFilePath(config);\n\n try {\n const raw = await readFile(filePath, 'utf8');\n const data: CliSessionData = JSON.parse(raw);\n\n if (!data.token || !data.expiresAt) {\n return null;\n }\n\n if (new Date() >= new Date(data.expiresAt)) {\n await clearCliSessionToken(config);\n return null;\n }\n\n return data;\n } catch {\n return null;\n }\n};\n\nexport const clearCliSessionToken = async (\n config: IntlayerConfig\n): Promise<void> => {\n try {\n await unlink(getSessionFilePath(config));\n } catch {\n // Ignore errors (file may not exist)\n }\n};\n"],"mappings":";;;;;AAIA,MAAM,oBAAoB;AAO1B,MAAM,sBAAsB,eAC1BA,gBAAK,OAAO,OAAO,SAAS,iBAAiB;AAE/C,MAAa,uBAAuB,OAClC,QACA,OACA,cACkB;CAClB,MAAM,WAAW,mBAAmB,MAAM;CAE1C,UAAMC,wBAAM,OAAO,OAAO,SAAS,EAAE,WAAW,KAAK,CAAC;CAEtD,MAAM,OAAuB;EAAE;EAAO,WAAW,UAAU,YAAY;CAAE;CACzE,UAAMC,4BAAU,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;AAC1E;AAEA,MAAa,sBAAsB,OACjC,WACmC;CACnC,MAAM,WAAW,mBAAmB,MAAM;CAE1C,IAAI;EACF,MAAM,MAAM,UAAMC,2BAAS,UAAU,MAAM;EAC3C,MAAM,OAAuB,KAAK,MAAM,GAAG;EAE3C,IAAI,CAAC,KAAK,SAAS,CAAC,KAAK,WACvB,OAAO;EAGT,oBAAI,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,SAAS,GAAG;GAC1C,MAAM,qBAAqB,MAAM;GACjC,OAAO;EACT;EAEA,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAa,uBAAuB,OAClC,WACkB;CAClB,IAAI;EACF,UAAMC,yBAAO,mBAAmB,MAAM,CAAC;CACzC,QAAQ,CAER;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"build.cjs","names":[],"sources":["../../src/build.ts"],"sourcesContent":["import {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { logConfigDetails } from '@intlayer/engine/cli';\nimport { type ParallelHandle, runParallel } from '@intlayer/engine/utils';\nimport { buildAndWatchIntlayer } from '@intlayer/engine/watcher';\n\ntype BuildOptions = {\n watch?: boolean;\n skipPrepare?: boolean;\n with?: string | string[];\n configOptions?: GetConfigurationOptions;\n};\n\n/**\n * Get locales dictionaries .content.{json|ts|tsx|js|jsx|mjs|cjs} and build the JSON dictionaries in the .intlayer directory.\n * Watch mode available to get the change in the .content.{json|ts|tsx|js|jsx|mjs|cjs}\n */\nexport const build = async (options?: BuildOptions) => {\n const config = getConfiguration(options?.configOptions);\n logConfigDetails(options?.configOptions);\n\n let parallelProcess: ParallelHandle | null = null;\n\n if (options?.with) {\n parallelProcess = runParallel(options.with);\n // Handle the promise to avoid unhandled rejection\n parallelProcess.result.catch(() => {\n // Parallel process failed or was terminated\n });\n }\n\n await buildAndWatchIntlayer({\n persistent: options?.watch ?? false,\n skipPrepare: options?.skipPrepare ?? false,\n configuration: config,\n });\n\n if (!options?.watch && parallelProcess) {\n parallelProcess.kill();\n }\n};\n"],"mappings":";;;;;;;;;;;AAmBA,MAAa,QAAQ,OAAO,YAA2B;CACrD,MAAM,qDAA0B,SAAS,aAAa;CACtD,2CAAiB,SAAS,aAAa;CAEvC,IAAI,kBAAyC;CAE7C,IAAI,SAAS,MAAM;EACjB,0DAA8B,QAAQ,IAAI;EAE1C,gBAAgB,OAAO,YAAY,CAEnC,CAAC;CACH;CAEA,0DAA4B;EAC1B,YAAY,SAAS,SAAS;EAC9B,aAAa,SAAS,eAAe;EACrC,eAAe;CACjB,CAAC;CAED,IAAI,CAAC,SAAS,SAAS,iBACrB,gBAAgB,KAAK;AAEzB"}
1
+ {"version":3,"file":"build.cjs","names":["getConfiguration","runParallel","buildAndWatchIntlayer"],"sources":["../../src/build.ts"],"sourcesContent":["import {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { logConfigDetails } from '@intlayer/engine/cli';\nimport { type ParallelHandle, runParallel } from '@intlayer/engine/utils';\nimport { buildAndWatchIntlayer } from '@intlayer/engine/watcher';\n\ntype BuildOptions = {\n watch?: boolean;\n skipPrepare?: boolean;\n with?: string | string[];\n configOptions?: GetConfigurationOptions;\n};\n\n/**\n * Get locales dictionaries .content.{json|ts|tsx|js|jsx|mjs|cjs} and build the JSON dictionaries in the .intlayer directory.\n * Watch mode available to get the change in the .content.{json|ts|tsx|js|jsx|mjs|cjs}\n */\nexport const build = async (options?: BuildOptions) => {\n const config = getConfiguration(options?.configOptions);\n logConfigDetails(options?.configOptions);\n\n let parallelProcess: ParallelHandle | null = null;\n\n if (options?.with) {\n parallelProcess = runParallel(options.with);\n // Handle the promise to avoid unhandled rejection\n parallelProcess.result.catch(() => {\n // Parallel process failed or was terminated\n });\n }\n\n await buildAndWatchIntlayer({\n persistent: options?.watch ?? false,\n skipPrepare: options?.skipPrepare ?? false,\n configuration: config,\n });\n\n if (!options?.watch && parallelProcess) {\n parallelProcess.kill();\n }\n};\n"],"mappings":";;;;;;;;;;;AAmBA,MAAa,QAAQ,OAAO,YAA2B;CACrD,MAAM,aAASA,wCAAiB,SAAS,aAAa;CACtD,2CAAiB,SAAS,aAAa;CAEvC,IAAI,kBAAyC;CAE7C,IAAI,SAAS,MAAM;EACjB,sBAAkBC,oCAAY,QAAQ,IAAI;EAE1C,gBAAgB,OAAO,YAAY,CAEnC,CAAC;CACH;CAEA,UAAMC,gDAAsB;EAC1B,YAAY,SAAS,SAAS;EAC9B,aAAa,SAAS,eAAe;EACrC,eAAe;CACjB,CAAC;CAED,IAAI,CAAC,SAAS,SAAS,iBACrB,gBAAgB,KAAK;AAEzB"}
@@ -1,5 +1,4 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
-
3
2
  //#region src/bundle.ts
4
3
  /**
5
4
  * Computes the tree-shaking defines derived from the project's built
@@ -1 +1 @@
1
- {"version":3,"file":"bundle.cjs","names":[],"sources":["../../src/bundle.ts"],"sourcesContent":["import type { BundleIntlayerOptions } from '@intlayer/config/bundle';\n\n/**\n * Computes the tree-shaking defines derived from the project's built\n * dictionaries (unused node types, dictionary selector usage) so the generated\n * bundle drops the corresponding runtime logic, mirroring what the bundler\n * plugins (vite / webpack / esbuild) inject at application build time.\n *\n * Returns an empty object when no dictionary has been built yet: shaking based\n * on an empty dictionary set would mark every node type as unused and strip\n * logic that the runtime content may require.\n */\nconst getDictionaryTreeShakingDefines = async (\n options: BundleIntlayerOptions\n): Promise<Record<string, string>> => {\n // Dynamic imports for performance reason\n const { formatDictionarySelectorEnvVar, formatNodeTypeToEnvVar } =\n await import('@intlayer/config/envVars');\n const { getConfiguration } = await import('@intlayer/config/node');\n const { getHasDictionarySelector, getUnusedNodeTypesAsync } = await import(\n '@intlayer/config/utils'\n );\n const { getDictionaries } = await import('@intlayer/dictionaries-entry');\n\n const configuration = getConfiguration(options.configOptions);\n const dictionaries = getDictionaries(configuration);\n\n if (Object.keys(dictionaries).length === 0) return {};\n\n const wrapKey = (key: string) => `process.env.${key}`;\n const wrapValue = (value: string) => `\"${value}\"`;\n\n const unusedNodeTypes = await getUnusedNodeTypesAsync(dictionaries);\n\n return {\n // Tree shaking based on unused node types\n ...formatNodeTypeToEnvVar(unusedNodeTypes, wrapKey, wrapValue),\n // Tree shaking the dictionary selector logic (collections / variants)\n ...formatDictionarySelectorEnvVar(\n getHasDictionarySelector(dictionaries),\n wrapKey,\n wrapValue\n ),\n };\n};\n\nexport const bundle = async (options: BundleIntlayerOptions): Promise<void> => {\n try {\n // Dynamic import for performance reason\n const { bundleIntlayer } = await import('@intlayer/config/bundle');\n\n const dictionaryDefines = await getDictionaryTreeShakingDefines(options);\n\n await bundleIntlayer({\n ...options,\n define: {\n ...dictionaryDefines,\n // Caller-supplied defines take precedence\n ...options.define,\n },\n });\n } catch (error) {\n console.error('Failed to create bundle:', error);\n process.exit(1);\n }\n};\n"],"mappings":";;;;;;;;;;;;;AAYA,MAAM,kCAAkC,OACtC,YACoC;CAEpC,MAAM,EAAE,gCAAgC,2BACtC,MAAM,OAAO;CACf,MAAM,EAAE,qBAAqB,MAAM,OAAO;CAC1C,MAAM,EAAE,0BAA0B,4BAA4B,MAAM,OAClE;CAEF,MAAM,EAAE,oBAAoB,MAAM,OAAO;CAGzC,MAAM,eAAe,gBADC,iBAAiB,QAAQ,aACE,CAAC;CAElD,IAAI,OAAO,KAAK,YAAY,CAAC,CAAC,WAAW,GAAG,OAAO,CAAC;CAEpD,MAAM,WAAW,QAAgB,eAAe;CAChD,MAAM,aAAa,UAAkB,IAAI,MAAM;CAI/C,OAAO;EAEL,GAAG,uBAAuB,MAJE,wBAAwB,YAAY,GAIrB,SAAS,SAAS;EAE7D,GAAG,+BACD,yBAAyB,YAAY,GACrC,SACA,SACF;CACF;AACF;AAEA,MAAa,SAAS,OAAO,YAAkD;CAC7E,IAAI;EAEF,MAAM,EAAE,mBAAmB,MAAM,OAAO;EAExC,MAAM,oBAAoB,MAAM,gCAAgC,OAAO;EAEvE,MAAM,eAAe;GACnB,GAAG;GACH,QAAQ;IACN,GAAG;IAEH,GAAG,QAAQ;GACb;EACF,CAAC;CACH,SAAS,OAAO;EACd,QAAQ,MAAM,4BAA4B,KAAK;EAC/C,QAAQ,KAAK,CAAC;CAChB;AACF"}
1
+ {"version":3,"file":"bundle.cjs","names":[],"sources":["../../src/bundle.ts"],"sourcesContent":["import type { BundleIntlayerOptions } from '@intlayer/config/bundle';\n\n/**\n * Computes the tree-shaking defines derived from the project's built\n * dictionaries (unused node types, dictionary selector usage) so the generated\n * bundle drops the corresponding runtime logic, mirroring what the bundler\n * plugins (vite / webpack / esbuild) inject at application build time.\n *\n * Returns an empty object when no dictionary has been built yet: shaking based\n * on an empty dictionary set would mark every node type as unused and strip\n * logic that the runtime content may require.\n */\nconst getDictionaryTreeShakingDefines = async (\n options: BundleIntlayerOptions\n): Promise<Record<string, string>> => {\n // Dynamic imports for performance reason\n const { formatDictionarySelectorEnvVar, formatNodeTypeToEnvVar } =\n await import('@intlayer/config/envVars');\n const { getConfiguration } = await import('@intlayer/config/node');\n const { getHasDictionarySelector, getUnusedNodeTypesAsync } = await import(\n '@intlayer/config/utils'\n );\n const { getDictionaries } = await import('@intlayer/dictionaries-entry');\n\n const configuration = getConfiguration(options.configOptions);\n const dictionaries = getDictionaries(configuration);\n\n if (Object.keys(dictionaries).length === 0) return {};\n\n const wrapKey = (key: string) => `process.env.${key}`;\n const wrapValue = (value: string) => `\"${value}\"`;\n\n const unusedNodeTypes = await getUnusedNodeTypesAsync(dictionaries);\n\n return {\n // Tree shaking based on unused node types\n ...formatNodeTypeToEnvVar(unusedNodeTypes, wrapKey, wrapValue),\n // Tree shaking the dictionary selector logic (collections / variants)\n ...formatDictionarySelectorEnvVar(\n getHasDictionarySelector(dictionaries),\n wrapKey,\n wrapValue\n ),\n };\n};\n\nexport const bundle = async (options: BundleIntlayerOptions): Promise<void> => {\n try {\n // Dynamic import for performance reason\n const { bundleIntlayer } = await import('@intlayer/config/bundle');\n\n const dictionaryDefines = await getDictionaryTreeShakingDefines(options);\n\n await bundleIntlayer({\n ...options,\n define: {\n ...dictionaryDefines,\n // Caller-supplied defines take precedence\n ...options.define,\n },\n });\n } catch (error) {\n console.error('Failed to create bundle:', error);\n process.exit(1);\n }\n};\n"],"mappings":";;;;;;;;;;;;AAYA,MAAM,kCAAkC,OACtC,YACoC;CAEpC,MAAM,EAAE,gCAAgC,2BACtC,MAAM,OAAO;CACf,MAAM,EAAE,qBAAqB,MAAM,OAAO;CAC1C,MAAM,EAAE,0BAA0B,4BAA4B,MAAM,OAClE;CAEF,MAAM,EAAE,oBAAoB,MAAM,OAAO;CAGzC,MAAM,eAAe,gBADC,iBAAiB,QAAQ,aACE,CAAC;CAElD,IAAI,OAAO,KAAK,YAAY,CAAC,CAAC,WAAW,GAAG,OAAO,CAAC;CAEpD,MAAM,WAAW,QAAgB,eAAe;CAChD,MAAM,aAAa,UAAkB,IAAI,MAAM;CAI/C,OAAO;EAEL,GAAG,uBAAuB,MAJE,wBAAwB,YAAY,GAIrB,SAAS,SAAS;EAE7D,GAAG,+BACD,yBAAyB,YAAY,GACrC,SACA,SACF;CACF;AACF;AAEA,MAAa,SAAS,OAAO,YAAkD;CAC7E,IAAI;EAEF,MAAM,EAAE,mBAAmB,MAAM,OAAO;EAExC,MAAM,oBAAoB,MAAM,gCAAgC,OAAO;EAEvE,MAAM,eAAe;GACnB,GAAG;GACH,QAAQ;IACN,GAAG;IAEH,GAAG,QAAQ;GACb;EACF,CAAC;CACH,SAAS,OAAO;EACd,QAAQ,MAAM,4BAA4B,KAAK;EAC/C,QAAQ,KAAK,CAAC;CAChB;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"ci.cjs","names":["sep"],"sources":["../../src/ci.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport { resolve, sep } from 'node:path';\nimport { logger } from '@intlayer/config/logger';\nimport {\n detectPackageManager,\n listProjects,\n type PackageManager,\n} from '@intlayer/engine/cli';\n\n/** CMS access keys of a single project. */\ntype ProjectCredentials = {\n clientId: string;\n clientSecret: string;\n};\n\n/**\n * Shape of the `INTLAYER_PROJECT_CREDENTIALS` environment variable: a JSON map\n * of project path (absolute, or relative to the repository root) to its CMS\n * access keys.\n */\ntype CredentialsMap = Record<string, ProjectCredentials>;\n\n/** Command used to run the locally installed `intlayer` binary. */\ntype PackageManagerCommand = {\n command: string;\n args: string[];\n};\n\n/**\n * Detects the command used to run the local `intlayer` binary. Prefers the\n * package manager that launched the current process (`npm_config_user_agent`),\n * and falls back to lock file detection when the CLI was not started through a\n * package manager (e.g. a global install or a bare CI runner).\n */\nexport const getPackageManagerCommand = (\n lockFileSearchDir: string\n): PackageManagerCommand => {\n const userAgent = process.env.npm_config_user_agent;\n\n const commandByPackageManager: Record<PackageManager, PackageManagerCommand> =\n {\n bun: { command: 'bun', args: ['intlayer'] }, // Bun runs local bins natively\n pnpm: { command: 'pnpm', args: ['exec', 'intlayer'] }, // pnpm requires 'exec'\n yarn: { command: 'yarn', args: ['run', 'intlayer'] }, // yarn requires 'run' or 'exec'\n npm: { command: 'npx', args: ['intlayer'] },\n };\n\n const userAgentPackageManager = (\n Object.keys(commandByPackageManager) as PackageManager[]\n ).find((packageManager) => userAgent?.startsWith(packageManager));\n\n if (userAgentPackageManager) {\n return commandByPackageManager[userAgentPackageManager];\n }\n\n // No user agent — infer the package manager from the repository lock file.\n return commandByPackageManager[detectPackageManager(lockFileSearchDir)];\n};\n\n/**\n * Splits a path into segments, accepting both posix and windows separators so\n * credential keys written on any OS compare equal to local project paths.\n */\nconst toPathSegments = (path: string): string[] =>\n path.split(/[\\\\/]+/).filter((segment) => segment !== '' && segment !== '.');\n\n/**\n * Returns true when a credentials key designates the given project directory.\n * A key matches when it is:\n * - the absolute path of the project,\n * - a path relative to `searchDir` (the repository root), or\n * - a trailing sequence of full path segments of the project path (so the key\n * `\"apps/web\"` matches `/repo/apps/web`, but `\"app\"` never matches\n * `/repo/my-app`).\n */\nexport const credentialsKeyMatchesProject = (\n key: string,\n projectPath: string,\n searchDir: string\n): boolean => {\n if (resolve(key) === projectPath) return true;\n if (resolve(searchDir, key) === projectPath) return true;\n\n const keySegments = toPathSegments(key);\n const projectSegments = toPathSegments(projectPath);\n\n if (keySegments.length === 0 || keySegments.length > projectSegments.length) {\n return false;\n }\n\n const trailingProjectSegments = projectSegments.slice(-keySegments.length);\n\n return keySegments.every(\n (segment, index) => trailingProjectSegments[index] === segment\n );\n};\n\n/**\n * Selects the projects the CI command must run in:\n * - run from the repository root → every discovered project (monorepo mode);\n * - run from inside a project directory (or any of its subdirectories) → that\n * project only, the deepest match winning when projects are nested;\n * - run anywhere else → every discovered project.\n */\nexport const selectProjectsToRun = (\n currentWorkingDir: string,\n searchDir: string,\n projectsPath: string[]\n): { projectsToRun: string[]; currentProject: string | undefined } => {\n const cwd = resolve(currentWorkingDir);\n\n if (cwd === resolve(searchDir)) {\n return { projectsToRun: projectsPath, currentProject: undefined };\n }\n\n const currentProject = projectsPath\n .filter(\n (projectPath) => cwd === projectPath || cwd.startsWith(projectPath + sep)\n )\n .sort((a, b) => b.length - a.length)[0];\n\n return {\n projectsToRun: currentProject ? [currentProject] : projectsPath,\n currentProject,\n };\n};\n\n/**\n * Runs an intlayer command in every Intlayer project of the repository (or\n * only in the current one when invoked from inside a project directory),\n * injecting per-project CMS credentials from `INTLAYER_PROJECT_CREDENTIALS`.\n * Exits with code 1 when the command fails in at least one project.\n */\nexport const runCI = async (commands: string[]) => {\n const credentialsEnv = process.env.INTLAYER_PROJECT_CREDENTIALS;\n let credentials: CredentialsMap = {};\n\n // Parse Credentials (Optional now)\n if (credentialsEnv) {\n try {\n credentials = JSON.parse(credentialsEnv);\n } catch {\n logger(\n 'INTLAYER_PROJECT_CREDENTIALS is not valid JSON. Proceeding without credentials.',\n {\n level: 'warn',\n }\n );\n }\n }\n\n // Discover projects from the git root so every project of the repository is\n // found regardless of the directory the command is launched from.\n const { searchDir, projectsPath } = await listProjects({ gitRoot: true });\n const normalizedProjectsPath = projectsPath.map((projectPath) =>\n resolve(projectPath)\n );\n\n if (normalizedProjectsPath.length === 0) {\n logger('No Intlayer projects found.', { level: 'warn' });\n return;\n }\n\n // Determine Context: Single Project vs All Projects\n const { projectsToRun, currentProject } = selectProjectsToRun(\n process.cwd(),\n searchDir,\n normalizedProjectsPath\n );\n\n const { command, args: pmArgs } = getPackageManagerCommand(searchDir);\n const finalArgs = [...pmArgs, ...commands];\n\n logger(`CI: Using package manager: ${command}`, {\n level: 'info',\n isVerbose: true,\n });\n\n if (currentProject) {\n logger(`CI: Detected project context: ${currentProject}`, {\n level: 'info',\n });\n } else {\n logger(\n `CI: No specific project context detected. Iterating over ${projectsToRun.length} discovered projects...`,\n {\n level: 'info',\n }\n );\n }\n\n const failedProjects: string[] = [];\n\n // Iterate and Execute\n for (const projectPath of projectsToRun) {\n // Attempt to match credentials to the project path\n const credsEntry = Object.entries(credentials).find(([key]) =>\n credentialsKeyMatchesProject(key, projectPath, searchDir)\n );\n\n const creds = credsEntry?.[1];\n\n logger(`\\nCI: Executing for ${projectPath}...`, {\n level: 'info',\n });\n\n const envVars = { ...process.env };\n\n if (creds) {\n envVars.INTLAYER_CLIENT_ID = creds.clientId;\n envVars.INTLAYER_CLIENT_SECRET = creds.clientSecret;\n } else if (credentialsEnv) {\n logger(\n `CI: No matching credentials found for ${projectPath} in INTLAYER_PROJECT_CREDENTIALS.`,\n { isVerbose: true }\n );\n }\n\n const result = spawnSync(command, finalArgs, {\n cwd: projectPath,\n stdio: 'inherit',\n env: envVars,\n });\n\n if (result.error) {\n logger(\n `CI: Failed to launch \"${command}\" for ${projectPath}: ${result.error.message}`,\n { level: 'error' }\n );\n failedProjects.push(projectPath);\n continue;\n }\n\n if (result.status !== 0) {\n logger(`CI: Failed for ${projectPath}`, {\n level: 'error',\n });\n failedProjects.push(projectPath);\n }\n }\n\n if (failedProjects.length > 0) {\n logger(\n `\\nCI: ${failedProjects.length}/${projectsToRun.length} project(s) failed:\\n${failedProjects\n .map((projectPath) => ` - ${projectPath}`)\n .join('\\n')}`,\n { level: 'error' }\n );\n process.exit(1);\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ci.cjs","names":["detectPackageManager","resolve","sep","listProjects","spawnSync"],"sources":["../../src/ci.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport { resolve, sep } from 'node:path';\nimport { logger } from '@intlayer/config/logger';\nimport {\n detectPackageManager,\n listProjects,\n type PackageManager,\n} from '@intlayer/engine/cli';\n\n/** CMS access keys of a single project. */\ntype ProjectCredentials = {\n clientId: string;\n clientSecret: string;\n};\n\n/**\n * Shape of the `INTLAYER_PROJECT_CREDENTIALS` environment variable: a JSON map\n * of project path (absolute, or relative to the repository root) to its CMS\n * access keys.\n */\ntype CredentialsMap = Record<string, ProjectCredentials>;\n\n/** Command used to run the locally installed `intlayer` binary. */\ntype PackageManagerCommand = {\n command: string;\n args: string[];\n};\n\n/**\n * Detects the command used to run the local `intlayer` binary. Prefers the\n * package manager that launched the current process (`npm_config_user_agent`),\n * and falls back to lock file detection when the CLI was not started through a\n * package manager (e.g. a global install or a bare CI runner).\n */\nexport const getPackageManagerCommand = (\n lockFileSearchDir: string\n): PackageManagerCommand => {\n const userAgent = process.env.npm_config_user_agent;\n\n const commandByPackageManager: Record<PackageManager, PackageManagerCommand> =\n {\n bun: { command: 'bun', args: ['intlayer'] }, // Bun runs local bins natively\n pnpm: { command: 'pnpm', args: ['exec', 'intlayer'] }, // pnpm requires 'exec'\n yarn: { command: 'yarn', args: ['run', 'intlayer'] }, // yarn requires 'run' or 'exec'\n npm: { command: 'npx', args: ['intlayer'] },\n };\n\n const userAgentPackageManager = (\n Object.keys(commandByPackageManager) as PackageManager[]\n ).find((packageManager) => userAgent?.startsWith(packageManager));\n\n if (userAgentPackageManager) {\n return commandByPackageManager[userAgentPackageManager];\n }\n\n // No user agent — infer the package manager from the repository lock file.\n return commandByPackageManager[detectPackageManager(lockFileSearchDir)];\n};\n\n/**\n * Splits a path into segments, accepting both posix and windows separators so\n * credential keys written on any OS compare equal to local project paths.\n */\nconst toPathSegments = (path: string): string[] =>\n path.split(/[\\\\/]+/).filter((segment) => segment !== '' && segment !== '.');\n\n/**\n * Returns true when a credentials key designates the given project directory.\n * A key matches when it is:\n * - the absolute path of the project,\n * - a path relative to `searchDir` (the repository root), or\n * - a trailing sequence of full path segments of the project path (so the key\n * `\"apps/web\"` matches `/repo/apps/web`, but `\"app\"` never matches\n * `/repo/my-app`).\n */\nexport const credentialsKeyMatchesProject = (\n key: string,\n projectPath: string,\n searchDir: string\n): boolean => {\n if (resolve(key) === projectPath) return true;\n if (resolve(searchDir, key) === projectPath) return true;\n\n const keySegments = toPathSegments(key);\n const projectSegments = toPathSegments(projectPath);\n\n if (keySegments.length === 0 || keySegments.length > projectSegments.length) {\n return false;\n }\n\n const trailingProjectSegments = projectSegments.slice(-keySegments.length);\n\n return keySegments.every(\n (segment, index) => trailingProjectSegments[index] === segment\n );\n};\n\n/**\n * Selects the projects the CI command must run in:\n * - run from the repository root → every discovered project (monorepo mode);\n * - run from inside a project directory (or any of its subdirectories) → that\n * project only, the deepest match winning when projects are nested;\n * - run anywhere else → every discovered project.\n */\nexport const selectProjectsToRun = (\n currentWorkingDir: string,\n searchDir: string,\n projectsPath: string[]\n): { projectsToRun: string[]; currentProject: string | undefined } => {\n const cwd = resolve(currentWorkingDir);\n\n if (cwd === resolve(searchDir)) {\n return { projectsToRun: projectsPath, currentProject: undefined };\n }\n\n const currentProject = projectsPath\n .filter(\n (projectPath) => cwd === projectPath || cwd.startsWith(projectPath + sep)\n )\n .sort((a, b) => b.length - a.length)[0];\n\n return {\n projectsToRun: currentProject ? [currentProject] : projectsPath,\n currentProject,\n };\n};\n\n/**\n * Runs an intlayer command in every Intlayer project of the repository (or\n * only in the current one when invoked from inside a project directory),\n * injecting per-project CMS credentials from `INTLAYER_PROJECT_CREDENTIALS`.\n * Exits with code 1 when the command fails in at least one project.\n */\nexport const runCI = async (commands: string[]) => {\n const credentialsEnv = process.env.INTLAYER_PROJECT_CREDENTIALS;\n let credentials: CredentialsMap = {};\n\n // Parse Credentials (Optional now)\n if (credentialsEnv) {\n try {\n credentials = JSON.parse(credentialsEnv);\n } catch {\n logger(\n 'INTLAYER_PROJECT_CREDENTIALS is not valid JSON. Proceeding without credentials.',\n {\n level: 'warn',\n }\n );\n }\n }\n\n // Discover projects from the git root so every project of the repository is\n // found regardless of the directory the command is launched from.\n const { searchDir, projectsPath } = await listProjects({ gitRoot: true });\n const normalizedProjectsPath = projectsPath.map((projectPath) =>\n resolve(projectPath)\n );\n\n if (normalizedProjectsPath.length === 0) {\n logger('No Intlayer projects found.', { level: 'warn' });\n return;\n }\n\n // Determine Context: Single Project vs All Projects\n const { projectsToRun, currentProject } = selectProjectsToRun(\n process.cwd(),\n searchDir,\n normalizedProjectsPath\n );\n\n const { command, args: pmArgs } = getPackageManagerCommand(searchDir);\n const finalArgs = [...pmArgs, ...commands];\n\n logger(`CI: Using package manager: ${command}`, {\n level: 'info',\n isVerbose: true,\n });\n\n if (currentProject) {\n logger(`CI: Detected project context: ${currentProject}`, {\n level: 'info',\n });\n } else {\n logger(\n `CI: No specific project context detected. Iterating over ${projectsToRun.length} discovered projects...`,\n {\n level: 'info',\n }\n );\n }\n\n const failedProjects: string[] = [];\n\n // Iterate and Execute\n for (const projectPath of projectsToRun) {\n // Attempt to match credentials to the project path\n const credsEntry = Object.entries(credentials).find(([key]) =>\n credentialsKeyMatchesProject(key, projectPath, searchDir)\n );\n\n const creds = credsEntry?.[1];\n\n logger(`\\nCI: Executing for ${projectPath}...`, {\n level: 'info',\n });\n\n const envVars = { ...process.env };\n\n if (creds) {\n envVars.INTLAYER_CLIENT_ID = creds.clientId;\n envVars.INTLAYER_CLIENT_SECRET = creds.clientSecret;\n } else if (credentialsEnv) {\n logger(\n `CI: No matching credentials found for ${projectPath} in INTLAYER_PROJECT_CREDENTIALS.`,\n { isVerbose: true }\n );\n }\n\n const result = spawnSync(command, finalArgs, {\n cwd: projectPath,\n stdio: 'inherit',\n env: envVars,\n });\n\n if (result.error) {\n logger(\n `CI: Failed to launch \"${command}\" for ${projectPath}: ${result.error.message}`,\n { level: 'error' }\n );\n failedProjects.push(projectPath);\n continue;\n }\n\n if (result.status !== 0) {\n logger(`CI: Failed for ${projectPath}`, {\n level: 'error',\n });\n failedProjects.push(projectPath);\n }\n }\n\n if (failedProjects.length > 0) {\n logger(\n `\\nCI: ${failedProjects.length}/${projectsToRun.length} project(s) failed:\\n${failedProjects\n .map((projectPath) => ` - ${projectPath}`)\n .join('\\n')}`,\n { level: 'error' }\n );\n process.exit(1);\n }\n};\n"],"mappings":"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"}