@crowdin/app-project-module 0.105.1 → 0.107.0-cf-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 (164) 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/index.d.ts +1 -0
  6. package/out/index.js +30 -12
  7. package/out/middlewares/crowdin-client.d.ts +1 -1
  8. package/out/middlewares/integration-credentials.d.ts +1 -1
  9. package/out/middlewares/integration-credentials.js +4 -1
  10. package/out/middlewares/render-ui-module.d.ts +3 -3
  11. package/out/middlewares/render-ui-module.js +9 -13
  12. package/out/middlewares/ui-module.d.ts +1 -1
  13. package/out/middlewares/ui-module.js +10 -1
  14. package/out/modules/about.d.ts +2 -1
  15. package/out/modules/about.js +10 -3
  16. package/out/modules/ai-prompt-provider/handlers/compile.d.ts +1 -1
  17. package/out/modules/ai-prompt-provider/index.js +2 -2
  18. package/out/modules/ai-provider/handlers/chat-completions.d.ts +1 -1
  19. package/out/modules/ai-provider/handlers/chat-completions.js +0 -1
  20. package/out/modules/ai-provider/handlers/get-model-list.d.ts +1 -1
  21. package/out/modules/ai-provider/index.js +2 -2
  22. package/out/modules/ai-provider/types.d.ts +2 -2
  23. package/out/modules/ai-provider/util/index.js +0 -2
  24. package/out/modules/ai-request-processors/handler.d.ts +1 -1
  25. package/out/modules/ai-tools/handlers/tool-calls.d.ts +1 -1
  26. package/out/modules/ai-tools/index.js +1 -1
  27. package/out/modules/api/api.js +0 -1
  28. package/out/modules/auth-guard/handlers/verify.d.ts +1 -1
  29. package/out/modules/auth-guard/index.js +1 -1
  30. package/out/modules/automation-action/handlers/execute.d.ts +1 -1
  31. package/out/modules/automation-action/handlers/input-schema.d.ts +1 -1
  32. package/out/modules/automation-action/handlers/output-schema.d.ts +1 -1
  33. package/out/modules/automation-action/handlers/validate-settings.d.ts +1 -1
  34. package/out/modules/automation-action/index.js +1 -1
  35. package/out/modules/context-menu/index.js +2 -2
  36. package/out/modules/custom-mt/handlers/translate.d.ts +2 -2
  37. package/out/modules/custom-mt/handlers/translate.js +53 -4
  38. package/out/modules/custom-mt/index.js +6 -3
  39. package/out/modules/custom-mt/types.d.ts +14 -2
  40. package/out/modules/custom-spell-check/handlers/get-languages-list.d.ts +1 -1
  41. package/out/modules/custom-spell-check/handlers/spell-check.d.ts +1 -1
  42. package/out/modules/custom-spell-check/index.js +4 -4
  43. package/out/modules/editor-right-panel/index.js +1 -1
  44. package/out/modules/external-qa-check/handlers/validate.d.ts +1 -1
  45. package/out/modules/external-qa-check/index.js +2 -2
  46. package/out/modules/file-processing/handlers/custom-file-format.d.ts +7 -2
  47. package/out/modules/file-processing/handlers/custom-file-format.js +59 -19
  48. package/out/modules/file-processing/handlers/file-download.d.ts +1 -1
  49. package/out/modules/file-processing/handlers/file-download.js +5 -0
  50. package/out/modules/file-processing/handlers/pre-post-process.d.ts +1 -1
  51. package/out/modules/file-processing/handlers/pre-post-process.js +34 -14
  52. package/out/modules/file-processing/handlers/translations-alignment.d.ts +1 -1
  53. package/out/modules/file-processing/index.js +12 -2
  54. package/out/modules/file-processing/util/defaults.js +50 -6
  55. package/out/modules/file-processing/util/files.js +2 -1
  56. package/out/modules/form-data-display.d.ts +1 -1
  57. package/out/modules/form-data-save.d.ts +1 -1
  58. package/out/modules/install.d.ts +1 -1
  59. package/out/modules/integration/handlers/crowdin-file-progress.d.ts +1 -1
  60. package/out/modules/integration/handlers/crowdin-files.d.ts +1 -1
  61. package/out/modules/integration/handlers/crowdin-project.d.ts +1 -1
  62. package/out/modules/integration/handlers/crowdin-update.d.ts +1 -1
  63. package/out/modules/integration/handlers/crowdin-webhook.d.ts +1 -1
  64. package/out/modules/integration/handlers/integration-data.d.ts +1 -1
  65. package/out/modules/integration/handlers/integration-login.d.ts +1 -1
  66. package/out/modules/integration/handlers/integration-logout.d.ts +1 -1
  67. package/out/modules/integration/handlers/integration-update.d.ts +1 -1
  68. package/out/modules/integration/handlers/integration-webhook.d.ts +1 -1
  69. package/out/modules/integration/handlers/invite-users.d.ts +1 -1
  70. package/out/modules/integration/handlers/job-cancel.d.ts +1 -1
  71. package/out/modules/integration/handlers/job-info-deprecated.d.ts +1 -1
  72. package/out/modules/integration/handlers/job-info.d.ts +1 -1
  73. package/out/modules/integration/handlers/job-list.d.ts +1 -1
  74. package/out/modules/integration/handlers/main.d.ts +1 -1
  75. package/out/modules/integration/handlers/main.js +13 -1
  76. package/out/modules/integration/handlers/oauth-login.d.ts +1 -1
  77. package/out/modules/integration/handlers/oauth-login.js +12 -3
  78. package/out/modules/integration/handlers/oauth-polling.d.ts +2 -2
  79. package/out/modules/integration/handlers/oauth-polling.js +5 -3
  80. package/out/modules/integration/handlers/oauth-url.d.ts +1 -1
  81. package/out/modules/integration/handlers/settings-save.d.ts +1 -1
  82. package/out/modules/integration/handlers/settings.d.ts +1 -1
  83. package/out/modules/integration/handlers/sync-settings-save.d.ts +1 -1
  84. package/out/modules/integration/handlers/sync-settings-save.js +0 -2
  85. package/out/modules/integration/handlers/sync-settings.d.ts +1 -1
  86. package/out/modules/integration/handlers/user-errors.d.ts +1 -1
  87. package/out/modules/integration/handlers/users.d.ts +1 -1
  88. package/out/modules/integration/index.js +13 -34
  89. package/out/modules/integration/util/cron.js +1 -10
  90. package/out/modules/integration/util/defaults.js +31 -28
  91. package/out/modules/integration/util/files.js +18 -11
  92. package/out/modules/integration/util/job.js +0 -4
  93. package/out/modules/integration/util/snapshot.js +1 -5
  94. package/out/modules/integration/util/webhooks.js +1 -8
  95. package/out/modules/manifest.js +15 -13
  96. package/out/modules/modal/index.js +2 -2
  97. package/out/modules/organization-menu/index.js +5 -4
  98. package/out/modules/organization-settings-menu/index.js +5 -4
  99. package/out/modules/profile-resources-menu/index.js +5 -4
  100. package/out/modules/profile-settings-menu/index.js +5 -4
  101. package/out/modules/project-menu/index.js +1 -1
  102. package/out/modules/project-menu-crowdsource/index.js +1 -1
  103. package/out/modules/project-reports/index.js +3 -2
  104. package/out/modules/project-tools/index.js +3 -2
  105. package/out/modules/status.d.ts +1 -1
  106. package/out/modules/status.js +12 -3
  107. package/out/modules/subscription-paid.d.ts +1 -1
  108. package/out/modules/uninstall.d.ts +1 -1
  109. package/out/modules/webhooks/handlers/webhook-handler.d.ts +1 -1
  110. package/out/modules/webhooks/handlers/webhook-handler.js +30 -15
  111. package/out/modules/webhooks/types.d.ts +7 -0
  112. package/out/modules/workflow-step-type/handlers/delete-step.d.ts +1 -1
  113. package/out/modules/workflow-step-type/handlers/step-settings-save.d.ts +1 -1
  114. package/out/modules/workflow-step-type/index.js +2 -2
  115. package/out/storage/d1.d.ts +107 -0
  116. package/out/storage/d1.js +829 -0
  117. package/out/storage/index.js +8 -2
  118. package/out/storage/mysql.js +0 -3
  119. package/out/storage/postgre.js +0 -1
  120. package/out/storage/sqlite.js +0 -3
  121. package/out/types.d.ts +41 -2
  122. package/out/util/axios.js +0 -1
  123. package/out/util/credentials-masker.d.ts +1 -1
  124. package/out/util/cron.d.ts +29 -0
  125. package/out/util/cron.js +87 -0
  126. package/out/util/index.d.ts +12 -1
  127. package/out/util/index.js +54 -6
  128. package/out/util/jsx-renderer.d.ts +6 -0
  129. package/out/util/jsx-renderer.js +13 -0
  130. package/out/util/logger.js +0 -4
  131. package/out/util/static-files.d.ts +19 -0
  132. package/out/util/static-files.js +87 -0
  133. package/out/views/AboutPage.d.ts +10 -0
  134. package/out/views/AboutPage.js +76 -0
  135. package/out/views/ErrorPage.d.ts +18 -0
  136. package/out/views/ErrorPage.js +56 -0
  137. package/out/views/FormPage.d.ts +14 -0
  138. package/out/views/FormPage.js +28 -0
  139. package/out/views/InstallPage.d.ts +10 -0
  140. package/out/views/InstallPage.js +22 -0
  141. package/out/views/LoginPage.d.ts +33 -0
  142. package/out/views/LoginPage.js +200 -0
  143. package/out/views/MainPage.d.ts +81 -0
  144. package/out/views/MainPage.js +1784 -0
  145. package/out/views/OAuthPage.d.ts +7 -0
  146. package/out/views/OAuthPage.js +17 -0
  147. package/out/views/SubscriptionPage.d.ts +7 -0
  148. package/out/views/SubscriptionPage.js +26 -0
  149. package/out/views/index.d.ts +13 -0
  150. package/out/views/index.js +25 -0
  151. package/out/views/layout/Head.d.ts +9 -0
  152. package/out/views/layout/Head.js +54 -0
  153. package/package.json +33 -18
  154. package/out/util/handlebars.d.ts +0 -1
  155. package/out/util/handlebars.js +0 -46
  156. package/out/views/about.handlebars +0 -102
  157. package/out/views/error.handlebars +0 -54
  158. package/out/views/form.handlebars +0 -31
  159. package/out/views/install.handlebars +0 -16
  160. package/out/views/login.handlebars +0 -332
  161. package/out/views/main.handlebars +0 -2042
  162. package/out/views/oauth.handlebars +0 -11
  163. package/out/views/partials/head.handlebars +0 -53
  164. package/out/views/subscription.handlebars +0 -26
@@ -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);
@@ -42,6 +41,7 @@ const logger_1 = require("../util/logger");
42
41
  const mysql_1 = require("./mysql");
43
42
  const postgre_1 = require("./postgre");
44
43
  const sqlite_1 = require("./sqlite");
44
+ const d1_1 = require("./d1");
45
45
  const path_1 = __importStar(require("path"));
46
46
  const fs_1 = __importDefault(require("fs"));
47
47
  const child_process_1 = require("child_process");
@@ -63,7 +63,13 @@ exports.TABLES = {
63
63
  let storage;
64
64
  function initialize(config) {
65
65
  return __awaiter(this, void 0, void 0, function* () {
66
- if (config.postgreConfig) {
66
+ if (config.d1Config) {
67
+ (0, logger_1.log)('Using Cloudflare D1 database');
68
+ storage = new d1_1.D1Storage(config.d1Config);
69
+ // Skip migration for D1 - it will be done lazily on first request
70
+ return;
71
+ }
72
+ else if (config.postgreConfig) {
67
73
  (0, logger_1.log)('Using PostgreSQL database');
68
74
  let dumpDirectory = null;
69
75
  if (config.migrateToPostgreFromSQLite) {
@@ -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/types.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
1
3
  import Crowdin from '@crowdin/crowdin-api-client';
2
4
  import { JwtPayload, VerifyOptions } from '@crowdin/crowdin-apps-functions';
3
5
  import { Request } from 'express';
@@ -10,6 +12,8 @@ import { IntegrationLogic } from './modules/integration/types';
10
12
  import { Storage } from './storage';
11
13
  import { MySQLStorageConfig } from './storage/mysql';
12
14
  import { PostgreStorageConfig } from './storage/postgre';
15
+ import { D1StorageConfig } from './storage/d1';
16
+ import type { Fetcher } from '@cloudflare/workers-types';
13
17
  import { ApiModule } from './modules/api/types';
14
18
  import { LogErrorFunction, LogFunction } from './util/logger';
15
19
  import { AiProviderModule } from './modules/ai-provider/types';
@@ -21,6 +25,8 @@ import { WorkflowStepTypeModule } from './modules/workflow-step-type/types';
21
25
  import { AiRequestProcessorModule, AiStreamProcessorModule } from './modules/ai-request-processors/types';
22
26
  import { AutomationActionModule } from './modules/automation-action/types';
23
27
  import { AuthGuardModule } from './modules/auth-guard/types';
28
+ import { Cron } from './util/cron';
29
+ export { Cron };
24
30
  export interface ClientConfig extends ImagePath {
25
31
  /**
26
32
  * Authentication Crowdin App type: "authorization_code", "crowdin_app", "crowdin_agent". Default: "crowdin_app"
@@ -102,6 +108,20 @@ export interface ClientConfig extends ImagePath {
102
108
  * config to configure MySQL as a storage
103
109
  */
104
110
  mysqlConfig?: MySQLStorageConfig;
111
+ /**
112
+ * config to configure Cloudflare D1 as a storage
113
+ */
114
+ d1Config?: D1StorageConfig;
115
+ /**
116
+ * Custom cron for scheduling background jobs.
117
+ * If not provided, the default node-cron will be used.
118
+ * Useful for external cron systems like Cloudflare Workers Scheduled Events.
119
+ */
120
+ cron?: Cron;
121
+ /**
122
+ * Cloudflare Workers Assets configuration
123
+ */
124
+ assetsConfig?: AssetsConfig;
105
125
  /**
106
126
  * integration module logic
107
127
  */
@@ -277,13 +297,27 @@ export interface ClientConfig extends ImagePath {
277
297
  * Auth guard module for custom authentication/authorization checks
278
298
  */
279
299
  authGuard?: AuthGuardModule | AuthGuardModule[];
300
+ /**
301
+ * custom file storage
302
+ */
303
+ fileStore?: FileStore;
304
+ /**
305
+ * path to assets files directory (default: 'static')
306
+ */
307
+ assetsPath?: string;
308
+ }
309
+ export interface AssetsConfig {
310
+ /**
311
+ * Cloudflare Workers Assets Fetcher
312
+ */
313
+ fetcher: Fetcher;
280
314
  }
281
315
  export interface Environments {
282
316
  environments?: Environment | Environment[];
283
317
  }
284
318
  type Environment = 'crowdin' | 'crowdin-enterprise';
285
319
  export type Config = ClientConfig & {
286
- baseUrl: string;
320
+ baseUrl?: string;
287
321
  clientId: string;
288
322
  clientSecret: string;
289
323
  port: number;
@@ -408,6 +442,7 @@ export interface CrowdinAppUtilities extends CrowdinMetadataStore {
408
442
  extra: Record<string, any>;
409
443
  }>;
410
444
  storage: Storage;
445
+ cron: Cron;
411
446
  }
412
447
  export interface CrowdinMetadataStore {
413
448
  saveMetadata: (id: string, metadata: any, crowdinId: string) => Promise<void>;
@@ -522,4 +557,8 @@ export interface ModalModule extends ModuleContent, UiModule, Environments {
522
557
  }
523
558
  export interface ContextModule extends ContextContent, UiModule, Environments {
524
559
  }
525
- export {};
560
+ export interface FileStore {
561
+ getFile: (fileRef: string) => Promise<Buffer>;
562
+ storeFile: (content: Buffer) => Promise<string>;
563
+ deleteFile: (fileRef: string) => Promise<void>;
564
+ }
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) {
@@ -6,5 +6,5 @@ declare function getRequestCredentialsMasker({ moduleConfig, dataPath, }: {
6
6
  moduleConfig?: UiModule;
7
7
  dataPath?: string;
8
8
  }): (req: Request | CrowdinClientRequest, res: Response, next: Function) => any;
9
- declare function postRequestCredentialsMasker(moduleConfig?: UiModule, credentialsExtractor?: Function): (req: 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;
9
+ declare function postRequestCredentialsMasker(moduleConfig?: UiModule, credentialsExtractor?: Function): (req: 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;
10
10
  export { getRequestCredentialsMasker, postRequestCredentialsMasker, maskKey };
@@ -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;
@@ -1,16 +1,27 @@
1
1
  import { Request, Response } from 'express';
2
2
  import { Config, CrowdinClientRequest, ImagePath, UnauthorizedConfig } from '../types';
3
+ export { renderJSX } from './jsx-renderer';
3
4
  export declare class CodeError extends Error {
4
5
  code: number | undefined;
5
6
  constructor(message: string, code?: number);
6
7
  }
8
+ export declare function extractBaseUrlFromRequest(req: Request): string;
7
9
  export declare function runAsyncWrapper(callback: Function): (req: Request | CrowdinClientRequest, res: Response, next: Function) => void;
8
10
  export declare function encryptData(config: Config, data: string): string;
9
11
  export declare function decryptData(config: Config, data: string): string;
10
12
  export declare function executeWithRetry<T>(func: () => Promise<T>, numOfRetries?: number): Promise<T>;
11
- export declare function getLogoUrl(moduleConfig?: ImagePath, modulePath?: string): string;
13
+ export declare function getLogoUrl(config: Config | UnauthorizedConfig, moduleConfig?: ImagePath, modulePath?: string): string;
14
+ /**
15
+ * Logo middleware with backwards compatibility
16
+ * Serves both /logo.png (backwards-compatible) and actual file name
17
+ * @param config - App configuration (required for Cloudflare Workers Assets support)
18
+ * @param moduleConfig - Module configuration with imagePath (optional, falls back to config.imagePath)
19
+ * @returns Express middleware
20
+ */
21
+ export declare function serveLogo(config: Config | UnauthorizedConfig, moduleConfig?: ImagePath): (req: Request, res: Response, next: Function) => Promise<void>;
12
22
  export declare function isAuthorizedConfig(config: Config | UnauthorizedConfig): config is Config;
13
23
  export declare function isJson(string: string): boolean;
24
+ export declare function isDefined(value: any): boolean;
14
25
  export declare function getPreviousDate(days: number): Date;
15
26
  export declare function prepareFormDataMetadataId(req: CrowdinClientRequest, config: Config): Promise<string>;
16
27
  export declare function validateEmail(email: string | number): boolean;
package/out/util/index.js CHANGED
@@ -32,12 +32,25 @@ 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.serveLogo = exports.getLogoUrl = exports.executeWithRetry = exports.decryptData = exports.encryptData = exports.runAsyncWrapper = exports.extractBaseUrlFromRequest = exports.CodeError = exports.renderJSX = void 0;
36
36
  const crypto = __importStar(require("crypto-js"));
37
37
  const storage_1 = require("../storage");
38
38
  const types_1 = require("../types");
39
39
  const logger_1 = require("./logger");
40
40
  const crowdin_apps_functions_1 = require("@crowdin/crowdin-apps-functions");
41
+ const jsx_renderer_1 = require("./jsx-renderer");
42
+ const views_1 = require("../views");
43
+ const static_files_1 = require("./static-files");
44
+ var jsx_renderer_2 = require("./jsx-renderer");
45
+ Object.defineProperty(exports, "renderJSX", { enumerable: true, get: function () { return jsx_renderer_2.renderJSX; } });
46
+ /**
47
+ * Extract file name from path (works in both Node.js and Cloudflare Workers)
48
+ * Supports both Unix (/) and Windows (\) path separators
49
+ */
50
+ function basename(filePath) {
51
+ // Replace all backslashes with forward slashes, then get the last segment
52
+ return filePath.replace(/\\/g, '/').split('/').pop();
53
+ }
41
54
  class CodeError extends Error {
42
55
  constructor(message, code) {
43
56
  super(message);
@@ -45,6 +58,12 @@ class CodeError extends Error {
45
58
  }
46
59
  }
47
60
  exports.CodeError = CodeError;
61
+ function extractBaseUrlFromRequest(req) {
62
+ const protocol = req.protocol;
63
+ const host = req.get('host');
64
+ return `${protocol}://${host}`;
65
+ }
66
+ exports.extractBaseUrlFromRequest = extractBaseUrlFromRequest;
48
67
  function isCrowdinClientRequest(req) {
49
68
  return req.crowdinContext;
50
69
  }
@@ -61,7 +80,9 @@ function handleError(err, req, res) {
61
80
  if (!res.headersSent) {
62
81
  const errorMessage = { message: (0, logger_1.getErrorMessage)(err), code };
63
82
  if ('integrationCredentials' in req) {
64
- res.render('error', errorMessage);
83
+ const html = (0, jsx_renderer_1.renderJSX)(views_1.ErrorPage, errorMessage);
84
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
85
+ res.send(html);
65
86
  }
66
87
  else {
67
88
  res.status(code).send({ error: errorMessage });
@@ -108,13 +129,36 @@ function executeWithRetry(func, numOfRetries = 2) {
108
129
  });
109
130
  }
110
131
  exports.executeWithRetry = executeWithRetry;
111
- function getLogoUrl(moduleConfig, modulePath) {
112
- if (!moduleConfig && !modulePath) {
113
- return '/logo.png';
132
+ function getLogoUrl(config, moduleConfig, modulePath) {
133
+ // Extract file name from imagePath with fallback to config.imagePath
134
+ const imagePath = (moduleConfig === null || moduleConfig === void 0 ? void 0 : moduleConfig.imagePath) || config.imagePath;
135
+ const fileName = basename(imagePath);
136
+ if (!modulePath) {
137
+ return `/${fileName}`;
114
138
  }
115
- return `/logo${modulePath}/logo.png`;
139
+ return `/logo${modulePath}/${fileName}`;
116
140
  }
117
141
  exports.getLogoUrl = getLogoUrl;
142
+ /**
143
+ * Logo middleware with backwards compatibility
144
+ * Serves both /logo.png (backwards-compatible) and actual file name
145
+ * @param config - App configuration (required for Cloudflare Workers Assets support)
146
+ * @param moduleConfig - Module configuration with imagePath (optional, falls back to config.imagePath)
147
+ * @returns Express middleware
148
+ */
149
+ function serveLogo(config, moduleConfig) {
150
+ const imagePath = (moduleConfig === null || moduleConfig === void 0 ? void 0 : moduleConfig.imagePath) || config.imagePath;
151
+ const fileName = basename(imagePath);
152
+ const fileHandler = (0, static_files_1.serveFile)(config, imagePath);
153
+ return (req, res, next) => __awaiter(this, void 0, void 0, function* () {
154
+ // Match exact paths: /logo.png (backwards-compatible) or /{actual-file-name}
155
+ if (req.path === '/logo.png' || req.path === `/${fileName}`) {
156
+ return fileHandler(req, res, next);
157
+ }
158
+ next();
159
+ });
160
+ }
161
+ exports.serveLogo = serveLogo;
118
162
  function isAuthorizedConfig(config) {
119
163
  return !!config.clientId && !!config.clientSecret && config.authenticationType !== types_1.AuthenticationType.NONE;
120
164
  }
@@ -129,6 +173,10 @@ function isJson(string) {
129
173
  return true;
130
174
  }
131
175
  exports.isJson = isJson;
176
+ function isDefined(value) {
177
+ return value !== undefined && value !== null;
178
+ }
179
+ exports.isDefined = isDefined;
132
180
  function getPreviousDate(days) {
133
181
  const date = new Date();
134
182
  date.setDate(date.getDate() - days);
@@ -0,0 +1,6 @@
1
+ import type { FC } from 'react';
2
+ /**
3
+ * Renders a React component to HTML string
4
+ * Uses React Server-Side Rendering (renderToStaticMarkup)
5
+ */
6
+ export declare function renderJSX<P = any>(Component: FC<P>, props: P): string;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderJSX = void 0;
4
+ const server_1 = require("react-dom/server");
5
+ /**
6
+ * Renders a React component to HTML string
7
+ * Uses React Server-Side Rendering (renderToStaticMarkup)
8
+ */
9
+ function renderJSX(Component, props) {
10
+ const element = Component(props);
11
+ return '<!DOCTYPE html>' + (0, server_1.renderToStaticMarkup)(element);
12
+ }
13
+ exports.renderJSX = renderJSX;
@@ -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: {
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Static Files Abstraction Layer
3
+ * Dual compatibility: Node.js (Express) + Cloudflare Workers (Assets Fetcher)
4
+ */
5
+ /// <reference types="serve-static" />
6
+ import express, { Request, Response } from 'express';
7
+ import { Config, UnauthorizedConfig } from '../types';
8
+ /**
9
+ * Serve static directory with dual compatibility
10
+ * @param config - App configuration
11
+ * @param staticPath - Path to static directory
12
+ */
13
+ export declare function serveStatic(config: Config | UnauthorizedConfig, staticPath: string): ((req: Request, res: Response, next: Function) => Promise<void>) | import("serve-static").RequestHandler<express.Response<any, Record<string, any>>>;
14
+ /**
15
+ * Serve single file (e.g., logo.png, icons/star.svg) with dual compatibility
16
+ * @param config - App configuration
17
+ * @param filePath - Path to file
18
+ */
19
+ export declare function serveFile(config: Config | UnauthorizedConfig, filePath: string): (req: Request, res: Response, next: Function) => Promise<void>;
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ /**
3
+ * Static Files Abstraction Layer
4
+ * Dual compatibility: Node.js (Express) + Cloudflare Workers (Assets Fetcher)
5
+ */
6
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
7
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
8
+ return new (P || (P = Promise))(function (resolve, reject) {
9
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
10
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
11
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
12
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
13
+ });
14
+ };
15
+ var __importDefault = (this && this.__importDefault) || function (mod) {
16
+ return (mod && mod.__esModule) ? mod : { "default": mod };
17
+ };
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.serveFile = exports.serveStatic = void 0;
20
+ const express_1 = __importDefault(require("express"));
21
+ const path_1 = __importDefault(require("path"));
22
+ /**
23
+ * Proxy Workers Assets response to Express response
24
+ */
25
+ function proxyAssetsResponse(fetcher, assetPath, baseUrl, req, res, next) {
26
+ return __awaiter(this, void 0, void 0, function* () {
27
+ try {
28
+ if (assetPath === '/') {
29
+ return next();
30
+ }
31
+ const assetUrl = new URL(assetPath, baseUrl);
32
+ const response = yield fetcher.fetch(assetUrl.toString());
33
+ if (!response.ok) {
34
+ return next();
35
+ }
36
+ const body = yield response.arrayBuffer();
37
+ const contentType = response.headers.get('content-type');
38
+ if (contentType) {
39
+ res.setHeader('Content-Type', contentType);
40
+ }
41
+ res.send(Buffer.from(body));
42
+ }
43
+ catch (err) {
44
+ next(err);
45
+ }
46
+ });
47
+ }
48
+ /**
49
+ * Serve static directory with dual compatibility
50
+ * @param config - App configuration
51
+ * @param staticPath - Path to static directory
52
+ */
53
+ function serveStatic(config, staticPath) {
54
+ var _a;
55
+ if ((_a = config.assetsConfig) === null || _a === void 0 ? void 0 : _a.fetcher) {
56
+ const assetsFetcher = config.assetsConfig.fetcher;
57
+ return (req, res, next) => __awaiter(this, void 0, void 0, function* () {
58
+ const assetPath = `/${staticPath}/${req.path}`.replace(/\/+/g, '/');
59
+ yield proxyAssetsResponse(assetsFetcher, assetPath, config.baseUrl, req, res, next);
60
+ });
61
+ }
62
+ const rootDir = path_1.default.dirname(__dirname);
63
+ const absolutePath = path_1.default.isAbsolute(staticPath) ? staticPath : path_1.default.join(rootDir, staticPath);
64
+ return express_1.default.static(absolutePath);
65
+ }
66
+ exports.serveStatic = serveStatic;
67
+ /**
68
+ * Serve single file (e.g., logo.png, icons/star.svg) with dual compatibility
69
+ * @param config - App configuration
70
+ * @param filePath - Path to file
71
+ */
72
+ function serveFile(config, filePath) {
73
+ return (req, res, next) => __awaiter(this, void 0, void 0, function* () {
74
+ var _a;
75
+ if ((_a = config.assetsConfig) === null || _a === void 0 ? void 0 : _a.fetcher) {
76
+ const assetsFetcher = config.assetsConfig.fetcher;
77
+ const assetPath = filePath.startsWith('/') ? filePath : `/${filePath}`;
78
+ yield proxyAssetsResponse(assetsFetcher, assetPath, config.baseUrl, req, res, next);
79
+ }
80
+ else {
81
+ const rootDir = path_1.default.dirname(__dirname);
82
+ const absolutePath = path_1.default.isAbsolute(filePath) ? filePath : path_1.default.join(rootDir, filePath);
83
+ res.sendFile(absolutePath);
84
+ }
85
+ });
86
+ }
87
+ exports.serveFile = serveFile;
@@ -0,0 +1,10 @@
1
+ import { FC } from 'react';
2
+ interface AboutPageProps {
3
+ name: string;
4
+ logo: string;
5
+ manifest: string;
6
+ storeLink?: string;
7
+ showDebugLink?: boolean;
8
+ }
9
+ export declare const AboutPage: FC<AboutPageProps>;
10
+ export {};