@crowdin/app-project-module 0.81.1 → 0.82.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
@@ -83,6 +83,7 @@ const projectReportsApp = __importStar(require("./modules/project-reports"));
83
83
  const projectToolsApp = __importStar(require("./modules/project-tools"));
84
84
  const webhooks = __importStar(require("./modules/webhooks"));
85
85
  const workflowStepType = __importStar(require("./modules/workflow-step-type"));
86
+ const aiRequestProcessors = __importStar(require("./modules/ai-request-processors"));
86
87
  const subscription_1 = require("./util/subscription");
87
88
  var types_2 = require("./types");
88
89
  Object.defineProperty(exports, "ProjectPermissions", { enumerable: true, get: function () { return types_2.ProjectPermissions; } });
@@ -190,6 +191,7 @@ function addCrowdinEndpoints(app, clientConfig) {
190
191
  externalQaCheck.register({ config, app });
191
192
  webhooks.register({ config, app });
192
193
  workflowStepType.register({ config, app });
194
+ aiRequestProcessors.register({ config, app });
193
195
  addFormSchema({ config, app });
194
196
  return Object.assign(Object.assign({}, exports.metadataStore), { storage: storage.getStorage(), establishCrowdinConnection: (authRequest, moduleKey) => {
195
197
  let jwtToken = '';
@@ -218,28 +220,26 @@ function addCrowdinEndpoints(app, clientConfig) {
218
220
  }
219
221
  exports.addCrowdinEndpoints = addCrowdinEndpoints;
220
222
  function addFormSchema({ app, config }) {
221
- let moduleConfigWithForm = null;
222
223
  for (const moduleKey of Object.keys(config)) {
223
224
  const moduleConfig = config[moduleKey];
224
225
  if ((0, form_schema_1.hasFormSchema)(moduleConfig)) {
225
- moduleConfigWithForm = (0, form_schema_1.getLowCodeUiConfigFromModuleConfig)(moduleConfig);
226
- break;
226
+ const moduleConfigWithForm = (0, form_schema_1.getLowCodeUiConfigFromModuleConfig)(moduleConfig);
227
+ if (moduleConfigWithForm) {
228
+ app.get(`/api/${moduleConfigWithForm.key}/form-data`, json_response_1.default, (0, crowdin_client_1.default)({
229
+ config,
230
+ optional: false,
231
+ checkSubscriptionExpiration: true,
232
+ moduleKey: moduleConfigWithForm.key,
233
+ }), (0, credentials_masker_1.getRequestCredentialsMasker)({ moduleConfig: moduleConfigWithForm }), (0, form_data_display_1.default)(config));
234
+ app.post(`/api/${moduleConfigWithForm.key}/form-data`, (0, crowdin_client_1.default)({
235
+ config,
236
+ optional: false,
237
+ checkSubscriptionExpiration: true,
238
+ moduleKey: moduleConfigWithForm.key,
239
+ }), (0, credentials_masker_1.postRequestCredentialsMasker)(moduleConfigWithForm), (0, form_data_save_1.default)(config));
240
+ }
227
241
  }
228
242
  }
229
- if (moduleConfigWithForm) {
230
- app.get('/api/form-data', json_response_1.default, (0, crowdin_client_1.default)({
231
- config,
232
- optional: false,
233
- checkSubscriptionExpiration: true,
234
- moduleKey: moduleConfigWithForm.key,
235
- }), (0, credentials_masker_1.getRequestCredentialsMasker)({ moduleConfig: moduleConfigWithForm }), (0, form_data_display_1.default)(config));
236
- app.post('/api/form-data', (0, crowdin_client_1.default)({
237
- config,
238
- optional: false,
239
- checkSubscriptionExpiration: true,
240
- moduleKey: moduleConfigWithForm.key,
241
- }), (0, credentials_masker_1.postRequestCredentialsMasker)(moduleConfigWithForm), (0, form_data_save_1.default)(config));
242
- }
243
243
  }
244
244
  function convertClientConfig(clientConfig) {
245
245
  const baseUrl = clientConfig.baseUrl || process.env.URL;
@@ -18,8 +18,12 @@ function handle(moduleConfig) {
18
18
  return (0, util_1.runAsyncWrapper)((req, res, next) => __awaiter(this, void 0, void 0, function* () {
19
19
  if (moduleConfig.formSchema) {
20
20
  return res.render('form', {
21
- formGetDataUrl: moduleConfig.formGetDataUrl ? moduleConfig.formGetDataUrl : '/api/form-data',
22
- formPostDataUrl: moduleConfig.formPostDataUrl ? moduleConfig.formPostDataUrl : '/api/form-data',
21
+ formGetDataUrl: moduleConfig.formGetDataUrl
22
+ ? moduleConfig.formGetDataUrl
23
+ : `/api/${moduleConfig.key}/form-data`,
24
+ formPostDataUrl: moduleConfig.formPostDataUrl
25
+ ? moduleConfig.formPostDataUrl
26
+ : `/api/${moduleConfig.key}/form-data`,
23
27
  formSchema: JSON.stringify(moduleConfig.formSchema),
24
28
  formUiSchema: moduleConfig.formUiSchema ? JSON.stringify(moduleConfig.formUiSchema) : '{}',
25
29
  });
@@ -0,0 +1,5 @@
1
+ /// <reference types="qs" />
2
+ import { Response } from 'express';
3
+ import { CrowdinClientRequest } from '../../types';
4
+ import { AiRequestProcessorModule, AiStreamProcessorModule } from './types';
5
+ export default function handle(module: AiRequestProcessorModule | AiStreamProcessorModule, canHandleStream?: boolean): (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,63 @@
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
+ const BUFFERING_FLAG = '~buffering~';
15
+ function handle(module, canHandleStream = false) {
16
+ return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
17
+ try {
18
+ if (canHandleStream && req.headers['transfer-encoding'] === 'chunked') {
19
+ if (!('processStream' in module)) {
20
+ res.writeHead(202, {}).end();
21
+ return;
22
+ }
23
+ res.writeHead(200, {
24
+ Connection: 'keep-alive',
25
+ 'Cache-Control': 'no-cache',
26
+ 'Content-Type': 'text/event-stream',
27
+ 'X-Accel-Buffering': 'no',
28
+ });
29
+ req.on('data', (chunk) => __awaiter(this, void 0, void 0, function* () {
30
+ let chunkStr = chunk.toString();
31
+ // actually we checked if processStream exists already, but need do to this again to calm down TS compiler
32
+ chunkStr = module.processStream
33
+ ? yield module.processStream(chunkStr, req.query, req.crowdinApiClient, req.crowdinContext)
34
+ : chunkStr;
35
+ if (!chunkStr && typeof chunkStr !== 'string') {
36
+ res.write(BUFFERING_FLAG);
37
+ return;
38
+ }
39
+ res.write(chunkStr);
40
+ }));
41
+ req.on('end', () => {
42
+ res.end();
43
+ });
44
+ req.on('error', (e) => {
45
+ if (req.logError) {
46
+ req.logError(e);
47
+ }
48
+ res.end();
49
+ });
50
+ return;
51
+ }
52
+ const result = yield module.processRequest(req.body, req.query, req.crowdinApiClient, req.crowdinContext);
53
+ res.send(result);
54
+ }
55
+ catch (e) {
56
+ if (req.logError) {
57
+ req.logError(e);
58
+ }
59
+ res.send({ error: { message: (0, logger_1.getErrorMessage)(e) } });
60
+ }
61
+ }));
62
+ }
63
+ exports.default = handle;
@@ -0,0 +1,16 @@
1
+ import { Express } from 'express';
2
+ import { Config } from '../../types';
3
+ export declare enum AiRequestProcessorModuleWithStream {
4
+ aiRequestPreParse = "aiRequestPreParse"
5
+ }
6
+ export declare enum AiRequestProcessorModuleWithoutStream {
7
+ aiRequestPreCompile = "aiRequestPreCompile",
8
+ aiRequestPostCompile = "aiRequestPostCompile",
9
+ aiRequestPostParse = "aiRequestPostParse"
10
+ }
11
+ export type AiRequestProcessorModuleType = AiRequestProcessorModuleWithoutStream | AiRequestProcessorModuleWithStream;
12
+ export declare function generateModuleSlugFromType(moduleType: string): string;
13
+ export declare function register({ config, app }: {
14
+ config: Config;
15
+ app: Express;
16
+ }): void;
@@ -0,0 +1,45 @@
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 = exports.generateModuleSlugFromType = exports.AiRequestProcessorModuleWithoutStream = exports.AiRequestProcessorModuleWithStream = void 0;
7
+ const json_response_1 = __importDefault(require("../../middlewares/json-response"));
8
+ const crowdin_client_1 = __importDefault(require("../../middlewares/crowdin-client"));
9
+ const handler_1 = __importDefault(require("./handler"));
10
+ var AiRequestProcessorModuleWithStream;
11
+ (function (AiRequestProcessorModuleWithStream) {
12
+ AiRequestProcessorModuleWithStream["aiRequestPreParse"] = "aiRequestPreParse";
13
+ })(AiRequestProcessorModuleWithStream = exports.AiRequestProcessorModuleWithStream || (exports.AiRequestProcessorModuleWithStream = {}));
14
+ var AiRequestProcessorModuleWithoutStream;
15
+ (function (AiRequestProcessorModuleWithoutStream) {
16
+ AiRequestProcessorModuleWithoutStream["aiRequestPreCompile"] = "aiRequestPreCompile";
17
+ AiRequestProcessorModuleWithoutStream["aiRequestPostCompile"] = "aiRequestPostCompile";
18
+ AiRequestProcessorModuleWithoutStream["aiRequestPostParse"] = "aiRequestPostParse";
19
+ })(AiRequestProcessorModuleWithoutStream = exports.AiRequestProcessorModuleWithoutStream || (exports.AiRequestProcessorModuleWithoutStream = {}));
20
+ function generateModuleSlugFromType(moduleType) {
21
+ return moduleType
22
+ .split(/\.?(?=[A-Z])/)
23
+ .join('-')
24
+ .toLowerCase();
25
+ }
26
+ exports.generateModuleSlugFromType = generateModuleSlugFromType;
27
+ function registerProcessorModuleByTypeKey(moduleKey, { config, app }) {
28
+ const module = config[moduleKey];
29
+ if (!module) {
30
+ return;
31
+ }
32
+ app.post('/ai-request-processor/' + generateModuleSlugFromType(moduleKey), json_response_1.default, (0, crowdin_client_1.default)({
33
+ config,
34
+ optional: false,
35
+ checkSubscriptionExpiration: true,
36
+ moduleKey: module.key,
37
+ }), (0, handler_1.default)(module, moduleKey in AiRequestProcessorModuleWithStream));
38
+ }
39
+ function register({ config, app }) {
40
+ [
41
+ ...Object.values(AiRequestProcessorModuleWithoutStream),
42
+ ...Object.values(AiRequestProcessorModuleWithStream),
43
+ ].forEach((moduleTypeKey) => registerProcessorModuleByTypeKey(moduleTypeKey, { config, app }));
44
+ }
45
+ exports.register = register;
@@ -0,0 +1,14 @@
1
+ import { CrowdinContextInfo, ModuleKey } from '../../types';
2
+ import Crowdin from '@crowdin/crowdin-api-client';
3
+ export interface AiRequestProcessorModule extends ModuleKey {
4
+ /**
5
+ * updates request data
6
+ */
7
+ processRequest: (requestData: any, requestContext: any, client: Crowdin, context: CrowdinContextInfo) => Promise<never>;
8
+ }
9
+ export interface AiStreamProcessorModule extends AiRequestProcessorModule {
10
+ /**
11
+ * updates request data
12
+ */
13
+ processStream?: (chunk: any, requestContext: any, client: Crowdin, context: CrowdinContextInfo) => Promise<string | null>;
14
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -92,11 +92,31 @@ function handle(baseConfig, config, folderName) {
92
92
  break;
93
93
  case types_1.ProcessFileJobType.POST_IMPORT:
94
94
  case types_1.ProcessFileJobType.PRE_EXPORT:
95
- const { strings, error: stringsFileError } = fileProcessResult;
95
+ const { strings, error: stringsFileError, previewFile } = fileProcessResult;
96
+ let maxSize = files_1.MAX_BODY_SIZE;
97
+ if (strings && previewFile && body.jobType === types_1.ProcessFileJobType.POST_IMPORT) {
98
+ maxSize = maxSize / 2;
99
+ }
100
+ if (previewFile && body.jobType === types_1.ProcessFileJobType.POST_IMPORT) {
101
+ if (Buffer.byteLength(previewFile) < maxSize) {
102
+ response.preview = previewFile.toString('base64');
103
+ }
104
+ else {
105
+ let url;
106
+ if (config.storeFile) {
107
+ url = yield config.storeFile(previewFile);
108
+ }
109
+ else {
110
+ const storedFile = yield (0, files_1.storeFile)(previewFile, path_1.default.join(folderPath, folderName));
111
+ url = `${baseFilesUrl}?file=${storedFile}`;
112
+ }
113
+ response.previewUrl = url;
114
+ }
115
+ }
96
116
  if (strings) {
97
117
  const stringsNDJson = strings.map((s) => JSON.stringify(s)).join('\n\r');
98
118
  const bufferData = Buffer.from(stringsNDJson, 'utf-8');
99
- if (Buffer.byteLength(bufferData) < files_1.MAX_BODY_SIZE) {
119
+ if (Buffer.byteLength(bufferData) < maxSize) {
100
120
  response.strings = strings;
101
121
  }
102
122
  else {
@@ -115,7 +115,7 @@ export interface BuildFileResponse {
115
115
  fileName?: string;
116
116
  fileType?: string;
117
117
  }
118
- export interface StringsFileResponse extends Omit<ParseFileResponse, 'previewFile'> {
118
+ export interface StringsFileResponse extends ParseFileResponse {
119
119
  notModified?: boolean;
120
120
  }
121
121
  export interface ContentFileResponse extends BuildFileResponse {
@@ -130,7 +130,7 @@ exports.runUpdateProviderJob = runUpdateProviderJob;
130
130
  function filesCron({ config, integration, period, }) {
131
131
  return __awaiter(this, void 0, void 0, function* () {
132
132
  (0, logger_1.log)(`Starting files cron job with period [${period}]`);
133
- const syncSettingsList = yield (0, storage_1.getStorage)().getAllSyncSettingsByType('schedule');
133
+ const syncSettingsList = yield (0, storage_1.getStorage)().getSyncSettingsBySchedule('schedule', period);
134
134
  const crowdinSyncSettings = syncSettingsList.filter((syncSettings) => syncSettings.provider === types_1.Provider.CROWDIN);
135
135
  const integrationSyncSettings = syncSettingsList.filter((syncSettings) => syncSettings.provider === types_1.Provider.INTEGRATION);
136
136
  yield Promise.all(crowdinSyncSettings.map((syncSettings) => processSyncSettings({ config, integration, period, syncSettings })));
@@ -6,6 +6,7 @@ const subscription_1 = require("../util/subscription");
6
6
  const api_1 = require("./api/api");
7
7
  const util_2 = require("./ai-tools/util");
8
8
  const util_3 = require("./workflow-step-type/util");
9
+ const ai_request_processors_1 = require("./ai-request-processors");
9
10
  function normalizeEnvironments(environments) {
10
11
  if (Array.isArray(environments)) {
11
12
  return environments;
@@ -236,6 +237,22 @@ function handle(config) {
236
237
  })), (uiModule ? { url: '/settings/' + (uiModule.fileName || 'index.html') } : {})),
237
238
  ];
238
239
  }
240
+ [
241
+ ...Object.values(ai_request_processors_1.AiRequestProcessorModuleWithoutStream),
242
+ ...Object.values(ai_request_processors_1.AiRequestProcessorModuleWithStream),
243
+ ].forEach((moduleType) => {
244
+ const module = config[moduleType];
245
+ if (!module) {
246
+ return;
247
+ }
248
+ const moduleSlug = (0, ai_request_processors_1.generateModuleSlugFromType)(moduleType);
249
+ modules[moduleSlug] = [
250
+ {
251
+ key: config.identifier + '-' + moduleSlug,
252
+ processorUrl: '/ai-request-processor/' + moduleSlug,
253
+ },
254
+ ];
255
+ });
239
256
  if (config.aiPromptProvider) {
240
257
  // prevent possible overrides of the other modules
241
258
  config.aiPromptProvider = Object.assign(Object.assign({}, config.aiPromptProvider), { key: config.identifier + '-ai-prompt-provider' });