@crowdin/app-project-module 0.98.0-cf-12 → 0.98.0-cf-13

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.d.ts CHANGED
@@ -4,6 +4,7 @@ import express from './util/terminus-express';
4
4
  import { getRequestCredentialsMasker, postRequestCredentialsMasker, maskKey } from './util/credentials-masker';
5
5
  export { getRequestCredentialsMasker, postRequestCredentialsMasker, maskKey };
6
6
  export { ProjectPermissions, Scope, UserPermissions } from './types';
7
+ export { Cron } from './util/cron';
7
8
  export { express };
8
9
  export declare const metadataStore: CrowdinMetadataStore;
9
10
  export declare function createApp(clientConfig: ClientConfig): void;
package/out/index.js CHANGED
@@ -51,6 +51,7 @@ const uninstall_1 = __importDefault(require("./modules/uninstall"));
51
51
  const status_1 = __importDefault(require("./modules/status"));
52
52
  const storage = __importStar(require("./storage"));
53
53
  const d1_1 = require("./storage/d1");
54
+ const cron = __importStar(require("./util/cron"));
54
55
  const types_1 = require("./types");
55
56
  const util_1 = require("./util");
56
57
  const form_schema_1 = require("./util/form-schema");
@@ -139,6 +140,7 @@ function addCrowdinEndpoints(app, clientConfig) {
139
140
  }
140
141
  storage.initialize(config);
141
142
  logger.initialize(config);
143
+ cron.initialize(config);
142
144
  // Middleware to ensure D1 migration before handling requests
143
145
  app.use((req, res, next) => __awaiter(this, void 0, void 0, function* () {
144
146
  try {
@@ -208,7 +210,7 @@ function addCrowdinEndpoints(app, clientConfig) {
208
210
  contextMenuApp.register({ config, app });
209
211
  //other apps only work in authorized context
210
212
  if (!(0, util_1.isAuthorizedConfig)(config)) {
211
- return Object.assign({}, exports.metadataStore);
213
+ return Object.assign(Object.assign({}, exports.metadataStore), { cron: cron.getCron() });
212
214
  }
213
215
  app.post('/installed', (0, install_1.default)(config));
214
216
  app.post('/uninstall', (0, uninstall_1.default)(config));
@@ -221,7 +223,7 @@ function addCrowdinEndpoints(app, clientConfig) {
221
223
  * /util -> folder for utilities
222
224
  * /handlers -> for http handlers
223
225
  */
224
- const { cronExecutions } = integrationApp.register({ config, app });
226
+ integrationApp.register({ config, app });
225
227
  customMTApp.register({ config, app });
226
228
  fileProcessingApps.registerCustomFileFormat({ config, app });
227
229
  fileProcessingApps.registerFilePreImport({ config, app });
@@ -238,8 +240,7 @@ function addCrowdinEndpoints(app, clientConfig) {
238
240
  workflowStepType.register({ config, app });
239
241
  aiRequestProcessors.register({ config, app });
240
242
  addFormSchema({ config, app });
241
- return Object.assign(Object.assign({}, exports.metadataStore), { //TODO for backward compatibility (needs to be removed)
242
- cronExecutions, storage: storage.getStorage(), establishCrowdinConnection: (authRequest, moduleKey) => {
243
+ return Object.assign(Object.assign({}, exports.metadataStore), { storage: storage.getStorage(), cron: cron.getCron(), establishCrowdinConnection: (authRequest, moduleKey) => {
243
244
  let jwtToken = '';
244
245
  if (typeof authRequest === 'string') {
245
246
  jwtToken = authRequest;
@@ -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;
@@ -1,27 +1,4 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
3
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
4
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -41,6 +18,7 @@ const integration_credentials_1 = __importDefault(require("../../middlewares/int
41
18
  const json_response_1 = __importDefault(require("../../middlewares/json-response"));
42
19
  const util_1 = require("../../util");
43
20
  const static_files_1 = require("../../util/static-files");
21
+ const cron_1 = require("../../util/cron");
44
22
  const defaults_1 = require("./util/defaults");
45
23
  const webhooks_1 = require("./util/webhooks");
46
24
  const crowdin_file_progress_1 = __importDefault(require("./handlers/crowdin-file-progress"));
@@ -66,14 +44,13 @@ const sync_settings_save_1 = __importDefault(require("./handlers/sync-settings-s
66
44
  const user_errors_1 = __importDefault(require("./handlers/user-errors"));
67
45
  const users_1 = __importDefault(require("./handlers/users"));
68
46
  const invite_users_1 = __importDefault(require("./handlers/invite-users"));
69
- const cron_1 = require("./util/cron");
47
+ const cron_2 = require("./util/cron");
70
48
  const storage_1 = require("../../storage");
71
49
  function register({ config, app }) {
72
50
  var _a, _b, _c;
73
- let cronExecutions = {};
74
51
  const integrationLogic = config.projectIntegration;
75
52
  if (!integrationLogic) {
76
- return { cronExecutions };
53
+ return;
77
54
  }
78
55
  (0, defaults_1.applyIntegrationModuleDefaults)(config, integrationLogic);
79
56
  app.get((0, util_1.getLogoUrl)(integrationLogic, '/integration'), (0, static_files_1.serveFile)(config, integrationLogic.imagePath || config.imagePath));
@@ -184,39 +161,27 @@ function register({ config, app }) {
184
161
  }), (0, oauth_polling_1.default)(integrationLogic));
185
162
  }
186
163
  }
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));
164
+ const cron = (0, cron_1.getCron)();
165
+ if (integrationLogic.cronJobs) {
166
+ integrationLogic.cronJobs.forEach((job) => {
167
+ cron.schedule(job.expression, () => (0, cron_2.runJob)({ config, integration: integrationLogic, job }).catch(console.error));
168
+ });
169
+ }
170
+ if (integrationLogic.withCronSync) {
171
+ cron.schedule('0 * * * *', () => (0, cron_2.filesCron)({ config, integration: integrationLogic, period: '1' }).catch(console.error));
172
+ cron.schedule('0 */3 * * *', () => (0, cron_2.filesCron)({ config, integration: integrationLogic, period: '3' }).catch(console.error));
173
+ cron.schedule('0 */6 * * *', () => (0, cron_2.filesCron)({ config, integration: integrationLogic, period: '6' }).catch(console.error));
174
+ cron.schedule('0 */12 * * *', () => (0, cron_2.filesCron)({ config, integration: integrationLogic, period: '12' }).catch(console.error));
175
+ cron.schedule('0 0 * * *', () => (0, cron_2.filesCron)({ config, integration: integrationLogic, period: '24' }).catch(console.error));
176
+ }
177
+ // remove user errors
178
+ cron.schedule('0 0 * * *', () => __awaiter(this, void 0, void 0, function* () {
179
+ if (integrationLogic.userErrorLifetimeDays) {
180
+ const date = (0, util_1.getPreviousDate)(integrationLogic.userErrorLifetimeDays);
181
+ yield (0, storage_1.getStorage)().deleteAllUsersErrorsOlderThan(`${date.getTime()}`);
200
182
  }
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
- });
183
+ }));
184
+ cron.schedule('0 0 1 * *', () => (0, cron_2.removeFinishedJobs)());
220
185
  if (integrationLogic.webhooks) {
221
186
  app.post(`${integrationLogic.webhooks.crowdinWebhookUrl
222
187
  ? integrationLogic.webhooks.crowdinWebhookUrl
@@ -253,6 +218,5 @@ function register({ config, app }) {
253
218
  checkSubscriptionExpiration: true,
254
219
  moduleKey: integrationLogic.key,
255
220
  }), (0, integration_credentials_1.default)(config, integrationLogic), (0, invite_users_1.default)());
256
- return { cronExecutions };
257
221
  }
258
222
  exports.register = register;
package/out/types.d.ts CHANGED
@@ -23,6 +23,8 @@ import { ExternalQaCheckModule } from './modules/external-qa-check/types';
23
23
  import { Webhook } from './modules/webhooks/types';
24
24
  import { WorkflowStepTypeModule } from './modules/workflow-step-type/types';
25
25
  import { AiRequestProcessorModule, AiStreamProcessorModule } from './modules/ai-request-processors/types';
26
+ import { Cron } from './util/cron';
27
+ export { Cron };
26
28
  export interface ClientConfig extends ImagePath {
27
29
  /**
28
30
  * Authentication Crowdin App type: "authorization_code", "crowdin_app", "crowdin_agent". Default: "crowdin_app"
@@ -108,6 +110,12 @@ export interface ClientConfig extends ImagePath {
108
110
  * config to configure Cloudflare D1 as a storage
109
111
  */
110
112
  d1Config?: D1StorageConfig;
113
+ /**
114
+ * Custom cron for scheduling background jobs.
115
+ * If not provided, the default node-cron will be used.
116
+ * Useful for external cron systems like Cloudflare Workers Scheduled Events.
117
+ */
118
+ cron?: Cron;
111
119
  /**
112
120
  * Cloudflare Workers Assets configuration
113
121
  */
@@ -414,7 +422,7 @@ export interface CrowdinAppUtilities extends CrowdinMetadataStore {
414
422
  extra: Record<string, any>;
415
423
  }>;
416
424
  storage: Storage;
417
- cronExecutions?: Record<string, () => Promise<void>>;
425
+ cron: Cron;
418
426
  }
419
427
  export interface CrowdinMetadataStore {
420
428
  saveMetadata: (id: string, metadata: any, crowdinId: string) => Promise<void>;
@@ -529,4 +537,3 @@ export interface FileStore {
529
537
  storeFile: (content: Buffer) => Promise<string>;
530
538
  deleteFile: (fileRef: string) => Promise<void>;
531
539
  }
532
- export {};
@@ -0,0 +1,29 @@
1
+ import { Config, UnauthorizedConfig } from '../types';
2
+ export interface Cron {
3
+ /**
4
+ * Schedule a task to run on a cron expression
5
+ * @param expression - Cron expression (e.g., '0 * * * *' for hourly)
6
+ * @param task - Async task to execute
7
+ */
8
+ schedule(expression: string, task: () => Promise<void>): void;
9
+ }
10
+ /**
11
+ * Default Node.js cron handler that uses node-cron
12
+ */
13
+ export declare class NodeCron implements Cron {
14
+ private cron;
15
+ private pendingSchedules;
16
+ private initialized;
17
+ constructor();
18
+ schedule(expression: string, task: () => Promise<void>): void;
19
+ }
20
+ /**
21
+ * Initialize the cron instance
22
+ * @param config - Client configuration
23
+ */
24
+ export declare function initialize(config: Config | UnauthorizedConfig): void;
25
+ /**
26
+ * Get the initialized cron instance
27
+ * @returns The cron instance
28
+ */
29
+ export declare function getCron(): Cron;
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.getCron = exports.initialize = exports.NodeCron = void 0;
27
+ const logger_1 = require("./logger");
28
+ /**
29
+ * Default Node.js cron handler that uses node-cron
30
+ */
31
+ class NodeCron {
32
+ constructor() {
33
+ this.pendingSchedules = [];
34
+ this.initialized = false;
35
+ // Dynamically import node-cron
36
+ Promise.resolve().then(() => __importStar(require('node-cron'))).then((nodeCron) => {
37
+ this.cron = nodeCron.default || nodeCron;
38
+ this.initialized = true;
39
+ // Schedule all pending tasks
40
+ this.pendingSchedules.forEach(({ expression, task }) => {
41
+ this.cron.schedule(expression, task);
42
+ });
43
+ this.pendingSchedules = [];
44
+ })
45
+ .catch((error) => {
46
+ console.error('Failed to load node-cron:', error);
47
+ throw new Error('node-cron is required for the default cron. Please install it or provide a custom cron in config.');
48
+ });
49
+ }
50
+ schedule(expression, task) {
51
+ if (this.initialized && this.cron) {
52
+ this.cron.schedule(expression, task);
53
+ }
54
+ else {
55
+ // Queue the task until node-cron is loaded
56
+ this.pendingSchedules.push({ expression, task });
57
+ }
58
+ }
59
+ }
60
+ exports.NodeCron = NodeCron;
61
+ let cron;
62
+ /**
63
+ * Initialize the cron instance
64
+ * @param config - Client configuration
65
+ */
66
+ function initialize(config) {
67
+ if (config.cron) {
68
+ (0, logger_1.log)('Using custom cron implementation');
69
+ cron = config.cron;
70
+ }
71
+ else {
72
+ (0, logger_1.log)('Using default NodeCron implementation');
73
+ cron = new NodeCron();
74
+ }
75
+ }
76
+ exports.initialize = initialize;
77
+ /**
78
+ * Get the initialized cron instance
79
+ * @returns The cron instance
80
+ */
81
+ function getCron() {
82
+ if (!cron) {
83
+ throw new Error('Cron not initialized');
84
+ }
85
+ return cron;
86
+ }
87
+ exports.getCron = getCron;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdin/app-project-module",
3
- "version": "0.98.0-cf-12",
3
+ "version": "0.98.0-cf-13",
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",