@crowdin/app-project-module 0.68.1 → 0.70.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 (46) hide show
  1. package/out/index.js +21 -3
  2. package/out/modules/ai-provider/handlers/chat-completions.js +20 -21
  3. package/out/modules/ai-provider/handlers/get-model-list.js +3 -2
  4. package/out/modules/ai-provider/types.d.ts +112 -7
  5. package/out/modules/ai-provider/util/index.d.ts +5 -0
  6. package/out/modules/ai-provider/util/index.js +72 -1
  7. package/out/modules/api/api.js +4 -4
  8. package/out/modules/file-processing/handlers/pre-post-process.js +6 -0
  9. package/out/modules/file-processing/types.d.ts +16 -8
  10. package/out/modules/integration/util/cron.js +8 -5
  11. package/out/modules/integration/util/webhooks.js +5 -4
  12. package/out/modules/manifest.js +46 -0
  13. package/out/modules/organization-settings-menu/index.d.ts +6 -0
  14. package/out/modules/organization-settings-menu/index.js +18 -0
  15. package/out/modules/profile-settings-menu/index.d.ts +6 -0
  16. package/out/modules/profile-settings-menu/index.js +18 -0
  17. package/out/modules/webhooks/handlers/webhook-handler.d.ts +5 -0
  18. package/out/modules/webhooks/handlers/webhook-handler.js +78 -0
  19. package/out/modules/webhooks/index.d.ts +6 -0
  20. package/out/modules/webhooks/index.js +18 -0
  21. package/out/modules/webhooks/types.d.ts +294 -0
  22. package/out/modules/webhooks/types.js +2 -0
  23. package/out/modules/workflow-step-type/handlers/delete-step.d.ts +5 -0
  24. package/out/modules/workflow-step-type/handlers/delete-step.js +70 -0
  25. package/out/modules/workflow-step-type/handlers/step-settings-save.d.ts +5 -0
  26. package/out/modules/workflow-step-type/handlers/step-settings-save.js +80 -0
  27. package/out/modules/workflow-step-type/index.d.ts +6 -0
  28. package/out/modules/workflow-step-type/index.js +36 -0
  29. package/out/modules/workflow-step-type/types.d.ts +57 -0
  30. package/out/modules/workflow-step-type/types.js +2 -0
  31. package/out/modules/workflow-step-type/util/index.d.ts +3 -0
  32. package/out/modules/workflow-step-type/util/index.js +16 -0
  33. package/out/static/js/form.js +32 -32
  34. package/out/static/js/main.js +2 -2
  35. package/out/storage/index.d.ts +1 -0
  36. package/out/storage/mysql.d.ts +1 -0
  37. package/out/storage/mysql.js +9 -0
  38. package/out/storage/postgre.d.ts +1 -0
  39. package/out/storage/postgre.js +9 -0
  40. package/out/storage/sqlite.d.ts +1 -0
  41. package/out/storage/sqlite.js +3 -0
  42. package/out/types.d.ts +19 -1
  43. package/out/util/logger.d.ts +5 -2
  44. package/out/util/logger.js +26 -10
  45. package/out/views/main.handlebars +48 -24
  46. package/package.json +16 -8
package/out/index.js CHANGED
@@ -74,11 +74,15 @@ const fileProcessingApps = __importStar(require("./modules/file-processing"));
74
74
  const integrationApp = __importStar(require("./modules/integration"));
75
75
  const modalApp = __importStar(require("./modules/modal"));
76
76
  const organizationMenuApp = __importStar(require("./modules/organization-menu"));
77
+ const organizationSettingsMenuApp = __importStar(require("./modules/organization-settings-menu"));
77
78
  const profileResourcesMenuApp = __importStar(require("./modules/profile-resources-menu"));
79
+ const profileSettingsMenuApp = __importStar(require("./modules/profile-settings-menu"));
78
80
  const projectMenuApp = __importStar(require("./modules/project-menu"));
79
81
  const projectMenuCrowdsourceApp = __importStar(require("./modules/project-menu-crowdsource"));
80
82
  const projectReportsApp = __importStar(require("./modules/project-reports"));
81
83
  const projectToolsApp = __importStar(require("./modules/project-tools"));
84
+ const webhooks = __importStar(require("./modules/webhooks"));
85
+ const workflowStepType = __importStar(require("./modules/workflow-step-type"));
82
86
  const subscription_1 = require("./util/subscription");
83
87
  var types_2 = require("./types");
84
88
  Object.defineProperty(exports, "ProjectPermissions", { enumerable: true, get: function () { return types_2.ProjectPermissions; } });
@@ -122,7 +126,14 @@ function addCrowdinEndpoints(app, clientConfig) {
122
126
  }
123
127
  storage.initialize(config);
124
128
  logger.initialize(config);
125
- app.use(terminus_express_1.default.json({ limit: '50mb' }));
129
+ app.use((req, res, next) => {
130
+ if (config.webhooks && req.path === '/webhooks') {
131
+ return terminus_express_1.default.raw({ type: '*/*', limit: '50mb' })(req, res, next);
132
+ }
133
+ else {
134
+ return terminus_express_1.default.json({ limit: '50mb' })(req, res, next);
135
+ }
136
+ });
126
137
  if (!config.disableLogsFormatter) {
127
138
  logsFormatter.setup();
128
139
  app.use(logsFormatter.contextResolverMiddleware());
@@ -141,7 +152,9 @@ function addCrowdinEndpoints(app, clientConfig) {
141
152
  editorRightPanelApp.register({ config, app });
142
153
  projectMenuApp.register({ config, app });
143
154
  profileResourcesMenuApp.register({ config, app });
155
+ profileSettingsMenuApp.register({ config, app });
144
156
  organizationMenuApp.register({ config, app });
157
+ organizationSettingsMenuApp.register({ config, app });
145
158
  projectMenuCrowdsourceApp.register({ config, app });
146
159
  projectToolsApp.register({ config, app });
147
160
  projectReportsApp.register({ config, app });
@@ -175,6 +188,8 @@ function addCrowdinEndpoints(app, clientConfig) {
175
188
  aiTools.registerAiTools({ config, app });
176
189
  aiTools.registerAiToolWidgets({ config, app });
177
190
  externalQaCheck.register({ config, app });
191
+ webhooks.register({ config, app });
192
+ workflowStepType.register({ config, app });
178
193
  addFormSchema({ config, app });
179
194
  return Object.assign(Object.assign({}, exports.metadataStore), { establishCrowdinConnection: (authRequest, moduleKey) => {
180
195
  let jwtToken = '';
@@ -191,13 +206,13 @@ function addCrowdinEndpoints(app, clientConfig) {
191
206
  checkSubscriptionExpiration: false,
192
207
  moduleKey,
193
208
  });
194
- }, encryptCrowdinConnection: (data) => (0, util_1.encryptData)(config, JSON.stringify(data)), dencryptCrowdinConnection: (hash) => __awaiter(this, void 0, void 0, function* () {
209
+ }, encryptCrowdinConnection: (data) => (0, util_1.encryptData)(config, JSON.stringify(data)), dencryptCrowdinConnection: (hash, autoRenew) => __awaiter(this, void 0, void 0, function* () {
195
210
  const { crowdinId, extra } = JSON.parse((0, util_1.decryptData)(config, hash));
196
211
  const credentials = yield storage.getStorage().getCrowdinCredentials(crowdinId);
197
212
  if (!credentials) {
198
213
  throw new Error('Failed to find Crowdin credentials');
199
214
  }
200
- const { client } = yield (0, connection_1.prepareCrowdinClient)({ config, credentials });
215
+ const { client } = yield (0, connection_1.prepareCrowdinClient)({ config, credentials, autoRenew });
201
216
  return { client, extra };
202
217
  }) });
203
218
  }
@@ -240,6 +255,9 @@ function convertClientConfig(clientConfig) {
240
255
  throw new Error('Missing [clientId, clientSecret] parameters');
241
256
  }
242
257
  }
258
+ if (clientConfig.projectIntegration) {
259
+ clientConfig.api = Object.assign({ default: true }, clientConfig.api);
260
+ }
243
261
  return Object.assign(Object.assign({}, clientConfig), { baseUrl: baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl, clientId,
244
262
  clientSecret, awsConfig: {
245
263
  tmpBucketName,
@@ -13,12 +13,7 @@ const util_1 = require("../../../util");
13
13
  const logger_1 = require("../../../util/logger");
14
14
  const files_1 = require("../../integration/util/files");
15
15
  const util_2 = require("../util");
16
- // more about roles in AI messages https://platform.openai.com/docs/api-reference/chat/create#chat-create-messages
17
- // Crowdin documentation https://support.crowdin.com/developer/crowdin-apps-module-custom-ai/#expected-response-from-the-app
18
- const ROLES = {
19
- assistant: 'assistant',
20
- };
21
- const DEFAULT_ROLE = ROLES.assistant;
16
+ const DEFAULT_ROLE = 'assistant';
22
17
  const getErrorStatus = (e) => {
23
18
  if (typeof e === 'object' && 'status' in e && typeof e.status === 'number') {
24
19
  return e.status;
@@ -39,7 +34,7 @@ function handle(aiProvider) {
39
34
  let message;
40
35
  let data;
41
36
  const chunks = [];
42
- const { crowdinApiClient: client, crowdinContext: context, body: { messages, model, action, stream, responseFormat: { type: responseFormatType } = { type: undefined }, }, } = req;
37
+ const { crowdinApiClient: client, crowdinContext: context, body: { messages, model, action, stream, tools, tool_choice: toolChoice, responseFormat: { type: responseFormatType } = { type: undefined }, }, } = req;
43
38
  isStream = !!stream;
44
39
  const startStream = () => {
45
40
  if (!res.headersSent) {
@@ -47,11 +42,12 @@ function handle(aiProvider) {
47
42
  Connection: 'keep-alive',
48
43
  'Cache-Control': 'no-cache',
49
44
  'Content-Type': 'text/event-stream',
45
+ 'X-Accel-Buffering': 'no',
50
46
  });
51
47
  }
52
48
  };
53
49
  const result = yield aiProvider.chatCompletions({
54
- messages,
50
+ messages: messages.map(util_2.inputMessageToChatCompletionMessage),
55
51
  model,
56
52
  action,
57
53
  responseFormat: responseFormatType,
@@ -59,19 +55,26 @@ function handle(aiProvider) {
59
55
  context,
60
56
  req,
61
57
  isStream,
62
- sendEvent: ({ content, role }) => __awaiter(this, void 0, void 0, function* () {
58
+ tools,
59
+ toolChoice,
60
+ /* eslint-disable @typescript-eslint/camelcase */
61
+ sendEvent: ({ content, role, tool_calls }) => __awaiter(this, void 0, void 0, function* () {
63
62
  if (!isStream) {
64
- chunks.push({ content, role });
63
+ chunks.push({ content, role, tool_calls });
65
64
  return;
66
65
  }
67
66
  startStream();
67
+ const delta = {
68
+ role: role || DEFAULT_ROLE,
69
+ content: content || null,
70
+ };
71
+ if (!!tool_calls) {
72
+ delta.tool_calls = tool_calls;
73
+ }
68
74
  const message = {
69
75
  choices: [
70
76
  {
71
- delta: {
72
- role: role || DEFAULT_ROLE,
73
- content,
74
- },
77
+ delta,
75
78
  },
76
79
  ],
77
80
  };
@@ -92,18 +95,14 @@ function handle(aiProvider) {
92
95
  data = result;
93
96
  }
94
97
  if (chunks.length) {
95
- data = [
96
- {
97
- role: chunks[0].role || DEFAULT_ROLE,
98
- content: chunks.map((chunk) => chunk.content).join(''),
99
- },
100
- ];
98
+ data = (0, util_2.mergeChatCompletionChunks)(chunks);
101
99
  }
102
100
  if (data && data.length > 0) {
103
101
  choices = data.map((message) => ({
104
102
  message: {
105
103
  role: message.role || DEFAULT_ROLE,
106
- content: message.content,
104
+ content: message.content || null,
105
+ tool_calls: message.tool_calls,
107
106
  },
108
107
  }));
109
108
  }
@@ -22,13 +22,14 @@ function handle(aiProvider) {
22
22
  context: req.crowdinContext,
23
23
  });
24
24
  const data = modelList.map((model) => {
25
- var _a, _b, _c, _d;
25
+ var _a, _b, _c, _d, _e;
26
26
  return ({
27
27
  id: model.id,
28
28
  supportsJsonMode: (_a = model.supportsJsonMode) !== null && _a !== void 0 ? _a : false,
29
29
  supportsFunctionCalling: (_b = model.supportsFunctionCalling) !== null && _b !== void 0 ? _b : false,
30
30
  supportsStreaming: (_c = model.supportsStreaming) !== null && _c !== void 0 ? _c : false,
31
- contextWindowLimit: (_d = model.contextWindowLimit) !== null && _d !== void 0 ? _d : exports.CONTEXT_WINDOW_LIMIT,
31
+ supportsVision: (_d = model.supportsVision) !== null && _d !== void 0 ? _d : false,
32
+ contextWindowLimit: (_e = model.contextWindowLimit) !== null && _e !== void 0 ? _e : exports.CONTEXT_WINDOW_LIMIT,
32
33
  });
33
34
  });
34
35
  res.send({ data });
@@ -17,8 +17,8 @@ export interface AiProviderModule extends Environments, ModuleKey {
17
17
  /**
18
18
  * processes a sequence of conversation messages and generates responses from the assistant
19
19
  */
20
- chatCompletions: ({ messages, model, action, responseFormat, client, context, req, }: {
21
- messages: Message[];
20
+ chatCompletions: ({ messages, model, action, responseFormat, client, context, req, isStream, sendEvent, tools, toolChoice, }: {
21
+ messages: ChatCompletionMessage[];
22
22
  model: string;
23
23
  action: string;
24
24
  responseFormat: string;
@@ -26,8 +26,10 @@ export interface AiProviderModule extends Environments, ModuleKey {
26
26
  context: CrowdinContextInfo;
27
27
  req: CrowdinClientRequest;
28
28
  isStream: boolean;
29
- sendEvent: (chunk: Message) => Promise<void>;
30
- }) => Promise<Message[] | ExtendedResult<Message[]>>;
29
+ sendEvent: (chunk: ChatCompletionChunkMessage) => Promise<void>;
30
+ tools?: ChatCompletionTool[];
31
+ toolChoice?: string | AiToolChoice;
32
+ }) => Promise<ChatCompletionResponseMessage[] | ExtendedResult<ChatCompletionResponseMessage[]> | void>;
31
33
  /**
32
34
  * provides a list of available model
33
35
  */
@@ -41,9 +43,112 @@ export interface SupportedModels {
41
43
  supportsJsonMode?: boolean;
42
44
  supportsFunctionCalling?: boolean;
43
45
  supportsStreaming?: boolean;
46
+ supportsVision?: boolean;
44
47
  contextWindowLimit?: number;
45
48
  }
46
- export interface Message {
47
- role?: string;
48
- content: string;
49
+ export interface ChatCompletionTool {
50
+ type: 'function';
51
+ function: ChatCompletionToolFunctionDeclaration;
49
52
  }
53
+ export interface ChatCompletionToolFunctionDeclaration {
54
+ /**
55
+ * The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.
56
+ */
57
+ name: string;
58
+ /**
59
+ * A description of what the function does, used by the model to choose when and how to call the function.
60
+ */
61
+ description?: string;
62
+ /**
63
+ * The parameters the functions accepts, described as a JSON Schema object.
64
+ * https://json-schema.org/understanding-json-schema/
65
+ */
66
+ parameters?: object;
67
+ }
68
+ export interface AiToolChoice {
69
+ type: 'function';
70
+ function: {
71
+ /**
72
+ * The name of the function to call.
73
+ */
74
+ name: string;
75
+ };
76
+ }
77
+ export type ChatCompletionMessage = ChatCompletionSystemMessage | ChatCompletionUserMessage | ChatCompletionAssistantMessage | ChatCompletionToolMessage;
78
+ export interface ChatCompletionSystemMessage {
79
+ role: 'system';
80
+ content: string | ChatCompletionContentPartText[];
81
+ }
82
+ export interface ChatCompletionUserMessage {
83
+ role?: 'user';
84
+ content: string | ChatCompletionContentPart[];
85
+ }
86
+ export type ChatCompletionResponseMessage = ChatCompletionAssistantMessage;
87
+ export interface ChatCompletionAssistantMessage {
88
+ role?: 'assistant';
89
+ content?: string | ChatCompletionContentPartText[] | null;
90
+ tool_calls?: ChatCompletionMessageToolCall[] | null;
91
+ }
92
+ export interface ChatCompletionChunkMessage {
93
+ role?: ROLES;
94
+ content?: string | null;
95
+ tool_calls?: ChatCompletionDeltaMessageToolCall[];
96
+ }
97
+ export interface ChatCompletionToolMessage {
98
+ content: string | ChatCompletionContentPartText[];
99
+ role: 'tool';
100
+ /**
101
+ * Tool call that this message is responding to.
102
+ */
103
+ tool_call_id: string;
104
+ }
105
+ export type ROLES = 'user' | 'assistant' | 'system' | 'tool';
106
+ export type ChatCompletionContentPart = ChatCompletionContentPartText | ChatCompletionContentPartImage;
107
+ export type ChatCompletionContentPartText = {
108
+ type: 'text';
109
+ text: string;
110
+ };
111
+ export type ChatCompletionContentPartImage = {
112
+ type: 'image_url';
113
+ image_url: {
114
+ /**
115
+ * Either a URL of the image or the base64 encoded image data.
116
+ */
117
+ url: string;
118
+ };
119
+ };
120
+ export interface ChatCompletionMessageToolCall {
121
+ id: string;
122
+ type: 'function';
123
+ function: {
124
+ /**
125
+ * The arguments to call the function with, as generated by the model in JSON format.
126
+ * Note that the model does not always generate valid JSON.
127
+ */
128
+ arguments?: string;
129
+ name: string;
130
+ };
131
+ }
132
+ export interface ChatCompletionDeltaMessageToolCall {
133
+ index: number;
134
+ id?: string;
135
+ type?: 'function';
136
+ function?: {
137
+ /**
138
+ * The arguments to call the function with, as generated by the model in JSON format.
139
+ * Note that the model does not always generate valid JSON.
140
+ */
141
+ arguments?: string;
142
+ name?: string;
143
+ };
144
+ }
145
+ export interface InputMessage {
146
+ role?: ROLES;
147
+ content: string | InputContentPart[];
148
+ }
149
+ export type InputContentPart = ChatCompletionContentPartText | InputChatCompletionContentPartImage;
150
+ export type InputChatCompletionContentPartImage = {
151
+ type: 'image';
152
+ mimeType: string;
153
+ url: string;
154
+ };
@@ -1,4 +1,5 @@
1
1
  import { AppModuleAggregateError } from '../../../util/logger';
2
+ import { ChatCompletionChunkMessage, ChatCompletionContentPart, ChatCompletionMessage, ChatCompletionMessageToolCall, ChatCompletionResponseMessage, InputContentPart, InputMessage } from '../types';
2
3
  interface RateLimitErrorOptions {
3
4
  error?: Error;
4
5
  message?: string;
@@ -8,4 +9,8 @@ export declare class RateLimitError extends AppModuleAggregateError {
8
9
  readonly status = 429;
9
10
  constructor({ error, message }?: RateLimitErrorOptions);
10
11
  }
12
+ export declare function normalizeContentParts(content: string | InputContentPart[]): string | ChatCompletionContentPart[];
13
+ export declare function inputMessageToChatCompletionMessage(message: InputMessage): ChatCompletionMessage;
14
+ export declare function mergeToolCalls(chunks: ChatCompletionChunkMessage[]): ChatCompletionMessageToolCall[] | null;
15
+ export declare function mergeChatCompletionChunks(chunks: ChatCompletionChunkMessage[]): ChatCompletionResponseMessage[];
11
16
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RateLimitError = exports.isRateLimitError = void 0;
3
+ exports.mergeChatCompletionChunks = exports.mergeToolCalls = exports.inputMessageToChatCompletionMessage = exports.normalizeContentParts = exports.RateLimitError = exports.isRateLimitError = void 0;
4
4
  const logger_1 = require("../../../util/logger");
5
5
  const HTTP_RATE_LIMIT = 429;
6
6
  function isRateLimitError(e) {
@@ -19,3 +19,74 @@ class RateLimitError extends logger_1.AppModuleAggregateError {
19
19
  }
20
20
  }
21
21
  exports.RateLimitError = RateLimitError;
22
+ function normalizeContentParts(content) {
23
+ return Array.isArray(content)
24
+ ? content.map((part) => part.type === 'image'
25
+ ? {
26
+ type: 'image_url',
27
+ // eslint-disable-next-line @typescript-eslint/camelcase
28
+ image_url: {
29
+ url: part.url,
30
+ },
31
+ }
32
+ : part)
33
+ : content;
34
+ }
35
+ exports.normalizeContentParts = normalizeContentParts;
36
+ function inputMessageToChatCompletionMessage(message) {
37
+ switch (message.role) {
38
+ case undefined:
39
+ case 'user':
40
+ return {
41
+ role: 'user',
42
+ content: normalizeContentParts(message.content),
43
+ };
44
+ default:
45
+ return message;
46
+ }
47
+ }
48
+ exports.inputMessageToChatCompletionMessage = inputMessageToChatCompletionMessage;
49
+ function mergeToolCalls(chunks) {
50
+ const toolCalls = [];
51
+ chunks.forEach((chunk) => {
52
+ if (!chunk.tool_calls) {
53
+ return;
54
+ }
55
+ chunk.tool_calls.forEach((toolCallChunk) => {
56
+ const delta = toolCalls.find((toolCall) => toolCall.index === toolCallChunk.index);
57
+ if (!delta) {
58
+ toolCalls.push(toolCallChunk);
59
+ }
60
+ else if (!!toolCallChunk.function && 'arguments' in toolCallChunk.function) {
61
+ if (!delta.function) {
62
+ delta.function = toolCallChunk.function;
63
+ }
64
+ else if (!delta.function.arguments) {
65
+ delta.function.arguments = toolCallChunk.function.arguments;
66
+ }
67
+ else {
68
+ delta.function.arguments += toolCallChunk.function.arguments;
69
+ }
70
+ }
71
+ });
72
+ });
73
+ toolCalls.map((tool) => {
74
+ if ('index' in tool) {
75
+ delete tool.index;
76
+ }
77
+ });
78
+ return !toolCalls.length ? null : toolCalls;
79
+ }
80
+ exports.mergeToolCalls = mergeToolCalls;
81
+ function mergeChatCompletionChunks(chunks) {
82
+ /* eslint-disable @typescript-eslint/camelcase */
83
+ const tool_calls = mergeToolCalls(chunks);
84
+ return [
85
+ {
86
+ role: chunks[0].role || 'assistant',
87
+ content: tool_calls ? null : chunks.map((chunk) => chunk.content).join(''),
88
+ tool_calls,
89
+ },
90
+ ];
91
+ }
92
+ exports.mergeChatCompletionChunks = mergeChatCompletionChunks;
@@ -462,7 +462,7 @@ function addDefaultApiEndpoints(app, config) {
462
462
  }
463
463
  exports.addDefaultApiEndpoints = addDefaultApiEndpoints;
464
464
  function addSwagerApiDocumentation(app, config) {
465
- var _a, _b;
465
+ var _a, _b, _c;
466
466
  const options = {
467
467
  swaggerDefinition: {
468
468
  openapi: '3.0.0',
@@ -492,16 +492,16 @@ function addSwagerApiDocumentation(app, config) {
492
492
  },
493
493
  ],
494
494
  },
495
- apis: config.projectIntegration
495
+ apis: config.projectIntegration && ((_a = config.api) === null || _a === void 0 ? void 0 : _a.default)
496
496
  ? [path_1.default.resolve(__dirname, './base.js'), path_1.default.resolve(__dirname, './components.js'), __filename]
497
497
  : [],
498
498
  };
499
- if ((_a = config.api) === null || _a === void 0 ? void 0 : _a.docFile) {
499
+ if ((_b = config.api) === null || _b === void 0 ? void 0 : _b.docFile) {
500
500
  options.apis.push(config.api.docFile);
501
501
  }
502
502
  const swaggerSpec = (0, swagger_jsdoc_1.default)(options);
503
503
  // remove Login info from doc
504
- if (config.projectIntegration && !((_b = config.projectIntegration) === null || _b === void 0 ? void 0 : _b.loginForm)) {
504
+ if (config.projectIntegration && !((_c = config.projectIntegration) === null || _c === void 0 ? void 0 : _c.loginForm)) {
505
505
  delete swaggerSpec.paths['/login'];
506
506
  delete swaggerSpec.paths['/login-fields'];
507
507
  delete swaggerSpec.components.schemas['Login'];
@@ -23,9 +23,15 @@ function handle(baseConfig, config, folderName) {
23
23
  fs_1.default.mkdirSync(path_1.default.join(folderPath, folderName), { recursive: true });
24
24
  }
25
25
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
26
+ var _a;
26
27
  const response = {};
27
28
  const baseFilesUrl = `${baseConfig.baseUrl}/file/download/${folderName}`;
28
29
  const body = req.body;
30
+ // Skip assets (e.g., images, videos, or other media files) as they typically don't need to be processed
31
+ if (((_a = body === null || body === void 0 ? void 0 : body.file) === null || _a === void 0 ? void 0 : _a.type) === 'assets' && !('processAssets' in config && config.processAssets)) {
32
+ res.sendStatus(304);
33
+ return;
34
+ }
29
35
  let processingError;
30
36
  let fileContent;
31
37
  let rawContent;
@@ -60,17 +60,24 @@ export interface CustomFileFormatLogic extends FileProcessLogic {
60
60
  }
61
61
  export type FileImportExportLogic = FilePreImportLogic | FilePostImportLogic | FilePreExportLogic | FilePostExportLogic;
62
62
  export type FileImportExportContent = ProcessFileString[] | Buffer | undefined;
63
- export interface FilePreImportLogic extends FileProcessLogic {
64
- fileProcess: (req: ProcessFileRequest, content: FileImportExportContent, client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<ContentFileResponse>;
63
+ export interface BaseFileProcessLogic<T> {
64
+ fileProcess: (req: ProcessFileRequest, content: FileImportExportContent, client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<T>;
65
65
  }
66
- export interface FilePostImportLogic extends FileProcessLogic {
67
- fileProcess: (req: ProcessFileRequest, content: FileImportExportContent, client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<StringsFileResponse>;
66
+ export interface FilePreImportLogic extends FileProcessLogic, BaseFileProcessLogic<ContentFileResponse> {
67
+ /**
68
+ * Set to `true` to enable asset processing in the application.
69
+ */
70
+ processAssets?: boolean;
71
+ }
72
+ export interface FilePostImportLogic extends FileProcessLogic, BaseFileProcessLogic<StringsFileResponse> {
68
73
  }
69
- export interface FilePreExportLogic extends FileProcessLogic {
70
- fileProcess: (req: ProcessFileRequest, content: FileImportExportContent, client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<StringsFileResponse>;
74
+ export interface FilePreExportLogic extends FileProcessLogic, BaseFileProcessLogic<StringsFileResponse> {
71
75
  }
72
- export interface FilePostExportLogic extends FileProcessLogic {
73
- fileProcess: (req: ProcessFileRequest, content: FileImportExportContent, client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<ContentFileResponse>;
76
+ export interface FilePostExportLogic extends FileProcessLogic, BaseFileProcessLogic<ContentFileResponse> {
77
+ /**
78
+ * Set to `true` to enable asset processing in the application.
79
+ */
80
+ processAssets?: boolean;
74
81
  }
75
82
  export interface ProcessFileRequest {
76
83
  jobType: ProcessFileJobType;
@@ -87,6 +94,7 @@ export interface ProcessFileRecord {
87
94
  path?: string;
88
95
  id?: number;
89
96
  name?: string;
97
+ type?: string;
90
98
  }
91
99
  export declare enum ProcessFileJobType {
92
100
  PARSE_FILE = "parse-file",
@@ -175,6 +175,8 @@ function processSyncSettings({ config, integration, period, syncSettings, }) {
175
175
  user_id: crowdinCredentials.userId,
176
176
  },
177
177
  },
178
+ crowdinId: crowdinCredentials.id,
179
+ clientId: integrationCredentials.id,
178
180
  };
179
181
  try {
180
182
  const preparedCrowdinClient = yield (0, connection_1.prepareCrowdinClient)({
@@ -187,7 +189,7 @@ function processSyncSettings({ config, integration, period, syncSettings, }) {
187
189
  crowdinClient = preparedCrowdinClient.client;
188
190
  }
189
191
  catch (e) {
190
- (0, logger_1.logError)(e);
192
+ (0, logger_1.logError)(e, context);
191
193
  return;
192
194
  }
193
195
  const { expired } = yield (0, subscription_1.checkSubscription)({
@@ -205,7 +207,7 @@ function processSyncSettings({ config, integration, period, syncSettings, }) {
205
207
  .data;
206
208
  }
207
209
  catch (e) {
208
- (0, logger_1.logError)(e);
210
+ (0, logger_1.logError)(e, context);
209
211
  return;
210
212
  }
211
213
  // eslint-disable-next-line @typescript-eslint/camelcase
@@ -229,7 +231,7 @@ function processSyncSettings({ config, integration, period, syncSettings, }) {
229
231
  });
230
232
  }
231
233
  catch (e) {
232
- (0, logger_1.logError)(e);
234
+ (0, logger_1.logError)(e, context);
233
235
  return;
234
236
  }
235
237
  }
@@ -268,6 +270,7 @@ function processSyncSettings({ config, integration, period, syncSettings, }) {
268
270
  crowdinClient,
269
271
  onlyApproved,
270
272
  onlyTranslated,
273
+ context,
271
274
  });
272
275
  if (Object.keys(filesToProcess).length <= 0) {
273
276
  return;
@@ -410,7 +413,7 @@ function getNewFoldersFile(folders, snapshotFiles) {
410
413
  files = files.filter((file) => 'type' in file);
411
414
  return files;
412
415
  }
413
- function getOnlyTranslatedOrApprovedFiles({ projectId, crowdinFiles, crowdinClient, onlyApproved, onlyTranslated, }) {
416
+ function getOnlyTranslatedOrApprovedFiles({ projectId, crowdinFiles, crowdinClient, onlyApproved, onlyTranslated, context, }) {
414
417
  return __awaiter(this, void 0, void 0, function* () {
415
418
  (0, logger_1.log)(`Filtering files to process only ${onlyApproved ? 'approved' : 'translated'} files`);
416
419
  const filesInfo = yield Promise.all(Object.keys(crowdinFiles).map((fileId) => __awaiter(this, void 0, void 0, function* () {
@@ -425,7 +428,7 @@ function getOnlyTranslatedOrApprovedFiles({ projectId, crowdinFiles, crowdinClie
425
428
  }
426
429
  catch (e) {
427
430
  delete crowdinFiles[fileId];
428
- (0, logger_1.logError)(e);
431
+ (0, logger_1.logError)(e, context);
429
432
  }
430
433
  })));
431
434
  const filteredFiles = {};
@@ -36,6 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  };
37
37
  Object.defineProperty(exports, "__esModule", { value: true });
38
38
  exports.listenQueueMessage = exports.updateCrowdinFromWebhookRequest = exports.prepareWebhookData = exports.unregisterAllCrowdinWebhooks = exports.unregisterWebhooks = exports.registerWebhooks = exports.HookEvents = void 0;
39
+ const logsFormatter = __importStar(require("@crowdin/logs-formatter"));
39
40
  const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
40
41
  const amqplib_1 = __importDefault(require("amqplib"));
41
42
  const types_1 = require("../types");
@@ -265,8 +266,7 @@ function prepareWebhookData({ config, integration, provider, webhookUrlParam, })
265
266
  if (!crowdinCredentials) {
266
267
  return { projectId, crowdinClient, rootFolder, appSettings, syncSettings, preparedIntegrationCredentials };
267
268
  }
268
- const context = {
269
- jwtPayload: {
269
+ const context = Object.assign({ jwtPayload: {
270
270
  context: {
271
271
  // eslint-disable-next-line @typescript-eslint/camelcase
272
272
  project_id: projectId,
@@ -275,8 +275,9 @@ function prepareWebhookData({ config, integration, provider, webhookUrlParam, })
275
275
  // eslint-disable-next-line @typescript-eslint/camelcase
276
276
  user_id: crowdinCredentials === null || crowdinCredentials === void 0 ? void 0 : crowdinCredentials.userId,
277
277
  },
278
- },
279
- };
278
+ }, crowdinId: crowdinCredentials.id }, ((integrationCredentials === null || integrationCredentials === void 0 ? void 0 : integrationCredentials.id) && { clientId: integrationCredentials.id }));
279
+ logsFormatter.resetContext();
280
+ logsFormatter.setContext(context);
280
281
  crowdinClient = yield (0, connection_1.prepareCrowdinClient)({
281
282
  config,
282
283
  credentials: crowdinCredentials,