@crowdin/app-project-module 0.98.0-cf-3 → 0.99.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 (142) hide show
  1. package/out/index.js +12 -28
  2. package/out/middlewares/crowdin-client.d.ts +1 -1
  3. package/out/middlewares/integration-credentials.d.ts +1 -1
  4. package/out/middlewares/integration-credentials.js +1 -4
  5. package/out/middlewares/render-ui-module.d.ts +3 -3
  6. package/out/middlewares/render-ui-module.js +13 -9
  7. package/out/middlewares/ui-module.d.ts +1 -1
  8. package/out/middlewares/ui-module.js +1 -4
  9. package/out/modules/about.d.ts +1 -1
  10. package/out/modules/about.js +2 -8
  11. package/out/modules/ai-prompt-provider/handlers/compile.d.ts +1 -1
  12. package/out/modules/ai-prompt-provider/index.js +2 -4
  13. package/out/modules/ai-provider/handlers/chat-completions.d.ts +1 -1
  14. package/out/modules/ai-provider/handlers/get-model-list.d.ts +1 -1
  15. package/out/modules/ai-provider/index.js +2 -4
  16. package/out/modules/ai-request-processors/handler.d.ts +1 -1
  17. package/out/modules/ai-tools/handlers/tool-calls.d.ts +1 -1
  18. package/out/modules/ai-tools/index.js +1 -1
  19. package/out/modules/api/api.js +87 -2
  20. package/out/modules/context-menu/index.js +2 -2
  21. package/out/modules/custom-mt/handlers/translate.d.ts +1 -1
  22. package/out/modules/custom-mt/index.js +1 -3
  23. package/out/modules/custom-spell-check/handlers/get-languages-list.d.ts +1 -1
  24. package/out/modules/custom-spell-check/handlers/spell-check.d.ts +1 -1
  25. package/out/modules/custom-spell-check/index.js +4 -4
  26. package/out/modules/editor-right-panel/index.js +1 -1
  27. package/out/modules/external-qa-check/handlers/validate.d.ts +1 -1
  28. package/out/modules/external-qa-check/index.js +2 -2
  29. package/out/modules/file-processing/handlers/custom-file-format.d.ts +1 -1
  30. package/out/modules/file-processing/handlers/file-download.d.ts +1 -1
  31. package/out/modules/file-processing/handlers/pre-post-process.d.ts +1 -1
  32. package/out/modules/form-data-display.d.ts +1 -1
  33. package/out/modules/form-data-save.d.ts +1 -1
  34. package/out/modules/install.d.ts +1 -1
  35. package/out/modules/integration/handlers/crowdin-file-progress.d.ts +1 -1
  36. package/out/modules/integration/handlers/crowdin-files.d.ts +1 -1
  37. package/out/modules/integration/handlers/crowdin-files.js +6 -4
  38. package/out/modules/integration/handlers/crowdin-project.d.ts +1 -1
  39. package/out/modules/integration/handlers/crowdin-update.d.ts +1 -1
  40. package/out/modules/integration/handlers/crowdin-update.js +18 -4
  41. package/out/modules/integration/handlers/crowdin-webhook.d.ts +1 -1
  42. package/out/modules/integration/handlers/integration-data.d.ts +1 -1
  43. package/out/modules/integration/handlers/integration-data.js +17 -13
  44. package/out/modules/integration/handlers/integration-login.d.ts +1 -1
  45. package/out/modules/integration/handlers/integration-logout.d.ts +1 -1
  46. package/out/modules/integration/handlers/integration-update.d.ts +1 -1
  47. package/out/modules/integration/handlers/integration-webhook.d.ts +1 -1
  48. package/out/modules/integration/handlers/invite-users.d.ts +1 -1
  49. package/out/modules/integration/handlers/job-cancel.d.ts +1 -1
  50. package/out/modules/integration/handlers/job-info-deprecated.d.ts +4 -0
  51. package/out/modules/integration/handlers/job-info-deprecated.js +138 -0
  52. package/out/modules/integration/handlers/job-info.d.ts +1 -2
  53. package/out/modules/integration/handlers/job-info.js +20 -75
  54. package/out/modules/integration/handlers/job-list.d.ts +3 -0
  55. package/out/modules/integration/handlers/job-list.js +46 -0
  56. package/out/modules/integration/handlers/main.d.ts +1 -1
  57. package/out/modules/integration/handlers/main.js +3 -13
  58. package/out/modules/integration/handlers/oauth-login.d.ts +1 -1
  59. package/out/modules/integration/handlers/oauth-login.js +2 -10
  60. package/out/modules/integration/handlers/oauth-polling.d.ts +1 -1
  61. package/out/modules/integration/handlers/oauth-url.d.ts +1 -1
  62. package/out/modules/integration/handlers/settings-save.d.ts +1 -1
  63. package/out/modules/integration/handlers/settings.d.ts +1 -1
  64. package/out/modules/integration/handlers/sync-settings-save.d.ts +1 -1
  65. package/out/modules/integration/handlers/sync-settings.d.ts +1 -1
  66. package/out/modules/integration/handlers/user-errors.d.ts +1 -1
  67. package/out/modules/integration/handlers/users.d.ts +1 -1
  68. package/out/modules/integration/index.d.ts +1 -3
  69. package/out/modules/integration/index.js +37 -38
  70. package/out/modules/integration/types.d.ts +11 -1
  71. package/out/modules/integration/util/defaults.js +51 -45
  72. package/out/modules/integration/util/files.d.ts +1 -0
  73. package/out/modules/integration/util/files.js +52 -4
  74. package/out/modules/integration/util/job.d.ts +1 -1
  75. package/out/modules/integration/util/job.js +3 -2
  76. package/out/modules/integration/util/types.d.ts +8 -0
  77. package/out/modules/modal/index.js +2 -2
  78. package/out/modules/organization-menu/index.js +4 -6
  79. package/out/modules/organization-settings-menu/index.js +4 -6
  80. package/out/modules/profile-resources-menu/index.js +4 -6
  81. package/out/modules/profile-settings-menu/index.js +4 -6
  82. package/out/modules/project-menu/index.js +1 -1
  83. package/out/modules/project-menu-crowdsource/index.js +1 -1
  84. package/out/modules/project-reports/index.js +2 -4
  85. package/out/modules/project-tools/index.js +2 -4
  86. package/out/modules/status.d.ts +1 -1
  87. package/out/modules/subscription-paid.d.ts +1 -1
  88. package/out/modules/uninstall.d.ts +1 -1
  89. package/out/modules/webhooks/handlers/webhook-handler.d.ts +1 -1
  90. package/out/modules/workflow-step-type/handlers/delete-step.d.ts +1 -1
  91. package/out/modules/workflow-step-type/handlers/step-settings-save.d.ts +1 -1
  92. package/out/modules/workflow-step-type/index.js +2 -3
  93. package/out/storage/index.d.ts +2 -1
  94. package/out/storage/index.js +1 -8
  95. package/out/storage/mysql.d.ts +3 -2
  96. package/out/storage/mysql.js +42 -1
  97. package/out/storage/postgre.d.ts +3 -2
  98. package/out/storage/postgre.js +43 -4
  99. package/out/storage/sqlite.d.ts +3 -2
  100. package/out/storage/sqlite.js +38 -4
  101. package/out/types.d.ts +1 -18
  102. package/out/util/credentials-masker.d.ts +1 -1
  103. package/out/util/handlebars.d.ts +1 -0
  104. package/out/util/handlebars.js +46 -0
  105. package/out/util/index.d.ts +0 -2
  106. package/out/util/index.js +2 -14
  107. package/out/views/about.handlebars +102 -0
  108. package/out/views/error.handlebars +54 -0
  109. package/out/views/form.handlebars +30 -0
  110. package/out/views/install.handlebars +16 -0
  111. package/out/views/login.handlebars +331 -0
  112. package/out/views/main.handlebars +1901 -0
  113. package/out/views/oauth.handlebars +11 -0
  114. package/out/views/partials/head.handlebars +53 -0
  115. package/out/views/subscription.handlebars +26 -0
  116. package/package.json +11 -12
  117. package/out/storage/d1.d.ts +0 -99
  118. package/out/storage/d1.js +0 -769
  119. package/out/util/jsx-renderer.d.ts +0 -6
  120. package/out/util/jsx-renderer.js +0 -13
  121. package/out/util/static-files.d.ts +0 -19
  122. package/out/util/static-files.js +0 -82
  123. package/out/views/AboutPage.d.ts +0 -9
  124. package/out/views/AboutPage.js +0 -79
  125. package/out/views/ErrorPage.d.ts +0 -18
  126. package/out/views/ErrorPage.js +0 -56
  127. package/out/views/FormPage.d.ts +0 -13
  128. package/out/views/FormPage.js +0 -27
  129. package/out/views/InstallPage.d.ts +0 -10
  130. package/out/views/InstallPage.js +0 -22
  131. package/out/views/LoginPage.d.ts +0 -33
  132. package/out/views/LoginPage.js +0 -199
  133. package/out/views/MainPage.d.ts +0 -79
  134. package/out/views/MainPage.js +0 -1613
  135. package/out/views/OAuthPage.d.ts +0 -7
  136. package/out/views/OAuthPage.js +0 -17
  137. package/out/views/SubscriptionPage.d.ts +0 -7
  138. package/out/views/SubscriptionPage.js +0 -26
  139. package/out/views/index.d.ts +0 -13
  140. package/out/views/index.js +0 -25
  141. package/out/views/layout/Head.d.ts +0 -9
  142. package/out/views/layout/Head.js +0 -54
@@ -14,7 +14,6 @@ const logger_1 = require("../../../util/logger");
14
14
  const subscription_1 = require("../../../util/subscription");
15
15
  const defaults_1 = require("../util/defaults");
16
16
  const users_1 = require("./users");
17
- const views_1 = require("../../../views");
18
17
  function handle(config, integration) {
19
18
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
20
19
  var _a, _b, _c, _d;
@@ -78,9 +77,11 @@ function handle(config, integration) {
78
77
  : {};
79
78
  options.integrationOneLevelFetching = integration.integrationOneLevelFetching;
80
79
  options.integrationSearchListener = integration.integrationSearchListener;
80
+ options.progressiveCrowdinFilesLoading = integration.progressiveCrowdinFilesLoading;
81
81
  options.checkSubscription = !(0, subscription_1.isAppFree)(config);
82
82
  options.uploadTranslations = integration.uploadTranslations;
83
83
  options.forcePushTranslations = integration.forcePushTranslations;
84
+ options.forcePushSources = integration.forcePushSources;
84
85
  options.excludedTargetLanguages = integration.excludedTargetLanguages;
85
86
  options.sentryData = process.env.SENTRY_DSN
86
87
  ? {
@@ -93,18 +94,7 @@ function handle(config, integration) {
93
94
  checkInterval: ((_d = integration.asyncProgress) === null || _d === void 0 ? void 0 : _d.checkInterval) || 1000,
94
95
  };
95
96
  logger(`Routing user to ${view} view`);
96
- let html;
97
- if (view === 'install') {
98
- html = (0, util_1.renderJSX)(views_1.InstallPage, options);
99
- }
100
- else if (view === 'login') {
101
- html = (0, util_1.renderJSX)(views_1.LoginPage, options);
102
- }
103
- else {
104
- html = (0, util_1.renderJSX)(views_1.MainPage, options);
105
- }
106
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
107
- return res.send(html);
97
+ return res.render(view, options);
108
98
  }));
109
99
  }
110
100
  exports.default = handle;
@@ -2,4 +2,4 @@
2
2
  import { Request, Response } from 'express';
3
3
  import { Config } from '../../../types';
4
4
  import { IntegrationLogic } from '../types';
5
- export default function handle(config: Config, integration: IntegrationLogic): (req: Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>> | import("../../../types").CrowdinClientRequest, res: Response<any, Record<string, any>>, next: Function) => void;
5
+ export default function handle(config: Config, integration: IntegrationLogic): (req: import("../../../types").CrowdinClientRequest | Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
@@ -18,7 +18,6 @@ const defaults_1 = require("../util/defaults");
18
18
  const logger_1 = require("../../../util/logger");
19
19
  const storage_1 = require("../../../storage");
20
20
  const crowdin_apps_functions_1 = require("@crowdin/crowdin-apps-functions");
21
- const views_1 = require("../../../views");
22
21
  function handle(config, integration) {
23
22
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
24
23
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
@@ -66,19 +65,12 @@ function handle(config, integration) {
66
65
  yield (0, storage_1.getStorage)().deleteMetadata((0, defaults_1.getOAuthPollingId)(clientId));
67
66
  yield (0, storage_1.getStorage)().saveMetadata((0, defaults_1.getOAuthPollingId)(clientId), oauthCredentials, organization);
68
67
  }
69
- const html = (0, util_1.renderJSX)(views_1.OAuthPage, {
70
- message: JSON.stringify(message),
71
- oauthMode: (_p = integration.oauthLogin) === null || _p === void 0 ? void 0 : _p.mode,
72
- });
73
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
74
- return res.send(html);
68
+ return res.render('oauth', { message: JSON.stringify(message), oauthMode: (_p = integration.oauthLogin) === null || _p === void 0 ? void 0 : _p.mode });
75
69
  }
76
70
  catch (e) {
77
71
  (0, logger_1.logError)(e);
78
72
  message.data = { error: (0, logger_1.getErrorMessage)(e) };
79
- const html = (0, util_1.renderJSX)(views_1.OAuthPage, { message: JSON.stringify(message) });
80
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
81
- return res.send(html);
73
+ return res.render('oauth', { message: JSON.stringify(message) });
82
74
  }
83
75
  }));
84
76
  }
@@ -2,4 +2,4 @@
2
2
  import { Response } from 'express';
3
3
  import { CrowdinClientRequest } from '../../../types';
4
4
  import { IntegrationLogic } from '../types';
5
- export default function handle(integration: IntegrationLogic): (req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>> | CrowdinClientRequest, res: Response<any, Record<string, any>>, next: Function) => void;
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;
@@ -2,4 +2,4 @@
2
2
  import { Response } from 'express';
3
3
  import { Config, CrowdinClientRequest } from '../../../types';
4
4
  import { IntegrationLogic } from '../types';
5
- export default function handle(config: Config, integration: IntegrationLogic): (req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>> | CrowdinClientRequest, 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;
@@ -2,4 +2,4 @@
2
2
  import { Response } from 'express';
3
3
  import { Config } from '../../../types';
4
4
  import { IntegrationLogic } from '../types';
5
- export default function handle(config: Config, integration: IntegrationLogic): (req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>> | import("../../../types").CrowdinClientRequest, res: Response<any, Record<string, any>>, next: Function) => void;
5
+ export default function handle(config: Config, integration: IntegrationLogic): (req: import("../../../types").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;
@@ -1,3 +1,3 @@
1
1
  /// <reference types="qs" />
2
2
  import { Response } from 'express';
3
- export default function handle(): (req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>> | import("../../../types").CrowdinClientRequest, res: Response<any, Record<string, any>>, next: Function) => void;
3
+ export default function handle(): (req: import("../../../types").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;
@@ -2,4 +2,4 @@
2
2
  import { Response } from 'express';
3
3
  import { Config } from '../../../types';
4
4
  import { IntegrationLogic } from '../types';
5
- export default function handle(config: Config, integration: IntegrationLogic): (req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>> | import("../../../types").CrowdinClientRequest, res: Response<any, Record<string, any>>, next: Function) => void;
5
+ export default function handle(config: Config, integration: IntegrationLogic): (req: import("../../../types").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;
@@ -1,3 +1,3 @@
1
1
  /// <reference types="qs" />
2
2
  import { Response } from 'express';
3
- export default function handle(): (req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>> | import("../../../types").CrowdinClientRequest, res: Response<any, Record<string, any>>, next: Function) => void;
3
+ export default function handle(): (req: import("../../../types").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;
@@ -1,3 +1,3 @@
1
1
  /// <reference types="qs" />
2
2
  import { Response } from 'express';
3
- export default function handle(): (req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>> | import("../../../types").CrowdinClientRequest, res: Response<any, Record<string, any>>, next: Function) => void;
3
+ export default function handle(): (req: import("../../../types").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;
@@ -2,7 +2,7 @@
2
2
  import Crowdin, { UsersModel } from '@crowdin/crowdin-api-client';
3
3
  import { Response } from 'express';
4
4
  export type ProjectMember = UsersModel.ProjectMember | UsersModel.EnterpriseProjectMember;
5
- export default function handle(): (req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>> | import("../../../types").CrowdinClientRequest, res: Response<any, Record<string, any>>, next: Function) => void;
5
+ export default function handle(): (req: import("../../../types").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;
6
6
  export declare function isManager({ client, projectId, memberId, }: {
7
7
  client: Crowdin;
8
8
  projectId: number;
@@ -3,6 +3,4 @@ import { Config } from '../../types';
3
3
  export declare function register({ config, app }: {
4
4
  config: Config;
5
5
  app: Express;
6
- }): {
7
- cronExecutions: {};
8
- };
6
+ }): void;
@@ -36,11 +36,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  };
37
37
  Object.defineProperty(exports, "__esModule", { value: true });
38
38
  exports.register = void 0;
39
+ const cron = __importStar(require("node-cron"));
39
40
  const crowdin_client_1 = __importDefault(require("../../middlewares/crowdin-client"));
40
41
  const integration_credentials_1 = __importDefault(require("../../middlewares/integration-credentials"));
41
42
  const json_response_1 = __importDefault(require("../../middlewares/json-response"));
42
43
  const util_1 = require("../../util");
43
- const static_files_1 = require("../../util/static-files");
44
44
  const defaults_1 = require("./util/defaults");
45
45
  const webhooks_1 = require("./util/webhooks");
46
46
  const crowdin_file_progress_1 = __importDefault(require("./handlers/crowdin-file-progress"));
@@ -54,7 +54,9 @@ const integration_logout_1 = __importDefault(require("./handlers/integration-log
54
54
  const integration_update_1 = __importDefault(require("./handlers/integration-update"));
55
55
  const integration_webhook_1 = __importDefault(require("./handlers/integration-webhook"));
56
56
  const job_cancel_1 = __importDefault(require("./handlers/job-cancel"));
57
+ const job_info_deprecated_1 = __importDefault(require("./handlers/job-info-deprecated"));
57
58
  const job_info_1 = __importDefault(require("./handlers/job-info"));
59
+ const job_list_1 = __importDefault(require("./handlers/job-list"));
58
60
  const main_1 = __importDefault(require("./handlers/main"));
59
61
  const oauth_login_1 = __importDefault(require("./handlers/oauth-login"));
60
62
  const oauth_url_1 = __importDefault(require("./handlers/oauth-url"));
@@ -70,13 +72,12 @@ const cron_1 = require("./util/cron");
70
72
  const storage_1 = require("../../storage");
71
73
  function register({ config, app }) {
72
74
  var _a, _b, _c;
73
- let cronExecutions = {};
74
75
  const integrationLogic = config.projectIntegration;
75
76
  if (!integrationLogic) {
76
- return { cronExecutions };
77
+ return;
77
78
  }
78
79
  (0, defaults_1.applyIntegrationModuleDefaults)(config, integrationLogic);
79
- app.get((0, util_1.getLogoUrl)(integrationLogic, '/integration'), (0, static_files_1.serveFile)(config, integrationLogic.imagePath || config.imagePath));
80
+ app.get((0, util_1.getLogoUrl)(integrationLogic, '/integration'), (req, res) => res.sendFile(integrationLogic.imagePath || config.imagePath));
80
81
  app.get('/', (0, crowdin_client_1.default)({
81
82
  config,
82
83
  optional: true,
@@ -89,12 +90,24 @@ function register({ config, app }) {
89
90
  checkSubscriptionExpiration: true,
90
91
  moduleKey: integrationLogic.key,
91
92
  }), (0, subscription_info_1.default)(config));
93
+ app.get('/api/all-jobs', json_response_1.default, (0, crowdin_client_1.default)({
94
+ config,
95
+ optional: false,
96
+ checkSubscriptionExpiration: true,
97
+ moduleKey: integrationLogic.key,
98
+ }), (0, job_list_1.default)());
99
+ app.get('/api/job-info', json_response_1.default, (0, crowdin_client_1.default)({
100
+ config,
101
+ optional: false,
102
+ checkSubscriptionExpiration: true,
103
+ moduleKey: integrationLogic.key,
104
+ }), (0, job_info_1.default)());
92
105
  app.get('/api/jobs', json_response_1.default, (0, crowdin_client_1.default)({
93
106
  config,
94
107
  optional: false,
95
108
  checkSubscriptionExpiration: true,
96
109
  moduleKey: integrationLogic.key,
97
- }), (0, job_info_1.default)(config));
110
+ }), (0, job_info_deprecated_1.default)(config));
98
111
  app.delete('/api/jobs', json_response_1.default, (0, crowdin_client_1.default)({
99
112
  config,
100
113
  optional: false,
@@ -184,39 +197,25 @@ function register({ config, app }) {
184
197
  }), (0, oauth_polling_1.default)(integrationLogic));
185
198
  }
186
199
  }
187
- Promise.resolve().then(() => __importStar(require('node-cron'))).then((nodeCron) => {
188
- const cron = nodeCron.default || nodeCron;
189
- if (integrationLogic.cronJobs) {
190
- integrationLogic.cronJobs.forEach((job) => {
191
- cron.schedule(job.expression, () => (0, cron_1.runJob)({ config, integration: integrationLogic, job }).catch(console.error));
192
- });
193
- }
194
- if (integrationLogic.withCronSync) {
195
- cron.schedule('0 * * * *', () => (0, cron_1.filesCron)({ config, integration: integrationLogic, period: '1' }).catch(console.error));
196
- cron.schedule('0 */3 * * *', () => (0, cron_1.filesCron)({ config, integration: integrationLogic, period: '3' }).catch(console.error));
197
- cron.schedule('0 */6 * * *', () => (0, cron_1.filesCron)({ config, integration: integrationLogic, period: '6' }).catch(console.error));
198
- cron.schedule('0 */12 * * *', () => (0, cron_1.filesCron)({ config, integration: integrationLogic, period: '12' }).catch(console.error));
199
- cron.schedule('0 0 * * *', () => (0, cron_1.filesCron)({ config, integration: integrationLogic, period: '24' }).catch(console.error));
200
+ if (integrationLogic.cronJobs) {
201
+ integrationLogic.cronJobs.forEach((job) => {
202
+ cron.schedule(job.expression, () => (0, cron_1.runJob)({ config, integration: integrationLogic, job }).catch(console.error));
203
+ });
204
+ }
205
+ if (integrationLogic.withCronSync) {
206
+ cron.schedule('0 * * * *', () => (0, cron_1.filesCron)({ config, integration: integrationLogic, period: '1' }).catch(console.error));
207
+ cron.schedule('0 */3 * * *', () => (0, cron_1.filesCron)({ config, integration: integrationLogic, period: '3' }).catch(console.error));
208
+ cron.schedule('0 */6 * * *', () => (0, cron_1.filesCron)({ config, integration: integrationLogic, period: '6' }).catch(console.error));
209
+ cron.schedule('0 */12 * * *', () => (0, cron_1.filesCron)({ config, integration: integrationLogic, period: '12' }).catch(console.error));
210
+ cron.schedule('0 0 * * *', () => (0, cron_1.filesCron)({ config, integration: integrationLogic, period: '24' }).catch(console.error));
211
+ }
212
+ // remove user errors
213
+ cron.schedule('0 0 * * *', () => __awaiter(this, void 0, void 0, function* () {
214
+ if (integrationLogic.userErrorLifetimeDays) {
215
+ const date = (0, util_1.getPreviousDate)(integrationLogic.userErrorLifetimeDays);
216
+ yield (0, storage_1.getStorage)().deleteAllUsersErrorsOlderThan(`${date.getTime()}`);
200
217
  }
201
- // remove user errors
202
- cron.schedule('0 0 * * *', () => __awaiter(this, void 0, void 0, function* () {
203
- if (integrationLogic.userErrorLifetimeDays) {
204
- const date = (0, util_1.getPreviousDate)(integrationLogic.userErrorLifetimeDays);
205
- yield (0, storage_1.getStorage)().deleteAllUsersErrorsOlderThan(`${date.getTime()}`);
206
- }
207
- }));
208
- cron.schedule('0 0 1 * *', () => (0, cron_1.removeFinishedJobs)());
209
- })
210
- .catch(() => {
211
- // node-cron not available (e.g., Cloudflare Workers), delegating to external cron handler
212
- cronExecutions = {
213
- '0 * * * *': (0, cron_1.filesCron)({ config, integration: integrationLogic, period: '1' }).catch(console.error),
214
- '0 */3 * * *': (0, cron_1.filesCron)({ config, integration: integrationLogic, period: '3' }).catch(console.error),
215
- '0 */6 * * *': (0, cron_1.filesCron)({ config, integration: integrationLogic, period: '6' }).catch(console.error),
216
- '0 */12 * * *': (0, cron_1.filesCron)({ config, integration: integrationLogic, period: '12' }).catch(console.error),
217
- '0 0 * * *': (0, cron_1.filesCron)({ config, integration: integrationLogic, period: '24' }).catch(console.error),
218
- };
219
- });
218
+ }));
220
219
  if (integrationLogic.webhooks) {
221
220
  app.post(`${integrationLogic.webhooks.crowdinWebhookUrl
222
221
  ? integrationLogic.webhooks.crowdinWebhookUrl
@@ -253,6 +252,6 @@ function register({ config, app }) {
253
252
  checkSubscriptionExpiration: true,
254
253
  moduleKey: integrationLogic.key,
255
254
  }), (0, integration_credentials_1.default)(config, integrationLogic), (0, invite_users_1.default)());
256
- return { cronExecutions };
255
+ cron.schedule('0 0 1 * *', () => (0, cron_1.removeFinishedJobs)());
257
256
  }
258
257
  exports.register = register;
@@ -36,7 +36,7 @@ export interface IntegrationLogic extends ModuleKey {
36
36
  /**
37
37
  * function to get crowdin files that are related with this integration
38
38
  */
39
- getCrowdinFiles?: (projectId: number, client: Crowdin, appRootFolder?: SourceFilesModel.Directory, config?: any) => Promise<TreeItem[]>;
39
+ getCrowdinFiles?: (projectId: number, client: Crowdin, appRootFolder?: SourceFilesModel.Directory, config?: any, mode?: CrowdinFilesLoadMode) => Promise<TreeItem[]>;
40
40
  /**
41
41
  * function to get data from integration
42
42
  */
@@ -160,6 +160,10 @@ export interface IntegrationLogic extends ModuleKey {
160
160
  * Enable integration next page event
161
161
  */
162
162
  integrationPagination?: boolean;
163
+ /**
164
+ * Enable progressive loading for Crowdin files (directories first, then files).
165
+ */
166
+ progressiveCrowdinFilesLoading?: boolean;
163
167
  /**
164
168
  * Enable the option to upload translations to crowdin that are already present in the integration.
165
169
  */
@@ -168,6 +172,10 @@ export interface IntegrationLogic extends ModuleKey {
168
172
  * Force sync translations from Crowdin to integration regardless of etag.
169
173
  */
170
174
  forcePushTranslations?: boolean;
175
+ /**
176
+ * Force sync sources from integration to Crowdin.
177
+ */
178
+ forcePushSources?: boolean;
171
179
  /**
172
180
  * Enable the option to upload file for translation into selected languages.
173
181
  */
@@ -352,6 +360,7 @@ export interface OAuthLogin {
352
360
  */
353
361
  performRefreshTokenRequest?: (currentCredentials: any, loginForm?: any) => Promise<any>;
354
362
  }
363
+ export type CrowdinFilesLoadMode = 'directories' | 'files';
355
364
  export interface BaseTreeItem {
356
365
  id: string;
357
366
  name: string;
@@ -363,6 +372,7 @@ export interface BaseTreeItem {
363
372
  tooltip?: string;
364
373
  path?: string;
365
374
  type?: string;
375
+ syncedAt?: string;
366
376
  }
367
377
  export interface File extends BaseTreeItem {
368
378
  type: SourceFilesModel.FileType;
@@ -60,7 +60,7 @@ exports.getOauthRoute = getOauthRoute;
60
60
  function applyIntegrationModuleDefaults(config, integration) {
61
61
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
62
62
  if (!integration.getCrowdinFiles) {
63
- integration.getCrowdinFiles = (projectId, client, rootFolder) => __awaiter(this, void 0, void 0, function* () {
63
+ integration.getCrowdinFiles = (projectId, client, rootFolder, config, mode) => __awaiter(this, void 0, void 0, function* () {
64
64
  const allBranches = (yield client.sourceFilesApi.withFetchAll().listProjectBranches(projectId)).data.map((d) => d.data);
65
65
  let options = {};
66
66
  if (rootFolder) {
@@ -69,57 +69,63 @@ function applyIntegrationModuleDefaults(config, integration) {
69
69
  recursion: 'true',
70
70
  };
71
71
  }
72
- const allDirectories = (yield client.sourceFilesApi.withFetchAll().listProjectDirectories(projectId, options)).data.map((d) => d.data);
73
- const directoryIds = allDirectories.map((d) => d.id);
74
- let files = (yield client.sourceFilesApi.withFetchAll().listProjectFiles(projectId, options)).data.map((d) => d.data);
75
- files = files.filter((f) => (rootFolder && f.directoryId === rootFolder.id) ||
76
- directoryIds.includes(f.directoryId) ||
77
- (!rootFolder && !f.directoryId));
78
72
  const res = [];
79
- allDirectories.forEach((e) => {
80
- let parentId = rootFolder && e.directoryId === rootFolder.id ? undefined : e.directoryId;
81
- if (!parentId && e.branchId) {
82
- const branch = allBranches.find((branch) => branch.id === e.branchId);
83
- if (branch) {
84
- parentId = branch.id;
85
- if (!res.find((node) => node.id === branch.id.toString())) {
86
- res.push({
87
- id: branch.id.toString(),
88
- name: branch.name,
89
- nodeType: '2',
90
- });
73
+ if (!mode || mode === 'directories') {
74
+ const allDirectories = (yield client.sourceFilesApi.withFetchAll().listProjectDirectories(projectId, options)).data.map((d) => d.data);
75
+ allDirectories.forEach((e) => {
76
+ let parentId = rootFolder && e.directoryId === rootFolder.id ? undefined : e.directoryId;
77
+ 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
+ }
91
88
  }
92
89
  }
93
- }
94
- res.push({
95
- id: e.id.toString(),
96
- parentId: parentId ? parentId.toString() : undefined,
97
- name: e.name,
90
+ res.push({
91
+ id: e.id.toString(),
92
+ parentId: parentId ? parentId.toString() : undefined,
93
+ name: e.name,
94
+ });
98
95
  });
99
- });
100
- files.forEach((e) => {
101
- let parentId = rootFolder && e.directoryId === rootFolder.id ? undefined : e.directoryId;
102
- if (!parentId && e.branchId) {
103
- const branch = allBranches.find((branch) => branch.id === e.branchId);
104
- if (branch) {
105
- parentId = branch.id;
106
- if (!res.find((node) => node.id === branch.id.toString())) {
107
- res.push({
108
- id: branch.id.toString(),
109
- name: branch.name,
110
- nodeType: '2',
111
- });
96
+ }
97
+ if (!mode || mode === 'files') {
98
+ const directoryIds = mode === 'files'
99
+ ? (yield client.sourceFilesApi.withFetchAll().listProjectDirectories(projectId, options)).data.map((d) => d.data.id)
100
+ : 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) ||
103
+ directoryIds.includes(f.directoryId) ||
104
+ (!rootFolder && !f.directoryId));
105
+ files.forEach((e) => {
106
+ let parentId = rootFolder && e.directoryId === rootFolder.id ? undefined : e.directoryId;
107
+ 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
+ }
112
118
  }
113
119
  }
114
- }
115
- res.push({
116
- id: e.id.toString(),
117
- parentId: parentId ? parentId.toString() : undefined,
118
- name: e.title || e.name,
119
- type: e.type,
120
- excludedTargetLanguages: e.excludedTargetLanguages,
120
+ res.push({
121
+ id: e.id.toString(),
122
+ parentId: parentId ? parentId.toString() : undefined,
123
+ name: e.title || e.name,
124
+ type: e.type,
125
+ excludedTargetLanguages: e.excludedTargetLanguages,
126
+ });
121
127
  });
122
- });
128
+ }
123
129
  return res;
124
130
  });
125
131
  }
@@ -23,3 +23,4 @@ export declare function getParentFiles(allFiles: TreeItem[], fileCollection: Tre
23
23
  export declare function filterFilesByPatterns(files: TreeItem[], patterns: string[]): TreeItem[];
24
24
  export declare function filterFilesByPath(files: TreeItem[], includePatterns?: string[], excludePatterns?: string[]): TreeItem[];
25
25
  export declare function updateSyncedData(clientId: string, crowdinId: string, payload: any[], provider?: Provider): Promise<void>;
26
+ export declare function filterSyncedData(payload: any[]): Promise<any[]>;
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.updateSyncedData = exports.filterFilesByPath = exports.filterFilesByPatterns = exports.getParentFiles = exports.getParentPaths = exports.buildPath = exports.filterLanguages = exports.getExcludedTargetLanguages = exports.markUnsyncedFiles = exports.isExtendedResultType = exports.expandFilesTree = exports.skipFilesByRegex = void 0;
15
+ exports.filterSyncedData = exports.updateSyncedData = exports.filterFilesByPath = exports.filterFilesByPatterns = exports.getParentFiles = exports.getParentPaths = exports.buildPath = exports.filterLanguages = exports.getExcludedTargetLanguages = exports.markUnsyncedFiles = exports.isExtendedResultType = exports.expandFilesTree = exports.skipFilesByRegex = void 0;
16
16
  const types_1 = require("../types");
17
17
  const types_2 = require("./types");
18
18
  const storage_1 = require("../../../storage");
@@ -38,7 +38,7 @@ function skipFilesByRegex(files, skipIntegrationNodes) {
38
38
  }
39
39
  exports.skipFilesByRegex = skipFilesByRegex;
40
40
  function expandFilesTree(nodes, req, integration, job) {
41
- var _a;
41
+ var _a, _b, _c, _d, _e, _f, _g;
42
42
  return __awaiter(this, void 0, void 0, function* () {
43
43
  if (job && types_2.JobStatus.CANCELED === ((_a = (yield job.get())) === null || _a === void 0 ? void 0 : _a.status)) {
44
44
  throw new Error('Job canceled');
@@ -69,6 +69,46 @@ function expandFilesTree(nodes, req, integration, job) {
69
69
  const expandedResult = yield expandFilesTree(checkNodes, req, integration, job);
70
70
  files.push(...expandedResult);
71
71
  }
72
+ const needsFileStatus = ((_c = (_b = integration.filtering) === null || _b === void 0 ? void 0 : _b.integrationFileStatus) === null || _c === void 0 ? void 0 : _c.isNew) ||
73
+ ((_e = (_d = integration.filtering) === null || _d === void 0 ? void 0 : _d.integrationFileStatus) === null || _e === void 0 ? void 0 : _e.isUpdated) ||
74
+ ((_g = (_f = integration.filtering) === null || _f === void 0 ? void 0 : _f.integrationFileStatus) === null || _g === void 0 ? void 0 : _g.notSynced) ||
75
+ integration.forcePushSources === true;
76
+ if (needsFileStatus) {
77
+ const { crowdinId, clientId } = req.crowdinContext;
78
+ const syncedData = yield (0, storage_1.getStorage)().getSyncedData(clientId, crowdinId, types_1.Provider.INTEGRATION);
79
+ const syncedFiles = (syncedData === null || syncedData === void 0 ? void 0 : syncedData.files) ? JSON.parse(syncedData.files) : [];
80
+ const lastSyncTimestamp = (syncedData === null || syncedData === void 0 ? void 0 : syncedData.updatedAt) ? Number(syncedData.updatedAt) : null;
81
+ return files.map((file) => {
82
+ var _a, _b, _c, _d, _e, _f;
83
+ if (!file.type) {
84
+ return file;
85
+ }
86
+ const syncedFile = syncedFiles.find((syncedItem) => syncedItem.id === file.id);
87
+ const notSynced = ((_b = (_a = integration.filtering) === null || _a === void 0 ? void 0 : _a.integrationFileStatus) === null || _b === void 0 ? void 0 : _b.notSynced) === true ? !syncedFile : false;
88
+ const fileSyncTimestamp = (syncedFile === null || syncedFile === void 0 ? void 0 : syncedFile.syncedAt) ? Number(syncedFile.syncedAt) : lastSyncTimestamp;
89
+ const isNew = ((_d = (_c = integration.filtering) === null || _c === void 0 ? void 0 : _c.integrationFileStatus) === null || _d === void 0 ? void 0 : _d.isNew) === true
90
+ ? !syncedFile &&
91
+ fileSyncTimestamp &&
92
+ file.createdAt &&
93
+ (typeof file.createdAt === 'string' ? new Date(file.createdAt).getTime() : file.createdAt) >
94
+ fileSyncTimestamp
95
+ : false;
96
+ const shouldCalculateIsUpdated = ((_f = (_e = integration.filtering) === null || _e === void 0 ? void 0 : _e.integrationFileStatus) === null || _f === void 0 ? void 0 : _f.isUpdated) === true ||
97
+ integration.forcePushSources === true;
98
+ const isUpdated = file.isUpdated !== undefined
99
+ ? file.isUpdated
100
+ : shouldCalculateIsUpdated
101
+ ? syncedFile &&
102
+ fileSyncTimestamp &&
103
+ file.updatedAt &&
104
+ (typeof file.updatedAt === 'string' ? new Date(file.updatedAt).getTime() : file.updatedAt) >
105
+ fileSyncTimestamp
106
+ : false;
107
+ return Object.assign(Object.assign({}, file), { isNew,
108
+ notSynced,
109
+ isUpdated });
110
+ });
111
+ }
72
112
  return files;
73
113
  });
74
114
  }
@@ -201,14 +241,22 @@ exports.filterFilesByPath = filterFilesByPath;
201
241
  function updateSyncedData(clientId, crowdinId, payload, provider = types_1.Provider.INTEGRATION) {
202
242
  return __awaiter(this, void 0, void 0, function* () {
203
243
  const existingSyncedData = yield (0, storage_1.getStorage)().getSyncedData(clientId, crowdinId, provider);
244
+ const currentTimestamp = Date.now().toString();
245
+ const filesWithTimestamp = (payload === null || payload === void 0 ? void 0 : payload.map((file) => (Object.assign(Object.assign({}, file), { syncedAt: currentTimestamp })))) || [];
204
246
  if (existingSyncedData) {
205
247
  const existingFiles = JSON.parse(existingSyncedData.files);
206
- const mergedFiles = (0, lodash_uniqby_1.default)([...existingFiles, ...payload], 'id');
248
+ const mergedFiles = (0, lodash_uniqby_1.default)([...filesWithTimestamp, ...existingFiles], 'id');
207
249
  yield (0, storage_1.getStorage)().updateSyncedData(JSON.stringify(mergedFiles), clientId, crowdinId, provider);
208
250
  }
209
251
  else {
210
- yield (0, storage_1.getStorage)().saveSyncedData(JSON.stringify(payload), clientId, crowdinId, provider);
252
+ yield (0, storage_1.getStorage)().saveSyncedData(JSON.stringify(filesWithTimestamp), clientId, crowdinId, provider);
211
253
  }
212
254
  });
213
255
  }
214
256
  exports.updateSyncedData = updateSyncedData;
257
+ function filterSyncedData(payload) {
258
+ return __awaiter(this, void 0, void 0, function* () {
259
+ return payload === null || payload === void 0 ? void 0 : payload.filter((file) => file.isUpdated !== false);
260
+ });
261
+ }
262
+ exports.filterSyncedData = filterSyncedData;
@@ -14,7 +14,7 @@ export declare function runAsJob({ integrationId, crowdinId, type, title, payloa
14
14
  jobType: JobClientType;
15
15
  jobStoreType: JobStoreType;
16
16
  jobCallback: (arg1: JobClient) => Promise<any>;
17
- onError?: (e: any) => Promise<void>;
17
+ onError?: (e: any, job: JobClient) => Promise<void>;
18
18
  reRunJobId?: string;
19
19
  forcePushTranslations?: boolean;
20
20
  }): Promise<void>;
@@ -85,7 +85,7 @@ function runAsJob({ integrationId, crowdinId, type, title, payload, res, project
85
85
  return yield storage.getJob({ id: jobId });
86
86
  });
87
87
  },
88
- update: function updateProgress({ progress, status, info, data, attempt, errors, }) {
88
+ update: function updateProgress({ progress, status, info, data, attempt, errors, processedEntities, }) {
89
89
  return __awaiter(this, void 0, void 0, function* () {
90
90
  const prevData = yield this.get();
91
91
  if ((prevData === null || prevData === void 0 ? void 0 : prevData.status) === types_1.JobStatus.CANCELED) {
@@ -99,6 +99,7 @@ function runAsJob({ integrationId, crowdinId, type, title, payload, res, project
99
99
  data: JSON.stringify(data),
100
100
  attempt,
101
101
  errors,
102
+ processedEntities: JSON.stringify(processedEntities),
102
103
  });
103
104
  return { isCanceled: false };
104
105
  });
@@ -222,7 +223,7 @@ function runAsJob({ integrationId, crowdinId, type, title, payload, res, project
222
223
  info: (0, logger_1.getErrorMessage)(e),
223
224
  });
224
225
  if (onError) {
225
- yield onError(e);
226
+ yield onError(e, job);
226
227
  }
227
228
  else {
228
229
  throw e;
@@ -41,6 +41,7 @@ export interface Job {
41
41
  info?: string;
42
42
  attempt?: number;
43
43
  errors?: string;
44
+ processedEntities?: string;
44
45
  }
45
46
  export type GetJobParams = Pick<Job, 'id'>;
46
47
  export type GetActiveJobsParams = Pick<Job, 'integrationId' | 'crowdinId'>;
@@ -53,6 +54,7 @@ export type UpdateJobParams = {
53
54
  data?: string;
54
55
  attempt?: number;
55
56
  errors?: string[];
57
+ processedEntities?: string;
56
58
  };
57
59
  export type JobClient = {
58
60
  get: () => Promise<Job | undefined>;
@@ -124,3 +126,9 @@ export interface IntegrationSyncedData {
124
126
  updatedAt: string;
125
127
  files?: any;
126
128
  }
129
+ export type GetAllJobsParams = {
130
+ integrationId: string;
131
+ crowdinId: string;
132
+ limit: number;
133
+ offset: number;
134
+ };
@@ -16,14 +16,14 @@ function register({ config, app }) {
16
16
  if (Array.isArray(config.modal)) {
17
17
  config.modal.forEach((modal) => {
18
18
  if ((modal === null || modal === void 0 ? void 0 : modal.uiPath) || (modal === null || modal === void 0 ? void 0 : modal.formSchema)) {
19
- app.use(`/modal-${modal.key}`, (0, ui_module_1.default)({ config, allowUnauthorized, moduleType: modal.key }), (0, render_ui_module_1.default)(modal, config));
19
+ app.use(`/modal-${modal.key}`, (0, ui_module_1.default)({ config, allowUnauthorized, moduleType: modal.key }), (0, render_ui_module_1.default)(modal));
20
20
  }
21
21
  });
22
22
  }
23
23
  else {
24
24
  // backward compatibility will be removed after migration
25
25
  if (((_a = config.modal) === null || _a === void 0 ? void 0 : _a.uiPath) || ((_b = config.modal) === null || _b === void 0 ? void 0 : _b.formSchema)) {
26
- app.use('/modal', (0, ui_module_1.default)({ config, allowUnauthorized, moduleType: config.modal.key }), (0, render_ui_module_1.default)(config.modal, config));
26
+ app.use('/modal', (0, ui_module_1.default)({ config, allowUnauthorized, moduleType: config.modal.key }), (0, render_ui_module_1.default)(config.modal));
27
27
  }
28
28
  }
29
29
  }