Skip to content

Commit c5e92d8

Browse files
github-actions[bot]slaveekse11sytalygurynneSpecc
authored
Update prod (#483)
* feat(noti): added loop sender worker (#479) * feat(noti): added loop sender worker * fix tests * added numbers declension * fix lint * added examples for decl * Update workers/loop/src/provider.ts Co-authored-by: e11sy <130844513+e11sy@users.noreply.github.com> * Update workers/loop/src/provider.ts Co-authored-by: e11sy <130844513+e11sy@users.noreply.github.com> * fix tests --------- Co-authored-by: e11sy <130844513+e11sy@users.noreply.github.com> * fix(js): jsx plugin integration (#482) * fix(js): use babel parser plugins based on type extension * fix(): comments * fix(): duplicated variable * Update mail message for "blocked workspace" event (#478) * Localize block workspace email templates to Russian Translated block workspace email subject, text, and layout components from English to Russian to improve localization for Russian-speaking users. Updated button label and system name references accordingly. * Update block workspace email wording Rephrased notification messages in both HTML and text email templates for blocked workspaces to improve clarity and conciseness. * Update workers/email/src/templates/emails/block-workspace/html.twig Co-authored-by: Peter <specc.dev@gmail.com> * Update block workspace email templates wording Revised subject and body text in block workspace email templates for clarity and consistency. The new wording emphasizes that the workspace no longer receives events and that users are not tracking new errors due to plan limits or expiration. * Update button label based on tariff plan The button label in the block workspace email template now changes to 'Увеличить лимит от 99₽' if the workspace's tariffPlanId matches a specific value, otherwise it remains 'Открыть настройки'. * Fix spacing in button label currency symbol Added a space between the amount and the ₽ currency symbol in the button label for improved readability in the block workspace email template. * Fix punctuation in block workspace email template Added a missing period and line break to improve readability and clarity in the block workspace notification email template. * Improve block workspace email template formatting Replaces line breaks with paragraph tags for better readability and updates Twig syntax for tariff plan comparison in the button label. --------- Co-authored-by: Peter <specc.dev@gmail.com> --------- Co-authored-by: Vyacheslav Chernyshev <81693471+slaveeks@users.noreply.github.com> Co-authored-by: e11sy <130844513+e11sy@users.noreply.github.com> Co-authored-by: Taly <vitalik7tv@yandex.ru> Co-authored-by: Peter <specc.dev@gmail.com>
1 parent f49277e commit c5e92d8

File tree

6 files changed

+195
-26
lines changed

6 files changed

+195
-26
lines changed

workers/email/src/templates/components/layout.twig

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@
123123
style="text-decoration: none; border-bottom: 1px solid #494F5E;">
124124
<font color="#969FB3"
125125
style="font-size: 13px; line-height: 1.46; letter-spacing: 0.16px; color: #969FB3;">
126-
Unsubscribe
126+
Отписаться
127127
</font>
128128
</a>
129129
</td>
@@ -143,7 +143,7 @@
143143
<a href="{{ host }}" style="text-decoration: none;">
144144
<font color="#969FB3"
145145
style="font-size: 13px; line-height: 1.46; letter-spacing: 0.16px; color: #969FB3;">
146-
Hawk
146+
Хоук
147147
</font>
148148
</a>
149149
</td>
@@ -152,7 +152,7 @@
152152
<td style="text-align: center">
153153
<font color="#969FB3"
154154
style="font-size: 13px; line-height: 1.46; letter-spacing: 0.16px; color: #969FB3;">
155-
Errors tracking system
155+
Российский трекер ошибок
156156
</font>
157157
</td>
158158
</tr>

workers/email/src/templates/emails/block-workspace/html.twig

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,26 @@
1414
<td align="center" style="padding: 15px 0;">
1515
<font color="#dbe6ff" style="font-size: 15px; text-align: center; color: #dbe6ff; letter-spacing: 0.4px;">
1616
<span style="vertical-align: middle; display: inline-block;">
17-
Workspace was blocked
17+
«{{ workspace.name | escape }}» не принимает события
1818
</span>
1919
</font>
2020
</td>
2121
</tr>
2222
<tr>
2323
<td style="display: block; padding: 20px; margin-bottom: 30px; border-width: 1px; border-color: #494f5e; border-style: solid; border-radius: 10px; line-height: 1.47">
2424
<font color="#dbe6ff" style="font-size: 15px; letter-spacing: 0.4px;">
25-
Your workspace "{{ workspace.name | escape }}" was blocked because the plan was not renewed or events limit has been reached. Please, check payment settings and renew the plan.
25+
<p style="margin-top: 0;">
26+
Вы больше не отслеживаете новые ошибки, потому что закончился лимит или срок действия тарифного плана.
27+
</p>
28+
<p style="margin-bottom: 0;">
29+
Чтобы продолжить получать события, выберите подходящий тарифный план и продлите подписку в настройках оплаты.
30+
</p>
2631
</font>
2732
</td>
2833
</tr>
2934
<tr>
3035
<td style="padding-right: 20px; padding-left: 20px; padding-bottom: 40px;">
31-
{% include '../../components/button.twig' with {href: host ~ '/workspace/' ~ workspace._id ~ '/settings/billing', label: 'Go to payment settings'} %}
36+
{% include '../../components/button.twig' with {href: host ~ '/workspace/' ~ workspace._id ~ '/settings/billing', label: workspace.tariffPlanId is same as('5f47f031ff71510040f433c1') ? 'Увеличить лимит от 99 ₽' : 'Открыть настройки'} %}
3237
</td>
3338
</tr>
3439
{% endblock %}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Workspace {{ workspace.name | escape }} was blocked!
1+
Мониторинг ошибок остановлен
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
Your workspace "{{ workspace.name | escape }}" was blocked because the plan was not renewed or events limit has been reached.
1+
Мониторинг ошибок остановлен
22

3-
Please, check payment settings and renew the plan: {{ host }}/workspace/{{ workspace._id }}/settings/billing
3+
Вы больше не отслеживаете новые ошибки «{{ workspace.name | escape }}», потому что закончился лимит или срок действия тарифного плана
4+
5+
Чтобы продолжить получать события, выберите подходящий тарифный план и продлите подписку в настройках оплаты: {{ host }}/workspace/{{ workspace._id }}/settings/billing
46

57
***
68

7-
Hawk
8-
Errors tracking system
9+
Хоук
10+
Российский трекер ошибок
911

10-
Made by CodeX
12+
Made by CodeX

workers/javascript/src/index.ts

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { beautifyUserAgent } from './utils';
1414
import { Collection } from 'mongodb';
1515
import { parse } from '@babel/parser';
1616
import traverse from '@babel/traverse';
17+
import { extname } from 'path';
1718
/* eslint-disable-next-line no-unused-vars */
1819
import { memoize } from '../../../lib/memoize';
1920

@@ -231,7 +232,11 @@ export default class JavascriptEventWorker extends EventWorker {
231232

232233
const originalContent = consumer.sourceContentFor(originalLocation.source);
233234

234-
functionContext = await this.getFunctionContext(originalContent, originalLocation.line) ?? originalLocation.name;
235+
functionContext = await this.getFunctionContext(
236+
originalContent,
237+
originalLocation.line,
238+
originalLocation.source
239+
) ?? originalLocation.name;
235240
} catch (e) {
236241
HawkCatcher.send(e);
237242
this.logger.error('Can\'t get function context');
@@ -253,28 +258,20 @@ export default class JavascriptEventWorker extends EventWorker {
253258
*
254259
* @param sourceCode - content of the source file
255260
* @param line - number of the line from the stack trace
261+
* @param sourcePath - original source path from the source map (used to pick parser plugins)
256262
* @returns {string | null} - string of the function context or null if it could not be parsed
257263
*/
258-
private getFunctionContext(sourceCode: string, line: number): string | null {
264+
private getFunctionContext(sourceCode: string, line: number, sourcePath?: string): string | null {
259265
let functionName: string | null = null;
260266
let className: string | null = null;
261267
let isAsync = false;
262268

263269
try {
264-
// @todo choose plugins based on source code file extention (related to possible jsx parser usage in future)
270+
const parserPlugins = this.getBabelParserPluginsForFile(sourcePath);
271+
265272
const ast = parse(sourceCode, {
266273
sourceType: 'module',
267-
plugins: [
268-
'jsx',
269-
'typescript',
270-
'classProperties',
271-
'decorators',
272-
'optionalChaining',
273-
'nullishCoalescingOperator',
274-
'dynamicImport',
275-
'bigInt',
276-
'topLevelAwait',
277-
],
274+
plugins: parserPlugins,
278275
});
279276

280277
traverse(ast as any, {
@@ -454,4 +451,55 @@ export default class JavascriptEventWorker extends EventWorker {
454451
this.logger.error(`Error on source-map consumer initialization: ${e}`);
455452
}
456453
}
454+
455+
/**
456+
* Choose babel parser plugins based on source file extension
457+
*
458+
* @param sourcePath - original file path from source map (e.g. "src/App.tsx")
459+
*/
460+
private getBabelParserPluginsForFile(sourcePath?: string): any[] {
461+
const basePlugins: string[] = [
462+
'classProperties',
463+
'decorators',
464+
'optionalChaining',
465+
'nullishCoalescingOperator',
466+
'dynamicImport',
467+
'bigInt',
468+
'topLevelAwait',
469+
];
470+
471+
/**
472+
* Default - use only typescript plugin because it's more stable and less likely will produce errors
473+
*/
474+
let enableTypeScript = true;
475+
let enableJSX = false;
476+
477+
if (sourcePath) {
478+
// remove query/hash if there is any
479+
const cleanPath = sourcePath.split('?')[0].split('#')[0];
480+
const ext = extname(cleanPath).toLowerCase();
481+
482+
const isTs = ext === '.ts' || ext === '.d.ts';
483+
const isTsx = ext === '.tsx';
484+
const isJs = ext === '.js' || ext === '.mjs' || ext === '.cjs';
485+
const isJsx = ext === '.jsx';
486+
487+
enableTypeScript = isTs || isTsx;
488+
// JSX:
489+
// - for .ts/.d.ts — DISABLE
490+
// - for .tsx/.jsx — ENABLE
491+
// - for .js — keep enabled, to not break App.js with JSX
492+
enableJSX = isTsx || isJsx || isJs;
493+
}
494+
495+
if (enableTypeScript) {
496+
basePlugins.push('typescript');
497+
}
498+
499+
if (enableJSX) {
500+
basePlugins.push('jsx');
501+
}
502+
503+
return basePlugins;
504+
}
457505
}

workers/javascript/tests/index.test.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,4 +442,118 @@ describe('JavaScript event worker', () => {
442442

443443
await worker.finish();
444444
});
445+
446+
it('should resolve function context for TypeScript function with angle-bracket type assertion', () => {
447+
const worker = new JavascriptEventWorker();
448+
449+
const tsSource = `
450+
const foo: string | null = 'bar';
451+
452+
function throwError() {
453+
const value = <string>foo;
454+
throw new Error(value);
455+
}
456+
457+
export { throwError };
458+
`;
459+
460+
/**
461+
* String with throw new Error(...) is the 6th line (if counting from 1)
462+
* 1: ''
463+
* 2: const foo...
464+
* 3: ''
465+
* 4: function throwError() {
466+
* 5: const value = <string>foo;
467+
* 6: throw new Error(value);
468+
* ...
469+
*/
470+
const context = (worker as any).getFunctionContext(tsSource, 6, 'example.ts');
471+
472+
/**
473+
* We expect that the build with fixes will return the function name,
474+
* but with the current configuration the jsx+typescript parser fails
475+
* and getFunctionContext returns null.
476+
*/
477+
expect(context).toBe('throwError');
478+
});
479+
480+
it('should resolve function context for TypeScript generic arrow function', () => {
481+
const worker = new JavascriptEventWorker();
482+
483+
const tsSource = `
484+
type User = {
485+
id: string;
486+
name: string;
487+
};
488+
489+
const wrap = <T>(value: T): T => {
490+
return value;
491+
};
492+
493+
export const useUser = () => {
494+
const user: User = wrap<User>({ id: '1', name: 'John' });
495+
496+
return user;
497+
};
498+
`;
499+
500+
/**
501+
* String inside useUser - where we want to get context:
502+
* 1: ''
503+
* 2: type User = { ...
504+
* ...
505+
* 7: const wrap = <T>(value: T): T => {
506+
* ...
507+
* 12: export const useUser = () => {
508+
* 13: const user: User = wrap<User>({ id: '1', name: 'John' });
509+
* 14:
510+
* 15: return user;
511+
* 16: };
512+
*/
513+
const context = (worker as any).getFunctionContext(tsSource, 13, 'example.ts');
514+
515+
expect(context).toBe('useUser');
516+
});
517+
518+
it('should resolve class method context for TypeScript class with type assertion', () => {
519+
const worker = new JavascriptEventWorker();
520+
521+
const tsSource = `
522+
class ApiClient {
523+
private baseUrl: string = 'https://example.com';
524+
525+
public request() {
526+
const raw = '{"ok":true}';
527+
const parsed = <Record<string, unknown>>JSON.parse(raw);
528+
529+
if (!parsed.ok) {
530+
throw new Error('Request failed');
531+
}
532+
533+
return parsed;
534+
}
535+
}
536+
537+
export default ApiClient;
538+
`;
539+
540+
/**
541+
* String where we want to get context - inside the request method:
542+
* 1: ''
543+
* 2: class ApiClient {
544+
* 3: private baseUrl...
545+
* 4:
546+
* 5: public request() {
547+
* 6: const raw = '{"ok":true}';
548+
* 7: const parsed = <Record<string, unknown>>JSON.parse(raw);
549+
* 8:
550+
* 9: if (!parsed.ok) {
551+
* 10: throw new Error('Request failed');
552+
* ...
553+
*/
554+
const context = (worker as any).getFunctionContext(tsSource, 7, 'example.ts');
555+
556+
// We expect "ApiClient.request"
557+
expect(context).toBe('ApiClient.request');
558+
});
445559
});

0 commit comments

Comments
 (0)