@crowdin/app-project-module 0.57.1 → 0.58.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.
package/out/index.js CHANGED
@@ -65,6 +65,7 @@ const aiPromptProvider = __importStar(require("./modules/ai-prompt-provider"));
65
65
  const aiTools = __importStar(require("./modules/ai-tools"));
66
66
  const editorRightPanelApp = __importStar(require("./modules/editor-right-panel"));
67
67
  const editorThemesApp = __importStar(require("./modules/editor-themes"));
68
+ const externalQaCheck = __importStar(require("./modules/external-qa-check"));
68
69
  const fileProcessingApps = __importStar(require("./modules/file-processing"));
69
70
  const integrationApp = __importStar(require("./modules/integration"));
70
71
  const modalApp = __importStar(require("./modules/modal"));
@@ -170,6 +171,7 @@ function addCrowdinEndpoints(app, clientConfig) {
170
171
  aiPromptProvider.register({ config, app });
171
172
  aiTools.registerAiTools({ config, app });
172
173
  aiTools.registerAiToolWidgets({ config, app });
174
+ externalQaCheck.register({ config, app });
173
175
  addFormSchema({ config, app });
174
176
  return Object.assign(Object.assign({}, exports.metadataStore), { establishCrowdinConnection: (authRequest) => {
175
177
  let jwtToken = '';
@@ -0,0 +1,5 @@
1
+ /// <reference types="qs" />
2
+ import { Response } from 'express';
3
+ import { CrowdinClientRequest } from '../../../types';
4
+ import { ExternalQaCheckModule } from '../types';
5
+ export default function handle(externalQaCheck: ExternalQaCheckModule): (req: CrowdinClientRequest | import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const util_1 = require("../../../util");
13
+ const logger_1 = require("../../../util/logger");
14
+ function handle(externalQaCheck) {
15
+ return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
16
+ const body = req.body.data;
17
+ (0, logger_1.log)('Received request for external qa check');
18
+ (0, logger_1.log)(`Payload ${JSON.stringify(body, null, 2)}`);
19
+ try {
20
+ const response = yield externalQaCheck.validate({
21
+ client: req.crowdinApiClient,
22
+ file: body.file,
23
+ project: body.project,
24
+ sourceLanguage: body.sourceLanguage,
25
+ strings: body.strings,
26
+ targetLanguage: body.targetLanguage,
27
+ translations: body.translations,
28
+ context: req.crowdinContext,
29
+ });
30
+ res.send({
31
+ data: { validations: response.validations },
32
+ error: response.error ? { message: response.error } : undefined,
33
+ });
34
+ }
35
+ catch (e) {
36
+ if (req.logError) {
37
+ req.logError(e);
38
+ }
39
+ res.send({ error: { message: (0, logger_1.getErrorMessage)(e) } });
40
+ }
41
+ }));
42
+ }
43
+ exports.default = handle;
@@ -0,0 +1,6 @@
1
+ import { Config } from '../../types';
2
+ import { Express } from 'express';
3
+ export declare function register({ config, app }: {
4
+ config: Config;
5
+ app: Express;
6
+ }): void;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.register = void 0;
7
+ const json_response_1 = __importDefault(require("../../middlewares/json-response"));
8
+ const ui_module_1 = __importDefault(require("../../middlewares/ui-module"));
9
+ const render_ui_module_1 = __importDefault(require("../../middlewares/render-ui-module"));
10
+ const crowdin_client_1 = __importDefault(require("../../middlewares/crowdin-client"));
11
+ const validate_1 = __importDefault(require("./handlers/validate"));
12
+ function register({ config, app }) {
13
+ const qaCheck = config.externalQaCheck;
14
+ if (!qaCheck) {
15
+ return;
16
+ }
17
+ if (qaCheck === null || qaCheck === void 0 ? void 0 : qaCheck.batchSize) {
18
+ app.use('/batch-size', json_response_1.default, (req, res) => {
19
+ res.send({ data: { size: qaCheck.batchSize } });
20
+ });
21
+ }
22
+ app.use('/validate', json_response_1.default, (0, crowdin_client_1.default)(config), (0, validate_1.default)(qaCheck));
23
+ if (qaCheck.settingsUiModule) {
24
+ app.use('/settings', (0, ui_module_1.default)(config, true), (0, render_ui_module_1.default)(qaCheck.settingsUiModule));
25
+ }
26
+ }
27
+ exports.register = register;
@@ -0,0 +1,57 @@
1
+ import { CrowdinContextInfo, Environments, UiModule } from '../../types';
2
+ import Crowdin, { SourceFilesModel, ProjectsGroupsModel, LanguagesModel, SourceStringsModel, StringTranslationsModel } from '@crowdin/crowdin-api-client';
3
+ export interface ExternalQaCheckModule extends Environments {
4
+ /**
5
+ * module description
6
+ */
7
+ description?: string;
8
+ /**
9
+ * module name
10
+ */
11
+ name?: string;
12
+ /**
13
+ * jobs chunk size
14
+ */
15
+ batchSize?: number;
16
+ validate: ({ client, file, project, sourceLanguage, strings, targetLanguage, translations, context, }: {
17
+ client?: Crowdin;
18
+ file?: SourceFilesModel.File;
19
+ project?: ProjectsGroupsModel.Project;
20
+ sourceLanguage: LanguagesModel.Language;
21
+ strings: SourceStringsModel.String[];
22
+ targetLanguage: LanguagesModel.Language;
23
+ translations: StringTranslationsModel.StringTranslation[];
24
+ context: CrowdinContextInfo;
25
+ }) => Promise<ExternalQaCheckResponse>;
26
+ /**
27
+ * Settings UI module
28
+ */
29
+ settingsUiModule?: UiModule;
30
+ }
31
+ export interface ExternalQaCheckRequest {
32
+ file: SourceFilesModel.File;
33
+ project: ProjectsGroupsModel.Project;
34
+ sourceLanguage: LanguagesModel.Language;
35
+ strings: SourceStringsModel.String[];
36
+ targetLanguage: LanguagesModel.Language;
37
+ translations: StringTranslationsModel.StringTranslation[];
38
+ }
39
+ interface SuggestedFix {
40
+ indexStart: number;
41
+ indexEnd: number;
42
+ replacement: string;
43
+ }
44
+ interface ValidationError {
45
+ message: string;
46
+ suggestedFixes?: SuggestedFix[];
47
+ }
48
+ interface Validation {
49
+ translationId: number;
50
+ passed: boolean;
51
+ error?: ValidationError;
52
+ }
53
+ export interface ExternalQaCheckResponse {
54
+ validations: Validation[];
55
+ error?: string;
56
+ }
57
+ export {};
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -241,6 +241,12 @@ function filesCron({ config, integration, period, }) {
241
241
  });
242
242
  }
243
243
  catch (e) {
244
+ yield (0, logger_1.handleUserError)({
245
+ action: 'Auto sync files to External Service',
246
+ error: e,
247
+ crowdinId: syncSettings.crowdinId,
248
+ clientId: syncSettings.integrationId,
249
+ });
244
250
  (0, logger_1.logError)(e, context);
245
251
  continue;
246
252
  }
@@ -285,6 +291,12 @@ function filesCron({ config, integration, period, }) {
285
291
  });
286
292
  }
287
293
  catch (e) {
294
+ yield (0, logger_1.handleUserError)({
295
+ action: 'Auto sync files to Crowdin',
296
+ error: e,
297
+ crowdinId: syncSettings.crowdinId,
298
+ clientId: syncSettings.integrationId,
299
+ });
288
300
  (0, logger_1.logError)(e, context);
289
301
  continue;
290
302
  }
@@ -207,6 +207,12 @@ function handle(config) {
207
207
  })));
208
208
  }
209
209
  }
210
+ if (config.externalQaCheck) {
211
+ const uiModule = config.externalQaCheck.settingsUiModule;
212
+ modules['external-qa-check'] = [
213
+ Object.assign(Object.assign({ key: config.identifier + '-qa-check', name: config.externalQaCheck.name || config.name, description: config.externalQaCheck.description || config.description, runQaCheckUrl: '/validate' }, (config.externalQaCheck.batchSize ? { getBatchSizeUrl: '/batch-size' } : {})), (uiModule ? { url: '/settings/' + (uiModule.fileName || 'index.html') } : {})),
214
+ ];
215
+ }
210
216
  const events = {
211
217
  installed: '/installed',
212
218
  uninstall: '/uninstall',
package/out/types.d.ts CHANGED
@@ -15,6 +15,7 @@ import { LogErrorFunction, LogFunction } from './util/logger';
15
15
  import { AiProviderModule } from './modules/ai-provider/types';
16
16
  import { AiPromptProviderModule } from './modules/ai-prompt-provider/types';
17
17
  import { AiTool, AiToolWidget } from './modules/ai-tools/types';
18
+ import { ExternalQaCheckModule } from './modules/external-qa-check/types';
18
19
  export interface ClientConfig extends ImagePath {
19
20
  /**
20
21
  * Authentication Crowdin App type: "authorization_code", "crowdin_app", "crowdin_agent". Default: "crowdin_app"
@@ -198,6 +199,10 @@ export interface ClientConfig extends ImagePath {
198
199
  * AI tool_calls modules with UI widgets
199
200
  */
200
201
  aiToolsWidget?: AiToolWidget | AiToolWidget[];
202
+ /**
203
+ * qa check module
204
+ */
205
+ externalQaCheck?: ExternalQaCheckModule & ImagePath;
201
206
  }
202
207
  export interface Environments {
203
208
  environments?: Environment | Environment[];
@@ -221,7 +221,7 @@ function storeUserError({ action, error, crowdinId, clientId, }) {
221
221
  if (error instanceof AppModuleError && error.appData) {
222
222
  data.appData = error.appData;
223
223
  }
224
- yield (0, storage_1.getStorage)().saveUserError(action, error.message, JSON.stringify(data), `${Date.now()}`, crowdinId, clientId);
224
+ yield (0, storage_1.getStorage)().saveUserError(action, (error === null || error === void 0 ? void 0 : error.message) || JSON.stringify(error, null, 2), JSON.stringify(data), `${Date.now()}`, crowdinId, clientId);
225
225
  });
226
226
  }
227
227
  function clearOldUserErrors(crowdinId, clientId) {
@@ -1141,12 +1141,25 @@
1141
1141
  document.getElementById('user-errors').classList.add('hidden');
1142
1142
  }
1143
1143
 
1144
+ function isJSON(string) {
1145
+ try {
1146
+ JSON.parse(string);
1147
+ return true;
1148
+ } catch (e) {
1149
+ return false;
1150
+ }
1151
+ }
1152
+
1144
1153
  function getUserErrorDetail(error) {
1145
1154
  const data = JSON.parse(error.data);
1146
1155
 
1147
1156
  let html = '<div class="error-detail-table"><table>';
1148
1157
  html += `<tr><td>Action</td><td>${sanitizeHTML(error.action)}</td></tr>`;
1149
- html += `<tr><td>Message</td><td>${sanitizeHTML(error.message)}</td></tr>`;
1158
+ if (isJSON(error.message)) {
1159
+ html += `<tr><td>Message</td><td><pre>${sanitizeHTML(error.message)}<pre></td></tr>`;
1160
+ } else {
1161
+ html += `<tr><td>Message</td><td>${sanitizeHTML(error.message)}</td></tr>`;
1162
+ }
1150
1163
  html += `<tr><td>Date/time</td><td>${sanitizeHTML(error.createdAt)}</td></tr>`;
1151
1164
 
1152
1165
  if (data.requestParams) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdin/app-project-module",
3
- "version": "0.57.1",
3
+ "version": "0.58.1",
4
4
  "description": "Module that generates for you all common endpoints for serving standalone Crowdin App",
5
5
  "main": "out/index.js",
6
6
  "types": "out/index.d.ts",