gt 2.7.0 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cli/base.d.ts +2 -1
  3. package/dist/cli/base.js +32 -20
  4. package/dist/cli/commands/upload.js +1 -1
  5. package/dist/cli/flags.js +1 -1
  6. package/dist/cli/react.js +1 -1
  7. package/dist/config/generateSettings.js +2 -2
  8. package/dist/config/validateSettings.js +1 -1
  9. package/dist/console/index.d.ts +10 -10
  10. package/dist/console/index.js +11 -11
  11. package/dist/console/logging.js +1 -1
  12. package/dist/formats/files/aggregateFiles.js +1 -1
  13. package/dist/formats/json/mergeJson.js +1 -1
  14. package/dist/formats/json/parseJson.js +1 -1
  15. package/dist/fs/determineFramework.js +1 -1
  16. package/dist/generated/version.d.ts +1 -1
  17. package/dist/generated/version.js +1 -1
  18. package/dist/locadex/setupFlow.js +1 -1
  19. package/dist/react/parse/addVitePlugin/index.js +1 -1
  20. package/dist/react/parse/addVitePlugin/updateViteConfig.js +1 -1
  21. package/dist/setup/userInput.js +3 -3
  22. package/dist/setup/wizard.js +1 -1
  23. package/dist/translation/stage.js +1 -1
  24. package/dist/translation/validate.js +1 -1
  25. package/dist/utils/addExplicitAnchorIds.js +1 -1
  26. package/dist/utils/calculateTimeoutMs.d.ts +4 -0
  27. package/dist/utils/calculateTimeoutMs.js +8 -0
  28. package/dist/utils/constants.d.ts +1 -0
  29. package/dist/utils/constants.js +1 -0
  30. package/dist/utils/credentials.d.ts +8 -4
  31. package/dist/utils/credentials.js +15 -19
  32. package/dist/utils/fetch.d.ts +11 -0
  33. package/dist/utils/fetch.js +22 -0
  34. package/dist/utils/installPackage.js +4 -4
  35. package/dist/utils/packageManager.js +1 -1
  36. package/dist/workflows/setupProject.js +2 -8
  37. package/dist/workflows/stage.js +2 -8
  38. package/dist/workflows/steps/BranchStep.js +3 -3
  39. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # gtx-cli
2
2
 
3
+ ## 2.7.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1085](https://github.com/generaltranslation/gt/pull/1085) [`dad7824`](https://github.com/generaltranslation/gt/commit/dad78246d164b201d4fc14c89213cc04f21c8b76) Thanks [@brian-lou](https://github.com/brian-lou)! - feat: Auth wizard supports both types of key creation
8
+
9
+ - [#1082](https://github.com/generaltranslation/gt/pull/1082) [`3cb3bbd`](https://github.com/generaltranslation/gt/commit/3cb3bbd13046e6c1f6f9d4b5286669b96b4a85b2) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Bumping CLI timeouts
10
+
11
+ - [#1076](https://github.com/generaltranslation/gt/pull/1076) [`19ae4eb`](https://github.com/generaltranslation/gt/commit/19ae4eb0baf7e6f15d19f9fad384621d38d73d57) Thanks [@moss-bryophyta](https://github.com/moss-bryophyta)! - Apply style guide to error messages and warnings: remove "Please", simplify verbose phrasing, fix `in-line` → `inline`.
12
+
13
+ - Updated dependencies [[`dad7824`](https://github.com/generaltranslation/gt/commit/dad78246d164b201d4fc14c89213cc04f21c8b76)]:
14
+ - generaltranslation@8.1.14
15
+
3
16
  ## 2.7.0
4
17
 
5
18
  ### Minor Changes
@@ -7,7 +7,8 @@ export type UploadOptions = {
7
7
  defaultLocale?: string;
8
8
  };
9
9
  export type LoginOptions = {
10
- keyType?: 'development' | 'production';
10
+ config?: string;
11
+ keyType?: 'development' | 'production' | 'all';
11
12
  };
12
13
  export declare class BaseCLI {
13
14
  protected library: SupportedLibraries;
package/dist/cli/base.js CHANGED
@@ -205,29 +205,31 @@ export class BaseCLI {
205
205
  setupLoginCommand() {
206
206
  this.program
207
207
  .command('auth')
208
- .description('Generate a General Translation API key and project ID')
208
+ .description('Generate General Translation API keys and project ID')
209
209
  .option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json']))
210
- .option('-t, --key-type <type>', 'Type of key to generate, production | development')
210
+ .option('-t, --key-type <type>', 'Type of key to generate, production | development | all')
211
211
  .action(async (options) => {
212
212
  displayHeader('Authenticating with General Translation...');
213
213
  if (!options.keyType) {
214
- const packageJson = await searchForPackageJson();
215
- if (packageJson &&
216
- INLINE_LIBRARIES.some((lib) => isPackageInstalled(lib, packageJson))) {
217
- options.keyType = 'development';
218
- }
219
- else {
220
- options.keyType = 'production';
221
- }
214
+ options.keyType = await promptSelect({
215
+ message: 'What type of API key would you like to generate?',
216
+ options: [
217
+ { value: 'development', label: 'Development' },
218
+ { value: 'production', label: 'Production' },
219
+ { value: 'all', label: 'Both' },
220
+ ],
221
+ defaultValue: 'all',
222
+ });
222
223
  }
223
224
  else {
224
225
  if (options.keyType !== 'development' &&
225
- options.keyType !== 'production') {
226
- logErrorAndExit('Invalid key type, must be development or production');
226
+ options.keyType !== 'production' &&
227
+ options.keyType !== 'all') {
228
+ logErrorAndExit('Invalid key type, must be development, production, or all');
227
229
  }
228
230
  }
229
231
  await this.handleLoginCommand(options);
230
- logger.endCommand(`Done! A ${options.keyType} key has been generated and saved to your .env.local file.`);
232
+ logger.endCommand(`Done! ${options.keyType} keys have been generated and saved to your .env.local file.`);
231
233
  });
232
234
  }
233
235
  setupInitCommand() {
@@ -403,7 +405,7 @@ See https://generaltranslation.com/en/docs/next/guides/local-tx`);
403
405
  const files = {};
404
406
  for (const fileExtension of fileExtensions) {
405
407
  const paths = await promptText({
406
- message: `${chalk.cyan(FILE_EXT_TO_EXT_LABEL[fileExtension])}: Please enter a space-separated list of glob patterns matching the location of the ${FILE_EXT_TO_EXT_LABEL[fileExtension]} files you would like to translate.\nMake sure to include [locale] in the patterns.\nSee https://generaltranslation.com/docs/cli/reference/config#include for more information.`,
408
+ message: `${chalk.cyan(FILE_EXT_TO_EXT_LABEL[fileExtension])}: Enter a space-separated list of glob patterns matching the location of the ${FILE_EXT_TO_EXT_LABEL[fileExtension]} files you would like to translate.\nMake sure to include [locale] in the patterns.\nSee https://generaltranslation.com/docs/cli/reference/config#include for more information.`,
407
409
  defaultValue: `./**/[locale]/*.${fileExtension}`,
408
410
  });
409
411
  files[fileExtension] = {
@@ -444,22 +446,32 @@ See https://generaltranslation.com/en/docs/next/guides/local-tx`);
444
446
  const loginQuestion = useDefaults
445
447
  ? true
446
448
  : await promptConfirm({
447
- message: `Would you like the wizard to automatically generate a ${isUsingGT ? 'development' : 'production'} API key and project ID for you?`,
449
+ message: 'Would you like the wizard to automatically generate API keys and a project ID for you?',
448
450
  defaultValue: true,
449
451
  });
450
452
  if (loginQuestion) {
451
453
  const settings = await generateSettings({});
452
- const keyType = isUsingGT ? 'development' : 'production';
454
+ const keyType = useDefaults
455
+ ? 'all'
456
+ : await promptSelect({
457
+ message: 'What type of API key would you like to generate?',
458
+ options: [
459
+ { value: 'development', label: 'Development' },
460
+ { value: 'production', label: 'Production' },
461
+ { value: 'all', label: 'Both' },
462
+ ],
463
+ defaultValue: 'all',
464
+ });
453
465
  const credentials = await retrieveCredentials(settings, keyType);
454
- await setCredentials(credentials, keyType, settings.framework);
466
+ await setCredentials(credentials, settings.framework);
455
467
  }
456
468
  }
457
469
  }
458
470
  async handleLoginCommand(options) {
459
- const settings = await generateSettings({});
460
- const keyType = options.keyType || 'production';
471
+ const settings = await generateSettings({ config: options.config });
472
+ const keyType = options.keyType || 'all';
461
473
  const credentials = await retrieveCredentials(settings, keyType);
462
- await setCredentials(credentials, keyType, settings.framework);
474
+ await setCredentials(credentials, settings.framework);
463
475
  }
464
476
  setupUpdateInstructionsCommand() {
465
477
  this.program
@@ -89,7 +89,7 @@ export async function upload(filePaths, placeholderPaths, transformPaths, dataFo
89
89
  }
90
90
  }
91
91
  if (allFiles.length === 0) {
92
- logger.error('No files to upload were found. Please check your configuration and try again.');
92
+ logger.error('No files to upload were found. Check your configuration and try again.');
93
93
  return;
94
94
  }
95
95
  if (!settings.defaultLocale) {
package/dist/cli/flags.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import findFilepath from '../fs/findFilepath.js';
2
2
  import { DEFAULT_GIT_REMOTE_NAME } from '../utils/constants.js';
3
- const DEFAULT_TIMEOUT = 600;
3
+ const DEFAULT_TIMEOUT = 900;
4
4
  export function attachSharedFlags(command) {
5
5
  command
6
6
  .option('-c, --config <path>', 'Filepath to config file, by default gt.config.json', findFilepath(['gt.config.json']))
package/dist/cli/react.js CHANGED
@@ -59,7 +59,7 @@ export class ReactCLI extends InlineCLI {
59
59
  logger.success(`Success! Added <T> tags and updated ${chalk.bold.cyan(filesUpdated.length)} files:\n` +
60
60
  filesUpdated.map((file) => `${chalk.green('-')} ${file}`).join('\n'));
61
61
  if (filesUpdated.length > 0) {
62
- logger.step(chalk.green('Please verify the changes before committing.'));
62
+ logger.step(chalk.green('Verify the changes before committing.'));
63
63
  }
64
64
  if (warnings.length > 0) {
65
65
  logger.warn(chalk.yellow('Warnings encountered:') +
@@ -53,12 +53,12 @@ export async function generateSettings(flags, cwd = process.cwd()) {
53
53
  if (gtConfig.projectId &&
54
54
  flags.projectId &&
55
55
  gtConfig.projectId !== flags.projectId) {
56
- logErrorAndExit(`Project ID mismatch between ${chalk.green(gtConfig.projectId)} and ${chalk.green(flags.projectId)}! Please use the same projectId in all configs.`);
56
+ logErrorAndExit(`Project ID mismatch between ${chalk.green(gtConfig.projectId)} and ${chalk.green(flags.projectId)}! Use the same projectId in all configs.`);
57
57
  }
58
58
  else if (gtConfig.projectId &&
59
59
  projectIdEnv &&
60
60
  gtConfig.projectId !== projectIdEnv) {
61
- logErrorAndExit(`Project ID mismatch between ${chalk.green(gtConfig.projectId)} and ${chalk.green(projectIdEnv)}! Please use the same projectId in all configs.`);
61
+ logErrorAndExit(`Project ID mismatch between ${chalk.green(gtConfig.projectId)} and ${chalk.green(projectIdEnv)}! Use the same projectId in all configs.`);
62
62
  }
63
63
  if (flags.options?.docsUrlPattern &&
64
64
  !flags.options?.docsUrlPattern.includes('[locale]')) {
@@ -18,7 +18,7 @@ export function validateSettings(settings) {
18
18
  settings.locales.some((locale) => isSupersetLocale(settings.defaultLocale, locale) &&
19
19
  !isSupersetLocale(locale, settings.defaultLocale))) {
20
20
  const locale = settings.locales.find((locale) => isSupersetLocale(settings.defaultLocale, locale));
21
- return logErrorAndExit(`defaultLocale: ${settings.defaultLocale} is a superset of another locale (${locale})! Please change the defaultLocale to a more specific locale.`);
21
+ return logErrorAndExit(`defaultLocale: ${settings.defaultLocale} is a superset of another locale (${locale})! Change the defaultLocale to a more specific locale.`);
22
22
  }
23
23
  }
24
24
  export function validateConfigExists() {
@@ -22,13 +22,13 @@ export declare const warnDataAttrOnBranch: (file: string, attrName: string, loca
22
22
  export declare const warnRecursiveFunctionCallSync: (file: string, functionName: string, location?: string) => string;
23
23
  export declare const warnDeclareStaticNotWrappedSync: (file: string, functionName: string, location?: string) => string;
24
24
  export declare const warnDeclareStaticNoResultsSync: (file: string, functionName: string, location?: string) => string;
25
- export declare const noLocalesError = "No locales found! Please provide a list of locales for translation, or specify them in your gt.config.json file.";
26
- export declare const noDefaultLocaleError = "No default locale found! Please provide a default locale, or specify it in your gt.config.json file.";
27
- export declare const noFilesError = "Incorrect or missing files configuration! Please make sure your files are configured correctly in your gt.config.json file.";
28
- export declare const noSourceFileError = "No source file found! Please double check your translations directory and default locale.";
29
- export declare const noSupportedFormatError = "Unsupported data format! Please make sure your translationsDir parameter ends with a supported file extension.";
30
- export declare const noApiKeyError = "No API key found! Please provide an API key using the --api-key flag or set the GT_API_KEY environment variable.";
31
- export declare const devApiKeyError = "You are using a development API key. Please use a production API key to use the General Translation API.\nYou can generate a production API key with the command: npx gt auth -t production";
32
- export declare const noProjectIdError = "No project ID found! Please provide a project ID using the --project-id flag, specify it in your gt.config.json file, or set the GT_PROJECT_ID environment variable.";
33
- export declare const noVersionIdError = "No version ID found! Please provide a version ID using the --version-id flag or specify it in your gt.config.json file as the _versionId property.";
34
- export declare const invalidConfigurationError = "Invalid files configuration! Please either provide a valid configuration to download local translations or set the --publish flag to true to upload translations to the CDN.";
25
+ export declare const noLocalesError = "No locales found! Provide a list of locales for translation, or specify them in your gt.config.json file.";
26
+ export declare const noDefaultLocaleError = "No default locale found! Provide a default locale, or specify it in your gt.config.json file.";
27
+ export declare const noFilesError = "Incorrect or missing files configuration! Make sure your files are configured correctly in your gt.config.json file.";
28
+ export declare const noSourceFileError = "No source file found! Double-check your translations directory and default locale.";
29
+ export declare const noSupportedFormatError = "Unsupported data format! Make sure your translationsDir parameter ends with a supported file extension.";
30
+ export declare const noApiKeyError = "No API key found! Provide an API key using the --api-key flag or set the GT_API_KEY environment variable.";
31
+ export declare const devApiKeyError = "Development API keys cannot be used with the General Translation API. Use a production API key instead.\nGenerate a production API key with: npx gt auth -t production";
32
+ export declare const noProjectIdError = "No project ID found! Provide a project ID using the --project-id flag, specify it in your gt.config.json file, or set the GT_PROJECT_ID environment variable.";
33
+ export declare const noVersionIdError = "No version ID found! Provide a version ID using the --version-id flag or specify it in your gt.config.json file as the _versionId property.";
34
+ export declare const invalidConfigurationError = "Invalid files configuration! Provide a valid configuration to download local translations or set the --publish flag to true to upload translations to the CDN.";
@@ -6,7 +6,7 @@ const withWillErrorInNextVersion = (message) => `${message} (This will become an
6
6
  const withStaticError = (message) => `<Static> rules violation: ${message}`;
7
7
  const withDeclareStaticError = (message) => `declareStatic() rules violation: ${message}`;
8
8
  // Synchronous wrappers for backward compatibility
9
- export const warnApiKeyInConfigSync = (optionsFilepath) => `${colorizeFilepath(optionsFilepath)}: Your API key is exposed! Please remove it from the file and include it as an environment variable.`;
9
+ export const warnApiKeyInConfigSync = (optionsFilepath) => `${colorizeFilepath(optionsFilepath)}: Your API key is exposed! Remove it from the file and include it as an environment variable.`;
10
10
  export const warnVariablePropSync = (file, attrName, value, location) => withLocation(file, `${colorizeComponent('<T>')} component has dynamic attribute ${colorizeIdString(attrName)} with value: ${colorizeContent(value)}. Change ${colorizeIdString(attrName)} to ensure this content is translated.`, location);
11
11
  export const warnInvalidReturnSync = (file, functionName, expression, location) => withLocation(file, withStaticError(`Function ${colorizeFunctionName(functionName)} does not return a static expression. ${colorizeFunctionName(functionName)} must return either (1) a static string literal, (2) another static function invocation, (3) static JSX content, or (4) a ternary expression. Instead got:\n${colorizeContent(expression)}`), location);
12
12
  // TODO: this is temporary until we handle implicit returns
@@ -34,13 +34,13 @@ export const warnRecursiveFunctionCallSync = (file, functionName, location) => w
34
34
  export const warnDeclareStaticNotWrappedSync = (file, functionName, location) => withLocation(file, withDeclareStaticError(`Could not resolve ${colorizeFunctionName(formatCodeClamp(functionName))}. This call is not wrapped in declareStatic(). Ensure the function is properly wrapped with declareStatic() and does not have circular import dependencies.`), location);
35
35
  export const warnDeclareStaticNoResultsSync = (file, functionName, location) => withLocation(file, withDeclareStaticError(`Could not resolve ${colorizeFunctionName(formatCodeClamp(functionName))}. DeclareStatic can only receive function invocations and cannot use undefined values or looped calls to construct its result.`), location);
36
36
  // Re-export error messages
37
- export const noLocalesError = `No locales found! Please provide a list of locales for translation, or specify them in your gt.config.json file.`;
38
- export const noDefaultLocaleError = `No default locale found! Please provide a default locale, or specify it in your gt.config.json file.`;
39
- export const noFilesError = `Incorrect or missing files configuration! Please make sure your files are configured correctly in your gt.config.json file.`;
40
- export const noSourceFileError = `No source file found! Please double check your translations directory and default locale.`;
41
- export const noSupportedFormatError = `Unsupported data format! Please make sure your translationsDir parameter ends with a supported file extension.`;
42
- export const noApiKeyError = `No API key found! Please provide an API key using the --api-key flag or set the GT_API_KEY environment variable.`;
43
- export const devApiKeyError = `You are using a development API key. Please use a production API key to use the General Translation API.\nYou can generate a production API key with the command: npx gt auth -t production`;
44
- export const noProjectIdError = `No project ID found! Please provide a project ID using the --project-id flag, specify it in your gt.config.json file, or set the GT_PROJECT_ID environment variable.`;
45
- export const noVersionIdError = `No version ID found! Please provide a version ID using the --version-id flag or specify it in your gt.config.json file as the _versionId property.`;
46
- export const invalidConfigurationError = `Invalid files configuration! Please either provide a valid configuration to download local translations or set the --publish flag to true to upload translations to the CDN.`;
37
+ export const noLocalesError = `No locales found! Provide a list of locales for translation, or specify them in your gt.config.json file.`;
38
+ export const noDefaultLocaleError = `No default locale found! Provide a default locale, or specify it in your gt.config.json file.`;
39
+ export const noFilesError = `Incorrect or missing files configuration! Make sure your files are configured correctly in your gt.config.json file.`;
40
+ export const noSourceFileError = `No source file found! Double-check your translations directory and default locale.`;
41
+ export const noSupportedFormatError = `Unsupported data format! Make sure your translationsDir parameter ends with a supported file extension.`;
42
+ export const noApiKeyError = `No API key found! Provide an API key using the --api-key flag or set the GT_API_KEY environment variable.`;
43
+ export const devApiKeyError = `Development API keys cannot be used with the General Translation API. Use a production API key instead.\nGenerate a production API key with: npx gt auth -t production`;
44
+ export const noProjectIdError = `No project ID found! Provide a project ID using the --project-id flag, specify it in your gt.config.json file, or set the GT_PROJECT_ID environment variable.`;
45
+ export const noVersionIdError = `No version ID found! Provide a version ID using the --version-id flag or specify it in your gt.config.json file as the _versionId property.`;
46
+ export const invalidConfigurationError = `Invalid files configuration! Provide a valid configuration to download local translations or set the --publish flag to true to upload translations to the CDN.`;
@@ -128,7 +128,7 @@ export async function promptConfirm({ message, defaultValue = true, cancelMessag
128
128
  // Warning display functions
129
129
  export function warnApiKeyInConfig(optionsFilepath) {
130
130
  logger.warn(`Found ${chalk.cyan('apiKey')} in "${chalk.green(optionsFilepath)}". ` +
131
- chalk.white('Your API key is exposed! Please remove it from the file and include it as an environment variable.'));
131
+ chalk.white('Your API key is exposed! Remove it from the file and include it as an environment variable.'));
132
132
  }
133
133
  export function warnVariableProp(file, attrName, value) {
134
134
  logger.warn(`Found ${chalk.green('<T>')} component in ${chalk.cyan(file)} with variable ${attrName}: "${chalk.white(value)}". ` +
@@ -150,7 +150,7 @@ export async function aggregateFiles(settings) {
150
150
  }
151
151
  }
152
152
  if (allFiles.length === 0 && !settings.publish) {
153
- logger.error('No files to translate were found. Please check your configuration and try again.');
153
+ logger.error('No files to translate were found. Check your configuration and try again.');
154
154
  }
155
155
  return allFiles;
156
156
  }
@@ -69,7 +69,7 @@ export function mergeJson(originalContent, inputPath, options, targets, defaultL
69
69
  // Get source item for default locale
70
70
  const matchingDefaultLocaleItems = findMatchingItemArray(canonicalDefaultLocale, sourceObjectOptions, sourceObjectPointer, sourceObjectValue);
71
71
  if (!Object.keys(matchingDefaultLocaleItems).length) {
72
- logger.warn(`Matching sourceItems not found at path: ${sourceObjectPointer}. Please check your JSON file includes the key field. Skipping this target`);
72
+ logger.warn(`Matching sourceItems not found at path: ${sourceObjectPointer}. Check that your JSON file includes the key field. Skipping this target`);
73
73
  continue;
74
74
  }
75
75
  const matchingDefaultLocaleItemKeys = new Set(Object.keys(matchingDefaultLocaleItems));
@@ -47,7 +47,7 @@ export function parseJson(content, filePath, options, defaultLocale) {
47
47
  // Find matching source items
48
48
  const matchingItems = findMatchingItemArray(defaultLocale, sourceObjectOptions, sourceObjectPointer, sourceObjectValue);
49
49
  if (!Object.keys(matchingItems).length) {
50
- logger.error(`Matching sourceItem not found at path: ${sourceObjectPointer} for locale: ${defaultLocale}. Please check your JSON schema`);
50
+ logger.error(`Matching sourceItem not found at path: ${sourceObjectPointer} for locale: ${defaultLocale}. Check your JSON schema`);
51
51
  return exitSync(1);
52
52
  }
53
53
  // Construct lvl 3
@@ -12,7 +12,7 @@ export function determineLibrary() {
12
12
  const packageJsonPath = path.join(cwd, 'package.json');
13
13
  // Check if package.json exists
14
14
  if (!fs.existsSync(packageJsonPath)) {
15
- logger.warn(chalk.yellow('No package.json found in the current directory. Please run this command from the root of your project.'));
15
+ logger.warn(chalk.yellow('No package.json found in the current directory. Run this command from the root of your project.'));
16
16
  return { library: 'base', additionalModules: [] };
17
17
  }
18
18
  // Read and parse package.json
@@ -1 +1 @@
1
- export declare const PACKAGE_VERSION = "2.7.0";
1
+ export declare const PACKAGE_VERSION = "2.7.1";
@@ -1,2 +1,2 @@
1
1
  // This file is auto-generated. Do not edit manually.
2
- export const PACKAGE_VERSION = '2.7.0';
2
+ export const PACKAGE_VERSION = '2.7.1';
@@ -5,5 +5,5 @@ export async function setupLocadex(settings) {
5
5
  await import('open').then((open) => open.default(urlToOpen, {
6
6
  wait: false,
7
7
  }));
8
- logger.message(`${chalk.dim(`If the browser window didn't open automatically, please open the following link:`)}\n\n${chalk.cyan(urlToOpen)}`);
8
+ logger.message(`${chalk.dim(`If the browser window didn't open automatically, open the following link:`)}\n\n${chalk.cyan(urlToOpen)}`);
9
9
  }
@@ -18,7 +18,7 @@ export async function addVitePlugin({ errors, warnings, filesUpdated, packageJso
18
18
  VITE_CONFIG_PATH_BASE + 'cts',
19
19
  ]);
20
20
  if (!viteConfigPath) {
21
- logger.error(`No ${VITE_CONFIG_PATH_BASE}[js|ts|mjs|mts|cjs|cts] file found. Please add the @generaltranslation/compiler plugin to your vite configuration file:
21
+ logger.error(`No ${VITE_CONFIG_PATH_BASE}[js|ts|mjs|mts|cjs|cts] file found. Add the @generaltranslation/compiler plugin to your vite configuration file:
22
22
  import { vite as gtCompiler } from '@generaltranslation/compiler';
23
23
  export default defineConfig({
24
24
  plugins: [gtCompiler()],
@@ -102,7 +102,7 @@ async function updateViteConfigAst({ code, warnings, viteConfigPath, packageJson
102
102
  if (!pluginAlreadyPresent) {
103
103
  success = addPluginInvocation({ ast, alias, namespaces });
104
104
  if (!success) {
105
- warnings.push(`Failed to add gt compiler plugin to ${viteConfigPath}. Please add the plugin manually:
105
+ warnings.push(`Failed to add gt compiler plugin to ${viteConfigPath}. Add the plugin manually:
106
106
  import { vite as gtCompiler } from '@generaltranslation/compiler';
107
107
  export default defineConfig({
108
108
  plugins: [gtCompiler()],
@@ -15,14 +15,14 @@ export async function getDesiredLocales() {
15
15
  validate: (input) => {
16
16
  const localeList = input.split(' ');
17
17
  if (localeList.length === 0) {
18
- return 'Please enter at least one locale';
18
+ return 'Enter at least one locale';
19
19
  }
20
20
  if (localeList.some((locale) => !locale.trim())) {
21
- return 'Please enter a valid locale (e.g., es fr de)';
21
+ return 'Enter a valid locale (e.g., es fr de)';
22
22
  }
23
23
  for (const locale of localeList) {
24
24
  if (!gt.isValidLocale(locale)) {
25
- return 'Please enter a valid locale (e.g., es fr de)';
25
+ return 'Enter a valid locale (e.g., es fr de)';
26
26
  }
27
27
  }
28
28
  return true;
@@ -58,7 +58,7 @@ Please let us know what you would like to see added at https://github.com/genera
58
58
  });
59
59
  const packageJson = await getPackageJson();
60
60
  if (!packageJson) {
61
- logger.error(chalk.red('No package.json found in the current directory. Please run this command from the root of your project.'));
61
+ logger.error(chalk.red('No package.json found in the current directory. Run this command from the root of your project.'));
62
62
  exitSync(1);
63
63
  }
64
64
  // Check if gt-next or gt-react is installed
@@ -37,7 +37,7 @@ export async function aggregateInlineTranslations(options, settings, library) {
37
37
  }
38
38
  }
39
39
  if (updates.length == 0) {
40
- logger.error(chalk.red(`No in-line content or dictionaries were found for ${chalk.green(library)}. Are you sure you're running this command in the right directory?`));
40
+ logger.error(chalk.red(`No inline content or dictionaries were found for ${chalk.green(library)}. Are you sure you're running this command in the right directory?`));
41
41
  return updates;
42
42
  }
43
43
  return updates;
@@ -92,7 +92,7 @@ export async function validateProject(settings, pkg, files) {
92
92
  .join('')));
93
93
  }
94
94
  if (updates.length === 0) {
95
- logger.error(chalk.red(`No in-line content or dictionaries were found for ${chalk.green(pkg)}. Are you sure you're running this command in the right directory?`));
95
+ logger.error(chalk.red(`No inline content or dictionaries were found for ${chalk.green(pkg)}. Are you sure you're running this command in the right directory?`));
96
96
  }
97
97
  else {
98
98
  logger.success(chalk.green(`Success! Found ${updates.length} translatable entries for ${chalk.green(pkg)}.`));
@@ -151,7 +151,7 @@ export function addExplicitAnchorIds(translatedContent, sourceHeadingMap, settin
151
151
  : 'translated file';
152
152
  logger.warn(`Header count mismatch detected! ${sourceFile} has ${sourceHeadingMap.length} headers but ${translatedFile} has ${translatedHeadings.length} headers. ` +
153
153
  `This likely means your source file was edited after translation was requested, causing a mismatch between ` +
154
- `the number of headers in your source file vs the translated file. Please re-translate this file to resolve the issue.`);
154
+ `the number of headers in your source file vs the translated file. Re-translate this file to resolve the issue.`);
155
155
  }
156
156
  // Create ID mapping based on positional matching
157
157
  const idMappings = new Map();
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Calculate timeout in ms with validation
3
+ */
4
+ export declare function calculateTimeoutMs(timeout: string | number | undefined): number;
@@ -0,0 +1,8 @@
1
+ import { DEFAULT_TIMEOUT_SECONDS } from './constants.js';
2
+ /**
3
+ * Calculate timeout in ms with validation
4
+ */
5
+ export function calculateTimeoutMs(timeout) {
6
+ const value = timeout !== undefined ? Number(timeout) : DEFAULT_TIMEOUT_SECONDS;
7
+ return (Number.isFinite(value) ? value : DEFAULT_TIMEOUT_SECONDS) * 1000;
8
+ }
@@ -3,3 +3,4 @@ export declare const GT_CONFIG_SCHEMA_URL = "https://assets.gtx.dev/config-schem
3
3
  export declare const TEMPLATE_FILE_NAME = "__INTERNAL_GT_TEMPLATE_NAME__";
4
4
  export declare const TEMPLATE_FILE_ID: string;
5
5
  export declare const DEFAULT_GIT_REMOTE_NAME = "origin";
6
+ export declare const DEFAULT_TIMEOUT_SECONDS = 900;
@@ -4,3 +4,4 @@ export const GT_CONFIG_SCHEMA_URL = 'https://assets.gtx.dev/config-schema.json';
4
4
  export const TEMPLATE_FILE_NAME = '__INTERNAL_GT_TEMPLATE_NAME__';
5
5
  export const TEMPLATE_FILE_ID = hashStringSync(TEMPLATE_FILE_NAME);
6
6
  export const DEFAULT_GIT_REMOTE_NAME = 'origin';
7
+ export const DEFAULT_TIMEOUT_SECONDS = 900;
@@ -1,12 +1,16 @@
1
1
  import { Settings, SupportedFrameworks } from '../types/index.js';
2
2
  type Credentials = {
3
- apiKey: string;
3
+ apiKeys: ApiKey[];
4
4
  projectId: string;
5
5
  };
6
- export declare function retrieveCredentials(settings: Settings, keyType: 'development' | 'production'): Promise<Credentials>;
7
- export declare function generateCredentialsSession(url: string, keyType: 'development' | 'production'): Promise<{
6
+ type ApiKey = {
7
+ key: string;
8
+ type: 'development' | 'production';
9
+ };
10
+ export declare function retrieveCredentials(settings: Settings, keyType: 'development' | 'production' | 'all'): Promise<Credentials>;
11
+ export declare function generateCredentialsSession(url: string, keyType: 'development' | 'production' | 'all'): Promise<{
8
12
  sessionId: string;
9
13
  }>;
10
14
  export declare function areCredentialsSet(): string | undefined;
11
- export declare function setCredentials(credentials: Credentials, type: 'development' | 'production', framework?: SupportedFrameworks, cwd?: string): Promise<void>;
15
+ export declare function setCredentials(credentials: Credentials, framework?: SupportedFrameworks, cwd?: string): Promise<void>;
12
16
  export {};
@@ -3,6 +3,7 @@ import { logger } from '../console/logger.js';
3
3
  import path from 'node:path';
4
4
  import fs from 'node:fs';
5
5
  import chalk from 'chalk';
6
+ import apiRequest from './fetch.js';
6
7
  // Fetches project ID and API key by opening the dashboard in the browser
7
8
  export async function retrieveCredentials(settings, keyType) {
8
9
  // Generate a session ID
@@ -18,15 +19,13 @@ export async function retrieveCredentials(settings, keyType) {
18
19
  const interval = setInterval(async () => {
19
20
  // Ping the dashboard to see if the credentials are set
20
21
  try {
21
- const res = await fetch(`${settings.baseUrl}/cli/wizard/${sessionId}`, {
22
- method: 'GET',
23
- });
22
+ const res = await apiRequest(settings.baseUrl, `/cli/wizard/${sessionId}`, { method: 'GET' });
24
23
  if (res.status === 200) {
25
24
  const data = await res.json();
26
25
  resolve(data);
27
26
  clearInterval(interval);
28
27
  clearTimeout(timeout);
29
- fetch(`${settings.baseUrl}/cli/wizard/${sessionId}`, {
28
+ apiRequest(settings.baseUrl, `/cli/wizard/${sessionId}`, {
30
29
  method: 'DELETE',
31
30
  });
32
31
  }
@@ -46,14 +45,8 @@ export async function retrieveCredentials(settings, keyType) {
46
45
  return credentials;
47
46
  }
48
47
  export async function generateCredentialsSession(url, keyType) {
49
- const res = await fetch(`${url}/cli/wizard/session`, {
50
- method: 'POST',
51
- headers: {
52
- 'Content-Type': 'application/json',
53
- },
54
- body: JSON.stringify({
55
- keyType,
56
- }),
48
+ const res = await apiRequest(url, '/cli/wizard/session', {
49
+ body: { keyType },
57
50
  });
58
51
  if (!res.ok) {
59
52
  logErrorAndExit('Failed to generate credentials session');
@@ -62,10 +55,11 @@ export async function generateCredentialsSession(url, keyType) {
62
55
  }
63
56
  // Checks if the credentials are set in the environment variables
64
57
  export function areCredentialsSet() {
65
- return process.env.GT_PROJECT_ID && process.env.GT_API_KEY;
58
+ return (process.env.GT_PROJECT_ID &&
59
+ (process.env.GT_API_KEY || process.env.GT_DEV_API_KEY));
66
60
  }
67
61
  // Sets the credentials in .env.local file
68
- export async function setCredentials(credentials, type, framework, cwd = process.cwd()) {
62
+ export async function setCredentials(credentials, framework, cwd = process.cwd()) {
69
63
  const envFile = path.join(cwd, '.env.local');
70
64
  let envContent = '';
71
65
  // Check if .env.local exists, create it if it doesn't
@@ -107,11 +101,13 @@ export async function setCredentials(credentials, type, framework, cwd = process
107
101
  prefix = 'REDWOOD_ENV_';
108
102
  }
109
103
  envContent += `\n${prefix}GT_PROJECT_ID=${credentials.projectId}\n`;
110
- if (type === 'development') {
111
- envContent += `${prefix || ''}GT_DEV_API_KEY=${credentials.apiKey}\n`;
112
- }
113
- else {
114
- envContent += `GT_API_KEY=${credentials.apiKey}\n`;
104
+ for (const apiKey of credentials.apiKeys) {
105
+ if (apiKey.type === 'development') {
106
+ envContent += `${prefix || ''}GT_DEV_API_KEY=${apiKey.key}\n`;
107
+ }
108
+ else {
109
+ envContent += `${prefix || ''}GT_API_KEY=${apiKey.key}\n`;
110
+ }
115
111
  }
116
112
  // Ensure we don't have excessive newlines
117
113
  envContent = envContent.replace(/\n{3,}/g, '\n\n').trim() + '\n';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @internal
3
+ *
4
+ * Makes an API request to the General Translation API.
5
+ *
6
+ * Encapsulates URL construction, headers, and JSON parsing.
7
+ */
8
+ export default function apiRequest<T>(baseUrl: string, endpoint: string, options?: {
9
+ body?: unknown;
10
+ method?: 'GET' | 'POST' | 'DELETE';
11
+ }): Promise<Response>;
@@ -0,0 +1,22 @@
1
+ import { API_VERSION } from 'generaltranslation';
2
+ /**
3
+ * @internal
4
+ *
5
+ * Makes an API request to the General Translation API.
6
+ *
7
+ * Encapsulates URL construction, headers, and JSON parsing.
8
+ */
9
+ export default async function apiRequest(baseUrl, endpoint, options) {
10
+ const method = options?.method ?? 'POST';
11
+ const requestInit = {
12
+ method,
13
+ headers: {
14
+ 'Content-Type': 'application/json',
15
+ 'gt-api-version': API_VERSION,
16
+ },
17
+ };
18
+ if (options?.body !== undefined) {
19
+ requestInit.body = JSON.stringify(options.body);
20
+ }
21
+ return fetch(`${baseUrl}${endpoint}`, requestInit);
22
+ }
@@ -20,7 +20,7 @@ export async function installPackage(packageName, packageManager, asDevDependenc
20
20
  }
21
21
  childProcess.on('error', (error) => {
22
22
  logger.error(chalk.red(`Installation error: ${error.message}`));
23
- logger.info(`Please manually install ${packageName} with: ${packageManager.name} ${packageManager.installCommand} ${packageName}`);
23
+ logger.info(`Manually install ${packageName} with: ${packageManager.name} ${packageManager.installCommand} ${packageName}`);
24
24
  reject(error);
25
25
  });
26
26
  childProcess.on('close', (code) => {
@@ -32,7 +32,7 @@ export async function installPackage(packageName, packageManager, asDevDependenc
32
32
  if (errorOutput) {
33
33
  logger.error(chalk.red(`Error details: ${errorOutput}`));
34
34
  }
35
- logger.info(`Please manually install ${packageName} with: ${packageManager.name} ${packageManager.installCommand} ${packageName}`);
35
+ logger.info(`Manually install ${packageName} with: ${packageManager.name} ${packageManager.installCommand} ${packageName}`);
36
36
  reject(new Error(`Process exited with code ${code}`));
37
37
  }
38
38
  });
@@ -57,7 +57,7 @@ export async function installPackageGlobal(packageName, version) {
57
57
  }
58
58
  childProcess.on('error', (error) => {
59
59
  logger.error(chalk.red(`Installation error: ${error.message}`));
60
- logger.info(`Please manually install ${packageName} with: npm install -g ${packageName}`);
60
+ logger.info(`Manually install ${packageName} with: npm install -g ${packageName}`);
61
61
  reject(error);
62
62
  });
63
63
  childProcess.on('close', (code) => {
@@ -69,7 +69,7 @@ export async function installPackageGlobal(packageName, version) {
69
69
  if (errorOutput) {
70
70
  logger.error(chalk.red(`Error details: ${errorOutput}`));
71
71
  }
72
- logger.info(`Please manually install ${packageName} with: npm install -g ${packageName}`);
72
+ logger.info(`Manually install ${packageName} with: npm install -g ${packageName}`);
73
73
  reject(new Error(`Process exited with code ${code}`));
74
74
  }
75
75
  });
@@ -258,7 +258,7 @@ export async function getPackageManager(cwd = process.cwd(), specifiedPackageMan
258
258
  throw new NoPackageManagerError('No package manager found');
259
259
  }
260
260
  const selectedPackageManager = await promptSelect({
261
- message: 'Please select your package manager.',
261
+ message: 'Select your package manager.',
262
262
  options: packageManagers.map((packageManager) => ({
263
263
  value: packageManager,
264
264
  label: packageManager.label,
@@ -4,13 +4,7 @@ import { UploadSourcesStep } from './steps/UploadSourcesStep.js';
4
4
  import { SetupStep } from './steps/SetupStep.js';
5
5
  import { BranchStep } from './steps/BranchStep.js';
6
6
  import { logCollectedFiles } from '../console/logging.js';
7
- /**
8
- * Helper: Calculate timeout with validation
9
- */
10
- function calculateTimeout(timeout) {
11
- const value = timeout !== undefined ? Number(timeout) : 600;
12
- return (Number.isFinite(value) ? value : 600) * 1000;
13
- }
7
+ import { calculateTimeoutMs } from '../utils/calculateTimeoutMs.js';
14
8
  /**
15
9
  * Sets up a project by uploading files running the setup step
16
10
  * @param files - Array of file objects to upload
@@ -23,7 +17,7 @@ export async function runSetupProjectWorkflow(files, options, settings) {
23
17
  // Log files to be translated
24
18
  logCollectedFiles(files);
25
19
  // Calculate timeout for setup step
26
- const timeoutMs = calculateTimeout(options.timeout);
20
+ const timeoutMs = calculateTimeoutMs(options.timeout);
27
21
  // Create workflow with steps
28
22
  const branchStep = new BranchStep(gt, settings);
29
23
  const uploadStep = new UploadSourcesStep(gt, settings);
@@ -5,13 +5,7 @@ import { SetupStep } from './steps/SetupStep.js';
5
5
  import { EnqueueStep } from './steps/EnqueueStep.js';
6
6
  import { BranchStep } from './steps/BranchStep.js';
7
7
  import { UserEditDiffsStep } from './steps/UserEditDiffsStep.js';
8
- /**
9
- * Helper: Calculate timeout with validation
10
- */
11
- function calculateTimeout(timeout) {
12
- const value = timeout !== undefined ? Number(timeout) : 600;
13
- return (Number.isFinite(value) ? value : 600) * 1000;
14
- }
8
+ import { calculateTimeoutMs } from '../utils/calculateTimeoutMs.js';
15
9
  /**
16
10
  * Sends multiple files for translation to the API using a workflow pattern
17
11
  * @param files - Array of file objects to translate
@@ -24,7 +18,7 @@ export async function runStageFilesWorkflow({ files, options, settings, }) {
24
18
  // Log files to be translated
25
19
  logCollectedFiles(files);
26
20
  // Calculate timeout for setup step
27
- const timeoutMs = calculateTimeout(options.timeout);
21
+ const timeoutMs = calculateTimeoutMs(options.timeout);
28
22
  // Create workflow with steps
29
23
  const branchStep = new BranchStep(gt, settings);
30
24
  const uploadStep = new UploadSourcesStep(gt, settings);
@@ -90,7 +90,7 @@ export class BranchStep extends WorkflowStep {
90
90
  }
91
91
  else {
92
92
  if (!current) {
93
- return logErrorAndExit('Failed to determine the current branch. Please specify a custom branch or enable automatic branch detection.');
93
+ return logErrorAndExit('Failed to determine the current branch. Specify a custom branch or enable automatic branch detection.');
94
94
  }
95
95
  const currentBranch = branchData.branches.find((b) => b.name === current.currentBranchName);
96
96
  if (!currentBranch) {
@@ -103,7 +103,7 @@ export class BranchStep extends WorkflowStep {
103
103
  }
104
104
  catch (error) {
105
105
  if (error instanceof ApiError && error.code === 403) {
106
- logger.warn('To enable translation branching, please upgrade your plan. Falling back to default branch.');
106
+ logger.warn('To enable translation branching, upgrade your plan. Falling back to default branch.');
107
107
  // retry with default branch
108
108
  try {
109
109
  const createBranchResult = await this.gt.createBranch({
@@ -121,7 +121,7 @@ export class BranchStep extends WorkflowStep {
121
121
  }
122
122
  }
123
123
  if (this.branchData.currentBranch.id === '') {
124
- return logErrorAndExit('Something went wrong while resolving branch information. Please try again.');
124
+ return logErrorAndExit('Something went wrong while resolving branch information. Try again.');
125
125
  }
126
126
  // Now set the incoming and checked out branches (first one that exists)
127
127
  this.branchData.incomingBranch =
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gt",
3
- "version": "2.7.0",
3
+ "version": "2.7.1",
4
4
  "main": "dist/index.js",
5
5
  "bin": "dist/main.js",
6
6
  "files": [
@@ -109,7 +109,7 @@
109
109
  "unified": "^11.0.5",
110
110
  "unist-util-visit": "^5.0.0",
111
111
  "yaml": "^2.8.0",
112
- "generaltranslation": "8.1.13",
112
+ "generaltranslation": "8.1.14",
113
113
  "gt-remark": "1.0.5"
114
114
  },
115
115
  "devDependencies": {