@crowdin/app-project-module 0.105.1 → 0.107.0

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 (33) hide show
  1. package/out/app-test/integration/get-integration-files.js +0 -1
  2. package/out/app-test/integration/mocks/mock-axios.js +0 -1
  3. package/out/app-test/integration/update-crowdin.js +0 -1
  4. package/out/app-test/integration/update-integration.js +0 -1
  5. package/out/modules/ai-provider/handlers/chat-completions.js +0 -1
  6. package/out/modules/ai-provider/util/index.js +0 -2
  7. package/out/modules/api/api.js +0 -1
  8. package/out/modules/custom-mt/handlers/translate.d.ts +2 -2
  9. package/out/modules/custom-mt/handlers/translate.js +44 -4
  10. package/out/modules/custom-mt/index.js +5 -2
  11. package/out/modules/custom-mt/types.d.ts +12 -1
  12. package/out/modules/integration/handlers/oauth-login.js +2 -1
  13. package/out/modules/integration/handlers/oauth-polling.d.ts +2 -2
  14. package/out/modules/integration/handlers/oauth-polling.js +5 -3
  15. package/out/modules/integration/handlers/sync-settings-save.js +0 -2
  16. package/out/modules/integration/index.js +1 -1
  17. package/out/modules/integration/util/cron.js +1 -10
  18. package/out/modules/integration/util/defaults.js +31 -28
  19. package/out/modules/integration/util/files.js +18 -11
  20. package/out/modules/integration/util/job.js +0 -4
  21. package/out/modules/integration/util/snapshot.js +1 -5
  22. package/out/modules/integration/util/webhooks.js +1 -8
  23. package/out/modules/manifest.js +4 -2
  24. package/out/modules/status.js +1 -1
  25. package/out/storage/index.js +0 -1
  26. package/out/storage/mysql.js +0 -3
  27. package/out/storage/postgre.js +0 -1
  28. package/out/storage/sqlite.js +0 -3
  29. package/out/util/axios.js +0 -1
  30. package/out/util/index.d.ts +1 -0
  31. package/out/util/index.js +5 -1
  32. package/out/util/logger.js +0 -4
  33. package/package.json +9 -7
@@ -62,7 +62,6 @@ const getIntegrationFilesTest = ({ appConfig, integrationTestConfig, }) => __awa
62
62
  yield (0, util_1.assert)(() => (0, globals_1.expect)(allFiles).toHaveLength(expectedTree.length), `Received files length: ${allFiles.length}, but expected tree length ${expectedTree.length}`);
63
63
  yield (0, util_1.assert)(() => (0, globals_1.expect)(allFiles).toEqual(globals_1.expect.arrayContaining(expectedTree.map((node) => globals_1.expect.objectContaining(node)))), 'Received files properties do not match to the expected properties');
64
64
  if (getIntegrationFiles === null || getIntegrationFiles === void 0 ? void 0 : getIntegrationFiles.extraChecks) {
65
- // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
66
65
  // @ts-ignore
67
66
  yield (0, util_1.assert)(() => getIntegrationFiles.extraChecks(), 'ExtraChecks for getIntegrationFiles() fails');
68
67
  }
@@ -10,7 +10,6 @@ const mock = Object.assign(Object.assign({}, mockOriginalAxios), { get: jest.fn(
10
10
  const storageId = file === null || file === void 0 ? void 0 : file.storageId;
11
11
  expect(storageId).not.toBeUndefined();
12
12
  return {
13
- // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
14
13
  // @ts-ignore
15
14
  data: index_1.mockStoragesById[storageId].data,
16
15
  };
@@ -134,7 +134,6 @@ const updateCrowdinTest = ({ appConfig, integrationTestConfig, }) => __awaiter(v
134
134
  yield (0, util_1.assert)(() => (0, globals_1.expect)(createdDirectories).toHaveLength(expectedDirectories.length), `Directories created: ${createdDirectories.length}, but expected: ${expectedDirectories.length}`);
135
135
  yield (0, util_1.assert)(() => (0, globals_1.expect)(createdDirectories).toEqual(globals_1.expect.arrayContaining(expectedDirectories.map((f) => globals_1.expect.objectContaining(f)))), 'The properties of the created directories do not match to the expected');
136
136
  if (updateCrowdin === null || updateCrowdin === void 0 ? void 0 : updateCrowdin.extraChecks) {
137
- // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
138
137
  // @ts-ignore
139
138
  yield (0, util_1.assert)(() => updateCrowdin.extraChecks({ request, result }), 'ExtraChecks for updateCrowdin() fails');
140
139
  }
@@ -75,7 +75,6 @@ const updateIntegrationTest = ({ appConfig, integrationTestConfig, }) => __await
75
75
  }
76
76
  if (updateIntegration === null || updateIntegration === void 0 ? void 0 : updateIntegration.extraChecks) {
77
77
  yield (0, util_1.assert)(
78
- // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
79
78
  // @ts-ignore
80
79
  () => updateIntegration.extraChecks({ request, result }), 'ExtraChecks for updateIntegration() fails');
81
80
  }
@@ -57,7 +57,6 @@ function handle(aiProvider) {
57
57
  isStream,
58
58
  tools,
59
59
  toolChoice,
60
- /* eslint-disable @typescript-eslint/camelcase */
61
60
  sendEvent: ({ content, role, tool_calls }) => __awaiter(this, void 0, void 0, function* () {
62
61
  if (!isStream) {
63
62
  chunks.push({ content, role, tool_calls });
@@ -24,7 +24,6 @@ function normalizeContentParts(content) {
24
24
  ? content.map((part) => part.type === 'image'
25
25
  ? {
26
26
  type: 'image_url',
27
- // eslint-disable-next-line @typescript-eslint/camelcase
28
27
  image_url: {
29
28
  url: part.url,
30
29
  },
@@ -79,7 +78,6 @@ function mergeToolCalls(chunks) {
79
78
  }
80
79
  exports.mergeToolCalls = mergeToolCalls;
81
80
  function mergeChatCompletionChunks(chunks) {
82
- /* eslint-disable @typescript-eslint/camelcase */
83
81
  const tool_calls = mergeToolCalls(chunks);
84
82
  return [
85
83
  {
@@ -164,7 +164,6 @@ function updateCrowdinContext(req, context) {
164
164
  if (context.clientId.includes('undefined')) {
165
165
  context.clientId = `${context.jwtPayload.domain || context.jwtPayload.context.organization_id}__${(_b = req.body) === null || _b === void 0 ? void 0 : _b.projectId}__${context.jwtPayload.sub}`;
166
166
  }
167
- // eslint-disable-next-line @typescript-eslint/camelcase
168
167
  context.jwtPayload.context.project_id = (_c = req.body) === null || _c === void 0 ? void 0 : _c.projectId;
169
168
  }
170
169
  return context;
@@ -1,5 +1,5 @@
1
1
  /// <reference types="qs" />
2
2
  import { Response } from 'express';
3
3
  import { CrowdinClientRequest } from '../../../types';
4
- import { CustomMTLogic } from '../types';
5
- export default function handle(config: CustomMTLogic): (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;
4
+ import { CustomMTHandleOptions } from '../types';
5
+ export default function handle({ baseUrl, folder, config }: CustomMTHandleOptions): (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;
@@ -8,10 +8,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
11
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
+ const fs_1 = __importDefault(require("fs"));
16
+ const path_1 = __importDefault(require("path"));
12
17
  const util_1 = require("../../../util");
13
18
  const logger_1 = require("../../../util/logger");
14
- function handle(config) {
19
+ const files_1 = require("../../file-processing/util/files");
20
+ function handle({ baseUrl, folder, config }) {
21
+ const dataFolder = path_1.default.join(folder, 'custom-mt');
22
+ if (!fs_1.default.existsSync(dataFolder)) {
23
+ fs_1.default.mkdirSync(dataFolder, { recursive: true });
24
+ }
25
+ const baseFilesUrl = `${baseUrl}/file/download/custom-mt`;
15
26
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
16
27
  const source = req.query.source;
17
28
  const target = req.query.target;
@@ -21,7 +32,17 @@ function handle(config) {
21
32
  (0, logger_1.log)(`Payload ${JSON.stringify(body, null, 2)}`);
22
33
  const projectId = req.query.project_id ? Number(req.query.project_id) : null;
23
34
  try {
24
- const isValidationRequest = source === 'en' && target === 'de' && body.strings && body.strings[0] === 'validation';
35
+ let strings;
36
+ if (body.strings) {
37
+ strings = body.strings;
38
+ }
39
+ else if (body.stringsUrl) {
40
+ strings = (yield (0, files_1.getFileStrings)(body.stringsUrl));
41
+ }
42
+ else {
43
+ throw new Error('Bad payload received: No strings found');
44
+ }
45
+ const isValidationRequest = source === 'en' && target === 'de' && strings.length > 0 && strings[0] === 'validation';
25
46
  if (isValidationRequest) {
26
47
  if (config.validate) {
27
48
  yield config.validate(req.crowdinApiClient);
@@ -29,8 +50,27 @@ function handle(config) {
29
50
  res.send({ data: { translations: [] } });
30
51
  return;
31
52
  }
32
- const translations = yield config.translate(req.crowdinApiClient, req.crowdinContext, projectId, source, target, body.strings);
33
- res.send({ data: { translations } });
53
+ const translations = yield config.translate(req.crowdinApiClient, req.crowdinContext, projectId, source, target, strings);
54
+ if (!Array.isArray(translations)) {
55
+ res.send({ data: { translations } });
56
+ return;
57
+ }
58
+ const translationsNDJson = translations.map((t) => JSON.stringify(t)).join('\n');
59
+ const bufferData = Buffer.from(translationsNDJson, 'utf8');
60
+ if (Buffer.byteLength(bufferData) < files_1.MAX_BODY_SIZE) {
61
+ res.send({ data: { translations } });
62
+ }
63
+ else {
64
+ let url;
65
+ if (config.storeFile) {
66
+ url = yield config.storeFile(bufferData);
67
+ }
68
+ else {
69
+ const storedFile = yield (0, files_1.storeFile)(bufferData, dataFolder);
70
+ url = `${baseFilesUrl}?file=${storedFile}`;
71
+ }
72
+ res.send({ data: { translationsUrl: url } });
73
+ }
34
74
  }
35
75
  catch (e) {
36
76
  req.logError(e);
@@ -6,24 +6,27 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.register = void 0;
7
7
  const crowdin_client_1 = __importDefault(require("../../middlewares/crowdin-client"));
8
8
  const util_1 = require("../../util");
9
+ const file_download_1 = __importDefault(require("../file-processing/handlers/file-download"));
9
10
  const translate_1 = __importDefault(require("./handlers/translate"));
10
11
  function register({ config, app }) {
11
12
  if (!config.customMT) {
12
13
  return;
13
14
  }
15
+ const folder = config.customMT.filesFolder || config.dbFolder;
14
16
  app.get((0, util_1.getLogoUrl)(config.customMT, '/mt'), (req, res) => { var _a; return res.sendFile(((_a = config.customMT) === null || _a === void 0 ? void 0 : _a.imagePath) || config.imagePath); });
15
17
  app.post('/mt/translate', (0, crowdin_client_1.default)({
16
18
  config,
17
19
  optional: false,
18
20
  checkSubscriptionExpiration: true,
19
21
  moduleKey: config.customMT.key,
20
- }), (0, translate_1.default)(config.customMT));
22
+ }), (0, translate_1.default)({ baseUrl: config.baseUrl, folder, config: config.customMT }));
23
+ app.get('/file/download/custom-mt', (0, file_download_1.default)(config, config.customMT, 'custom-mt'));
21
24
  // TEMPORARY CODE: it needs to support old path
22
25
  app.post('/translate', (0, crowdin_client_1.default)({
23
26
  config,
24
27
  optional: false,
25
28
  checkSubscriptionExpiration: true,
26
- }), (0, translate_1.default)(config.customMT));
29
+ }), (0, translate_1.default)({ baseUrl: config.baseUrl, folder, config: config.customMT }));
27
30
  // END TEMPORARY CODE
28
31
  }
29
32
  exports.register = register;
@@ -1,14 +1,25 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
1
3
  import Crowdin, { SourceStringsModel } from '@crowdin/crowdin-api-client';
2
4
  import { CrowdinContextInfo, ModuleKey } from '../../types';
3
5
  export interface CustomMTLogic extends ModuleKey {
4
6
  withContext?: boolean;
7
+ splitStringsIntoChunks?: boolean;
5
8
  batchSize?: number;
6
9
  maskEntities?: boolean;
10
+ filesFolder?: string;
7
11
  translate: (client: Crowdin, context: CrowdinContextInfo, projectId: number | null, source: string, target: string, strings: CustomMtString[]) => Promise<string[]>;
8
12
  validate?: (client: Crowdin) => Promise<void>;
13
+ storeFile?: (fileContent: Buffer) => Promise<string>;
9
14
  }
10
15
  export interface CustomMTRequest {
11
- strings: CustomMtString[];
16
+ strings?: CustomMtString[];
17
+ stringsUrl?: string;
18
+ }
19
+ export interface CustomMTHandleOptions {
20
+ baseUrl: string;
21
+ folder: string;
22
+ config: CustomMTLogic;
12
23
  }
13
24
  export type CustomMtString = string | {
14
25
  id: number;
@@ -63,7 +63,8 @@ function handle(config, integration) {
63
63
  message.data = oauthCredentials;
64
64
  if (((_o = integration.oauthLogin) === null || _o === void 0 ? void 0 : _o.mode) === 'polling') {
65
65
  yield (0, storage_1.getStorage)().deleteMetadata((0, defaults_1.getOAuthPollingId)(clientId));
66
- yield (0, storage_1.getStorage)().saveMetadata((0, defaults_1.getOAuthPollingId)(clientId), oauthCredentials, organization);
66
+ const encryptedCredentials = (0, util_1.encryptData)(config, JSON.stringify(oauthCredentials));
67
+ yield (0, storage_1.getStorage)().saveMetadata((0, defaults_1.getOAuthPollingId)(clientId), encryptedCredentials, organization);
67
68
  }
68
69
  return res.render('oauth', { message: JSON.stringify(message), oauthMode: (_p = integration.oauthLogin) === null || _p === void 0 ? void 0 : _p.mode });
69
70
  }
@@ -1,5 +1,5 @@
1
1
  /// <reference types="qs" />
2
2
  import { Response } from 'express';
3
- import { CrowdinClientRequest } from '../../../types';
3
+ import { Config, CrowdinClientRequest } from '../../../types';
4
4
  import { IntegrationLogic } from '../types';
5
- export default function handle(integration: IntegrationLogic): (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;
5
+ export default function handle(config: Config, integration: IntegrationLogic): (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;
@@ -12,7 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const storage_1 = require("../../../storage");
13
13
  const util_1 = require("../../../util");
14
14
  const defaults_1 = require("../util/defaults");
15
- function handle(integration) {
15
+ function handle(config, integration) {
16
16
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
17
17
  req.logInfo('Received OAuth polling request');
18
18
  const { oauthLogin } = integration;
@@ -22,9 +22,11 @@ function handle(integration) {
22
22
  return;
23
23
  }
24
24
  const oauthId = (0, defaults_1.getOAuthPollingId)(req.crowdinContext.clientId);
25
- const oauthCreds = yield (0, storage_1.getStorage)().getMetadata(oauthId);
26
- if (oauthCreds) {
25
+ const encryptedCreds = yield (0, storage_1.getStorage)().getMetadata(oauthId);
26
+ let oauthCreds;
27
+ if (encryptedCreds) {
27
28
  yield (0, storage_1.getStorage)().deleteMetadata(oauthId);
29
+ oauthCreds = JSON.parse((0, util_1.decryptData)(config, encryptedCreds));
28
30
  }
29
31
  res.send({ oauthCreds });
30
32
  }));
@@ -62,9 +62,7 @@ function handle(config, integration) {
62
62
  const allFiles = expandedFiles.map((node) => ({
63
63
  id: node.id,
64
64
  name: node.name,
65
- // eslint-disable-next-line @typescript-eslint/camelcase
66
65
  node_type: node.nodeType,
67
- // eslint-disable-next-line @typescript-eslint/camelcase
68
66
  parent_id: node.parentId,
69
67
  schedule: true,
70
68
  sync: false,
@@ -194,7 +194,7 @@ function register({ config, app }) {
194
194
  optional: false,
195
195
  checkSubscriptionExpiration: false,
196
196
  moduleKey: integrationLogic.key,
197
- }), (0, oauth_polling_1.default)(integrationLogic));
197
+ }), (0, oauth_polling_1.default)(config, integrationLogic));
198
198
  }
199
199
  }
200
200
  if (integrationLogic.cronJobs) {
@@ -172,13 +172,9 @@ function processSyncSettings({ config, integration, period, syncSettings, }) {
172
172
  const context = {
173
173
  jwtPayload: {
174
174
  context: {
175
- // eslint-disable-next-line @typescript-eslint/camelcase
176
175
  project_id: projectId,
177
- // eslint-disable-next-line @typescript-eslint/camelcase
178
176
  organization_id: crowdinCredentials.organizationId,
179
- // eslint-disable-next-line @typescript-eslint/camelcase
180
177
  organization_domain: crowdinCredentials.domain,
181
- // eslint-disable-next-line @typescript-eslint/camelcase
182
178
  user_id: crowdinCredentials.userId,
183
179
  },
184
180
  },
@@ -221,7 +217,6 @@ function processSyncSettings({ config, integration, period, syncSettings, }) {
221
217
  (0, logger_1.logError)(e, context);
222
218
  return;
223
219
  }
224
- // eslint-disable-next-line @typescript-eslint/camelcase
225
220
  context.jwtPayload.context.project_identifier = projectData.identifier;
226
221
  const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, crowdinClient, projectId);
227
222
  const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
@@ -348,11 +343,7 @@ function processSyncSettings({ config, integration, period, syncSettings, }) {
348
343
  (0, logger_1.log)(`updateIntegration task for files cron job with period [${period}] for project ${projectId} completed`);
349
344
  }
350
345
  else {
351
- const allIntFiles = [...files, ...newFiles].map((file) => (Object.assign({ id: file.id, name: file.name, parentId: file.parent_id || file.parentId,
352
- // eslint-disable-next-line @typescript-eslint/camelcase
353
- parent_id: file.parent_id || file.parentId,
354
- // eslint-disable-next-line @typescript-eslint/camelcase
355
- node_type: file.nodeType || file.node_type }, (file.type ? { type: file.type } : {}))));
346
+ const allIntFiles = [...files, ...newFiles].map((file) => (Object.assign({ id: file.id, name: file.name, parentId: file.parent_id || file.parentId, parent_id: file.parent_id || file.parentId, node_type: file.nodeType || file.node_type }, (file.type ? { type: file.type } : {}))));
356
347
  let intFiles = allIntFiles.filter((file) => 'type' in file);
357
348
  if (integration.forcePushSources === true && currentFileSnapshot.length > 0) {
358
349
  const snapshotMap = new Map(currentFileSnapshot.map((f) => [f.id, f]));
@@ -61,7 +61,6 @@ function applyIntegrationModuleDefaults(config, integration) {
61
61
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
62
62
  if (!integration.getCrowdinFiles) {
63
63
  integration.getCrowdinFiles = (projectId, client, rootFolder, config, mode) => __awaiter(this, void 0, void 0, function* () {
64
- const allBranches = (yield client.sourceFilesApi.withFetchAll().listProjectBranches(projectId)).data.map((d) => d.data);
65
64
  let options = {};
66
65
  if (rootFolder) {
67
66
  options = {
@@ -69,23 +68,38 @@ function applyIntegrationModuleDefaults(config, integration) {
69
68
  recursion: 'true',
70
69
  };
71
70
  }
71
+ const needDirectories = !mode || mode === 'directories' || mode === 'files';
72
+ const needFiles = !mode || mode === 'files';
73
+ const [branchesResponse, directoriesResponse, filesResponse] = yield Promise.all([
74
+ client.sourceFilesApi.withFetchAll().listProjectBranches(projectId),
75
+ needDirectories
76
+ ? client.sourceFilesApi.withFetchAll().listProjectDirectories(projectId, options)
77
+ : { data: [] },
78
+ needFiles ? client.sourceFilesApi.withFetchAll().listProjectFiles(projectId, options) : { data: [] },
79
+ ]);
80
+ const allBranches = branchesResponse.data.map((d) => d.data);
81
+ const allDirectories = directoriesResponse.data.map((d) => d.data);
82
+ const allFiles = filesResponse.data.map((d) => d.data);
83
+ const branchesMap = new Map(allBranches.map((b) => [b.id, b]));
84
+ const addedBranchIds = new Set();
72
85
  const res = [];
86
+ const addBranchIfNeeded = (branchId) => {
87
+ const branch = branchesMap.get(branchId);
88
+ if (branch && !addedBranchIds.has(branch.id)) {
89
+ addedBranchIds.add(branch.id);
90
+ res.push({
91
+ id: branch.id.toString(),
92
+ name: branch.name,
93
+ nodeType: '2',
94
+ });
95
+ }
96
+ return branch === null || branch === void 0 ? void 0 : branch.id;
97
+ };
73
98
  if (!mode || mode === 'directories') {
74
- const allDirectories = (yield client.sourceFilesApi.withFetchAll().listProjectDirectories(projectId, options)).data.map((d) => d.data);
75
99
  allDirectories.forEach((e) => {
76
100
  let parentId = rootFolder && e.directoryId === rootFolder.id ? undefined : e.directoryId;
77
101
  if (!parentId && e.branchId) {
78
- const branch = allBranches.find((branch) => branch.id === e.branchId);
79
- if (branch) {
80
- parentId = branch.id;
81
- if (!res.find((node) => node.id === branch.id.toString())) {
82
- res.push({
83
- id: branch.id.toString(),
84
- name: branch.name,
85
- nodeType: '2',
86
- });
87
- }
88
- }
102
+ parentId = addBranchIfNeeded(e.branchId);
89
103
  }
90
104
  res.push({
91
105
  id: e.id.toString(),
@@ -96,26 +110,15 @@ function applyIntegrationModuleDefaults(config, integration) {
96
110
  }
97
111
  if (!mode || mode === 'files') {
98
112
  const directoryIds = mode === 'files'
99
- ? (yield client.sourceFilesApi.withFetchAll().listProjectDirectories(projectId, options)).data.map((d) => d.data.id)
113
+ ? allDirectories.map((d) => d.id)
100
114
  : res.filter((item) => !item.type && item.nodeType !== '2').map((d) => parseInt(d.id));
101
- let files = (yield client.sourceFilesApi.withFetchAll().listProjectFiles(projectId, options)).data.map((d) => d.data);
102
- files = files.filter((f) => (rootFolder && f.directoryId === rootFolder.id) ||
115
+ const filteredFiles = allFiles.filter((f) => (rootFolder && f.directoryId === rootFolder.id) ||
103
116
  directoryIds.includes(f.directoryId) ||
104
117
  (!rootFolder && !f.directoryId));
105
- files.forEach((e) => {
118
+ filteredFiles.forEach((e) => {
106
119
  let parentId = rootFolder && e.directoryId === rootFolder.id ? undefined : e.directoryId;
107
120
  if (!parentId && e.branchId) {
108
- const branch = allBranches.find((branch) => branch.id === e.branchId);
109
- if (branch) {
110
- parentId = branch.id;
111
- if (!res.find((node) => node.id === branch.id.toString())) {
112
- res.push({
113
- id: branch.id.toString(),
114
- name: branch.name,
115
- nodeType: '2',
116
- });
117
- }
118
- }
121
+ parentId = addBranchIfNeeded(e.branchId);
119
122
  }
120
123
  res.push({
121
124
  id: e.id.toString(),
@@ -213,17 +213,25 @@ function markUnsyncedFiles({ integrationId, crowdinId, client, files, }) {
213
213
  let unsyncedFiles = (unsyncedFilesData === null || unsyncedFilesData === void 0 ? void 0 : unsyncedFilesData.files)
214
214
  ? JSON.parse(unsyncedFilesData === null || unsyncedFilesData === void 0 ? void 0 : unsyncedFilesData.files)
215
215
  : [];
216
- const fileIds = files.filter((file) => isFileLeaf(file)).map((file) => `${file.id}`);
217
- const idsToRemove = unsyncedFiles.filter((file) => !fileIds.includes(file.id)).map((file) => file.id);
218
- unsyncedFiles = unsyncedFiles.filter((file) => !idsToRemove.includes(file.id));
216
+ if (unsyncedFiles.length === 0) {
217
+ return files;
218
+ }
219
+ const fileIds = new Set(files.filter((file) => isFileLeaf(file)).map((file) => `${file.id}`));
220
+ const idsToRemove = unsyncedFiles.filter((file) => !fileIds.has(`${file.id}`)).map((file) => file.id);
221
+ if (idsToRemove.length > 0) {
222
+ unsyncedFiles = unsyncedFiles.filter((file) => !idsToRemove.includes(file.id));
223
+ yield (0, storage_1.getStorage)().updateUnsyncedFiles({
224
+ integrationId,
225
+ crowdinId,
226
+ files: JSON.stringify(unsyncedFiles),
227
+ });
228
+ }
229
+ if (unsyncedFiles.length === 0) {
230
+ return files;
231
+ }
219
232
  const userTimezone = (yield client.usersApi.getAuthenticatedUser()).data.timezone;
220
- yield (0, storage_1.getStorage)().updateUnsyncedFiles({
221
- integrationId,
222
- crowdinId,
223
- files: JSON.stringify(unsyncedFiles),
224
- });
225
- files = files.map((file) => {
226
- const unsynced = unsyncedFiles.find((unsyncedFile) => unsyncedFile.id === file.id);
233
+ return files.map((file) => {
234
+ const unsynced = unsyncedFiles.find((f) => `${f.id}` === `${file.id}`);
227
235
  if (unsynced && isFileLeaf(file)) {
228
236
  const formattedDate = (0, util_1.getFormattedDate)({ date: new Date(+unsynced.updatedAt), userTimezone });
229
237
  file.labels = [
@@ -238,7 +246,6 @@ function markUnsyncedFiles({ integrationId, crowdinId, client, files, }) {
238
246
  }
239
247
  return file;
240
248
  });
241
- return files;
242
249
  });
243
250
  }
244
251
  exports.markUnsyncedFiles = markUnsyncedFiles;
@@ -260,13 +260,9 @@ function reRunInProgressJobs(config) {
260
260
  const context = {
261
261
  jwtPayload: {
262
262
  context: {
263
- // eslint-disable-next-line @typescript-eslint/camelcase
264
263
  project_id: projectId,
265
- // eslint-disable-next-line @typescript-eslint/camelcase
266
264
  organization_id: crowdinCredentials.organizationId,
267
- // eslint-disable-next-line @typescript-eslint/camelcase
268
265
  organization_domain: crowdinCredentials.domain,
269
- // eslint-disable-next-line @typescript-eslint/camelcase
270
266
  user_id: crowdinCredentials.userId,
271
267
  },
272
268
  },
@@ -65,11 +65,7 @@ function getIntegrationSnapshot(integration, integrationCredentials, integration
65
65
  files = (0, files_1.skipFilesByRegex)(files, integration.skipIntegrationNodes);
66
66
  }
67
67
  // trick for compatibility in requests and set files
68
- files = files.map((file) => (Object.assign(Object.assign({}, file), { parentId: file.parent_id || file.parentId,
69
- // eslint-disable-next-line @typescript-eslint/camelcase
70
- parent_id: file.parent_id || file.parentId,
71
- // eslint-disable-next-line @typescript-eslint/camelcase
72
- node_type: file.nodeType || file.node_type })));
68
+ files = files.map((file) => (Object.assign(Object.assign({}, file), { parentId: file.parent_id || file.parentId, parent_id: file.parent_id || file.parentId, node_type: file.nodeType || file.node_type })));
73
69
  return files;
74
70
  });
75
71
  }
@@ -266,11 +266,8 @@ function prepareWebhookData({ config, integration, provider, webhookUrlParam, })
266
266
  }
267
267
  const context = Object.assign({ jwtPayload: {
268
268
  context: {
269
- // eslint-disable-next-line @typescript-eslint/camelcase
270
269
  project_id: projectId,
271
- // eslint-disable-next-line @typescript-eslint/camelcase
272
270
  organization_id: crowdinCredentials === null || crowdinCredentials === void 0 ? void 0 : crowdinCredentials.organizationId,
273
- // eslint-disable-next-line @typescript-eslint/camelcase
274
271
  user_id: crowdinCredentials === null || crowdinCredentials === void 0 ? void 0 : crowdinCredentials.userId,
275
272
  },
276
273
  }, crowdinId: crowdinCredentials.id }, ((integrationCredentials === null || integrationCredentials === void 0 ? void 0 : integrationCredentials.id) && { clientId: integrationCredentials.id }));
@@ -325,11 +322,7 @@ function updateCrowdinFromWebhookRequest(args) {
325
322
  if (fileNodeType !== '1') {
326
323
  continue;
327
324
  }
328
- const initFile = Object.assign({ id: file.id, name: file.name, sync: false, schedule: true,
329
- // eslint-disable-next-line @typescript-eslint/camelcase
330
- parent_id: file.parent_id || file.parentId,
331
- // eslint-disable-next-line @typescript-eslint/camelcase
332
- node_type: file.nodeType || file.node_type || '1' }, (file.type ? { type: file.type } : {}));
325
+ const initFile = Object.assign({ id: file.id, name: file.name, sync: false, schedule: true, parent_id: file.parent_id || file.parentId, node_type: file.nodeType || file.node_type || '1' }, (file.type ? { type: file.type } : {}));
333
326
  if (syncSettings) {
334
327
  if (!syncFiles.find((obj) => obj.id === initFile.id)) {
335
328
  yield (0, storage_1.getStorage)().updateSyncSettings(JSON.stringify([...syncFiles, initFile]), syncSettings.integrationId, syncSettings.crowdinId, 'schedule', syncSettings.provider);
@@ -102,9 +102,11 @@ function handle(config) {
102
102
  // prevent possible overrides of the other modules
103
103
  config.customMT = Object.assign(Object.assign({}, config.customMT), { key: config.identifier + '-mt' });
104
104
  modules['custom-mt'] = [
105
- Object.assign(Object.assign(Object.assign({ key: config.customMT.key, name: config.name, logo: (0, util_1.getLogoUrl)(config.customMT, '/mt'), url: '/mt/translate', withContext: !!config.customMT.withContext }, (config.customMT.batchSize !== null && {
105
+ Object.assign(Object.assign(Object.assign(Object.assign({ key: config.customMT.key, name: config.name, logo: (0, util_1.getLogoUrl)(config.customMT, '/mt'), url: '/mt/translate', withContext: !!config.customMT.withContext }, ((0, util_1.isDefined)(config.customMT.splitStringsIntoChunks) && {
106
+ splitStringsIntoChunks: config.customMT.splitStringsIntoChunks,
107
+ })), ((0, util_1.isDefined)(config.customMT.batchSize) && {
106
108
  batchSize: config.customMT.batchSize,
107
- })), (config.customMT.maskEntities !== null && {
109
+ })), ((0, util_1.isDefined)(config.customMT.maskEntities) && {
108
110
  maskEntities: config.customMT.maskEntities,
109
111
  })), (!!config.customMT.environments && {
110
112
  environments: normalizeEnvironments(config.customMT.environments),
@@ -47,7 +47,7 @@ function handle(config) {
47
47
  let status = 'ok';
48
48
  let message = 'Filesystem access successful';
49
49
  try {
50
- const testFile = path_1.default.join(os_1.default.tmpdir(), 'status-check.txt');
50
+ const testFile = path_1.default.join(os_1.default.tmpdir(), `status-check-${Date.now()}.txt`);
51
51
  yield promises_1.default.writeFile(testFile, 'test');
52
52
  yield promises_1.default.unlink(testFile);
53
53
  }
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- /* eslint-disable @typescript-eslint/camelcase */
3
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
3
  if (k2 === undefined) k2 = k;
5
4
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -1,8 +1,5 @@
1
1
  "use strict";
2
2
  /* eslint-disable no-unused-expressions */
3
- /* eslint-disable @typescript-eslint/no-var-requires */
4
- /* eslint-disable @typescript-eslint/ban-ts-ignore */
5
- /* eslint-disable @typescript-eslint/camelcase */
6
3
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
7
4
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
8
5
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -1,6 +1,5 @@
1
1
  "use strict";
2
2
  /* eslint-disable no-unused-expressions */
3
- /* eslint-disable @typescript-eslint/camelcase */
4
3
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
5
4
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
6
5
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -1,8 +1,5 @@
1
1
  "use strict";
2
- /* eslint-disable @typescript-eslint/camelcase */
3
- /* eslint-disable @typescript-eslint/ban-ts-ignore */
4
2
  /* eslint-disable no-unused-expressions */
5
- /* eslint-disable @typescript-eslint/no-var-requires */
6
3
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
7
4
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
8
5
  return new (P || (P = Promise))(function (resolve, reject) {
package/out/util/axios.js CHANGED
@@ -19,7 +19,6 @@ class AxiosProvider {
19
19
  }
20
20
  configureRequest() {
21
21
  this.axios.interceptors.request.use((config) => {
22
- // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
23
22
  return new Promise((resolve) => {
24
23
  const interval = setInterval(() => {
25
24
  if (this.pendingRequests < AxiosProvider.CROWDIN_API_MAX_CONCURRENT_REQUESTS) {
@@ -11,6 +11,7 @@ export declare function executeWithRetry<T>(func: () => Promise<T>, numOfRetries
11
11
  export declare function getLogoUrl(moduleConfig?: ImagePath, modulePath?: string): string;
12
12
  export declare function isAuthorizedConfig(config: Config | UnauthorizedConfig): config is Config;
13
13
  export declare function isJson(string: string): boolean;
14
+ export declare function isDefined(value: any): boolean;
14
15
  export declare function getPreviousDate(days: number): Date;
15
16
  export declare function prepareFormDataMetadataId(req: CrowdinClientRequest, config: Config): Promise<string>;
16
17
  export declare function validateEmail(email: string | number): boolean;
package/out/util/index.js CHANGED
@@ -32,7 +32,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
32
32
  });
33
33
  };
34
34
  Object.defineProperty(exports, "__esModule", { value: true });
35
- exports.getFormattedDate = exports.validateEmail = exports.prepareFormDataMetadataId = exports.getPreviousDate = exports.isJson = exports.isAuthorizedConfig = exports.getLogoUrl = exports.executeWithRetry = exports.decryptData = exports.encryptData = exports.runAsyncWrapper = exports.CodeError = void 0;
35
+ exports.getFormattedDate = exports.validateEmail = exports.prepareFormDataMetadataId = exports.getPreviousDate = exports.isDefined = exports.isJson = exports.isAuthorizedConfig = exports.getLogoUrl = exports.executeWithRetry = exports.decryptData = exports.encryptData = exports.runAsyncWrapper = exports.CodeError = void 0;
36
36
  const crypto = __importStar(require("crypto-js"));
37
37
  const storage_1 = require("../storage");
38
38
  const types_1 = require("../types");
@@ -129,6 +129,10 @@ function isJson(string) {
129
129
  return true;
130
130
  }
131
131
  exports.isJson = isJson;
132
+ function isDefined(value) {
133
+ return value !== undefined && value !== null;
134
+ }
135
+ exports.isDefined = isDefined;
132
136
  function getPreviousDate(days) {
133
137
  const date = new Date();
134
138
  date.setDate(date.getDate() - days);
@@ -73,9 +73,7 @@ function log(message, context) {
73
73
  project: {
74
74
  id: context.jwtPayload.context.project_id,
75
75
  identifier: (_a = context.jwtPayload.context.project_identifier) !== null && _a !== void 0 ? _a : '',
76
- // eslint-disable-next-line @typescript-eslint/camelcase
77
76
  organization_id: context.jwtPayload.context.organization_id,
78
- // eslint-disable-next-line @typescript-eslint/camelcase
79
77
  user_id: context.jwtPayload.context.user_id,
80
78
  },
81
79
  user: {
@@ -108,9 +106,7 @@ function logError(e, context) {
108
106
  project: {
109
107
  id: context.jwtPayload.context.project_id,
110
108
  identifier: (_a = context.jwtPayload.context.project_identifier) !== null && _a !== void 0 ? _a : '',
111
- // eslint-disable-next-line @typescript-eslint/camelcase
112
109
  organization_id: context.jwtPayload.context.organization_id,
113
- // eslint-disable-next-line @typescript-eslint/camelcase
114
110
  user_id: context.jwtPayload.context.user_id,
115
111
  },
116
112
  user: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdin/app-project-module",
3
- "version": "0.105.1",
3
+ "version": "0.107.0",
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",
@@ -50,6 +50,8 @@
50
50
  "@babel/preset-react": "^7.26.3",
51
51
  "@emotion/react": "^11.14.0",
52
52
  "@emotion/styled": "^11.14.0",
53
+ "@eslint/eslintrc": "^3.3.3",
54
+ "@eslint/js": "^9.39.2",
53
55
  "@monaco-editor/react": "^4.7.0",
54
56
  "@mui/icons-material": "^5.16.14",
55
57
  "@mui/material": "^5.16.14",
@@ -79,15 +81,15 @@
79
81
  "@types/pg": "^8.11.11",
80
82
  "@types/swagger-jsdoc": "^6.0.4",
81
83
  "@types/uuid": "^9.0.7",
82
- "@typescript-eslint/eslint-plugin": "^2.3.1",
83
- "@typescript-eslint/parser": "^2.3.1",
84
+ "@typescript-eslint/eslint-plugin": "^8.49.0",
85
+ "@typescript-eslint/parser": "^8.49.0",
84
86
  "auto-changelog": "^2.5.0",
85
- "eslint": "^6.4.0",
86
- "eslint-config-prettier": "^6.3.0",
87
- "eslint-plugin-prettier": "^3.1.1",
87
+ "eslint": "^9.39.2",
88
+ "eslint-config-prettier": "^10.1.8",
89
+ "eslint-plugin-prettier": "^5.5.4",
88
90
  "jest": "^29.7.0",
89
91
  "jest-junit": "^15.0.0",
90
- "prettier": "^2.8.8",
92
+ "prettier": "^3.7.4",
91
93
  "react": "^18.3.1",
92
94
  "react-dom": "^18.3.1",
93
95
  "rollup": "^3.29.4",