@escapenavigator/services 1.10.141 → 1.10.142

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.
@@ -3,14 +3,42 @@ import { AxiosInstance, AxiosPromise, AxiosRequestConfig } from 'axios';
3
3
  * Контекст ошибки, который передаётся в `clientOptions.onError`.
4
4
  * Сделан плоским и сериализуемым — host-приложение само решит,
5
5
  * что отправлять в Sentry/Datadog/etc.
6
+ *
7
+ * Важно: тело запроса (`params`) сюда НЕ кладётся — там может быть PII
8
+ * (email, телефон, имя клиента). Если конкретному endpoint'у нужны
9
+ * sanitized-параметры — пусть host-апп прокидывает их через свой
10
+ * api-wrapper.
6
11
  */
7
12
  export type ApiErrorContext = {
8
13
  method: string;
9
14
  url: string;
10
15
  status?: number;
16
+ /** Сетевой код axios: ECONNABORTED/ERR_CANCELED/... — для 0-status кейсов. */
11
17
  code?: string;
12
18
  /** имя ApiMethodDeclaration, например `users.me` */
13
19
  apiKey: string;
20
+ /**
21
+ * Бизнес-код ошибки от бэка (`errorCode` из ErrorData). Лежит в теле
22
+ * 4xx-ответа. Используем для классификации (см. `classifyApiError`):
23
+ * generic коды → debug-уровень, конкретные (`SLOT_TAKEN`, `PROMO_INVALID`)
24
+ * → business-уровень.
25
+ */
26
+ errorCode?: string;
27
+ /** Изначальный бизнес-код, до маппинга бэком на generic (`originalErrorCode`). */
28
+ originalErrorCode?: string;
29
+ /** Человекочитаемое сообщение от бэка (`ErrorData.message`). */
30
+ backendMessage?: string;
31
+ /**
32
+ * Сериализуемое тело ответа сервера (только структурированные поля
33
+ * `ErrorData`, без всего payload'а — чтобы случайно не утащить PII).
34
+ */
35
+ responseData?: {
36
+ errorId?: string;
37
+ application?: string;
38
+ errorCode?: string;
39
+ originalErrorCode?: string;
40
+ message?: string;
41
+ };
14
42
  };
15
43
  export type InitApiClientOptions = {
16
44
  /** Кастомный клиент (например React Native с interceptors и без редиректа в браузере). */
package/dist/api/index.js CHANGED
@@ -72,13 +72,34 @@ var axios_1 = __importDefault(require("axios"));
72
72
  * хосту ВСЁ кроме самых шумных — пусть сам отфильтрует.
73
73
  */
74
74
  function buildErrorContext(error, axiosConfig, apiKey) {
75
- var _a, _b, _c;
75
+ var _a, _b, _c, _d;
76
+ // Тело ответа от бэка приходит как `response.data: ErrorData`. Для blob-
77
+ // экспортов (XLSX/PDF) бэк может вернуть Blob даже на 4xx — тогда у нас
78
+ // тут будет инстанс Blob, который сериализовать в Sentry бессмысленно.
79
+ // Host-апп уже распаковывает JSON-Blob в `useApiMethod` через
80
+ // `error.response.data.text() → JSON.parse`, но onError вызывается
81
+ // раньше этого хэндлинга. Поэтому здесь просто читаем `data` как есть
82
+ // и пробрасываем только если это похоже на объект.
83
+ var rawData = (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data;
84
+ var responseData = rawData && typeof rawData === 'object' && !(rawData instanceof Blob)
85
+ ? {
86
+ errorId: rawData.errorId,
87
+ application: rawData.application,
88
+ errorCode: rawData.errorCode,
89
+ originalErrorCode: rawData.originalErrorCode,
90
+ message: rawData.message,
91
+ }
92
+ : undefined;
76
93
  return {
77
94
  apiKey: apiKey,
78
- method: ((_a = axiosConfig === null || axiosConfig === void 0 ? void 0 : axiosConfig.method) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'GET',
79
- url: (_b = axiosConfig === null || axiosConfig === void 0 ? void 0 : axiosConfig.url) !== null && _b !== void 0 ? _b : apiKey,
80
- status: (_c = error === null || error === void 0 ? void 0 : error.response) === null || _c === void 0 ? void 0 : _c.status,
95
+ method: ((_b = axiosConfig === null || axiosConfig === void 0 ? void 0 : axiosConfig.method) === null || _b === void 0 ? void 0 : _b.toUpperCase()) || 'GET',
96
+ url: (_c = axiosConfig === null || axiosConfig === void 0 ? void 0 : axiosConfig.url) !== null && _c !== void 0 ? _c : apiKey,
97
+ status: (_d = error === null || error === void 0 ? void 0 : error.response) === null || _d === void 0 ? void 0 : _d.status,
81
98
  code: error === null || error === void 0 ? void 0 : error.code,
99
+ errorCode: responseData === null || responseData === void 0 ? void 0 : responseData.errorCode,
100
+ originalErrorCode: responseData === null || responseData === void 0 ? void 0 : responseData.originalErrorCode,
101
+ backendMessage: responseData === null || responseData === void 0 ? void 0 : responseData.message,
102
+ responseData: responseData,
82
103
  };
83
104
  }
84
105
  var getAuthLoginPathBySubdomain = function () {
@@ -0,0 +1,14 @@
1
+ import { ProfileEmailCardRO } from '@escapenavigator/types/dist/profile/email/profile-email-card.ro';
2
+ import { QueryDto } from '@escapenavigator/types/dist/shared/query.dto';
3
+ import { QueryRO } from '@escapenavigator/types/dist/shared/query.ro';
4
+ import { ApiMethodDeclaration } from '../..';
5
+ type ParamsData = QueryDto;
6
+ type ResponseData = QueryRO<ProfileEmailCardRO>;
7
+ /**
8
+ * Пагинированный список писем профиля. Поддерживает фильтр `where.clientId`
9
+ * для блока «Письма клиенту» в модалке клиента — раньше там был отдельный
10
+ * `getEmailsByClient` без пагинации (брал 100 за раз и упирался в таймаут
11
+ * у тяжёлых клиентов). Теперь грузим как «игры»: первые N, потом «ещё».
12
+ */
13
+ export declare const queryProfileEmails: ApiMethodDeclaration<ParamsData, ResponseData>;
14
+ export {};
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.queryProfileEmails = void 0;
4
+ /**
5
+ * Пагинированный список писем профиля. Поддерживает фильтр `where.clientId`
6
+ * для блока «Письма клиенту» в модалке клиента — раньше там был отдельный
7
+ * `getEmailsByClient` без пагинации (брал 100 за раз и упирался в таймаут
8
+ * у тяжёлых клиентов). Теперь грузим как «игры»: первые N, потом «ещё».
9
+ */
10
+ var queryProfileEmails = function (params) { return ({
11
+ url: '/profile-emails',
12
+ method: 'GET',
13
+ params: params,
14
+ }); };
15
+ exports.queryProfileEmails = queryProfileEmails;
@@ -34,9 +34,9 @@ import { updateCurrent } from './current/update-current';
34
34
  import { updateGa } from './current/update-ga';
35
35
  import { getAllCustomersEmail } from './email/get-all-customer-emails';
36
36
  import { getCustomerEmail } from './email/get-customer-email';
37
- import { getEmailsByClient } from './email/get-emails-by-client';
38
37
  import { getProfileEmailsSummary } from './email/get-profile-emails-summary';
39
38
  import { queryCampaigns } from './email/query-campaigns';
39
+ import { queryProfileEmails } from './email/query-profile-emails';
40
40
  import { resendProfileEmail } from './email/resend-profile-email';
41
41
  import { testEmail } from './email/test-email';
42
42
  import { addEmailDomain } from './email-senders/add-email-domain';
@@ -138,7 +138,7 @@ type ApiDeclaration = {
138
138
  getAllCustomersEmail: typeof getAllCustomersEmail;
139
139
  getProfileEmailsSummary: typeof getProfileEmailsSummary;
140
140
  getCustomerEmail: typeof getCustomerEmail;
141
- getEmailsByClient: typeof getEmailsByClient;
141
+ queryProfileEmails: typeof queryProfileEmails;
142
142
  resendProfileEmail: typeof resendProfileEmail;
143
143
  testEmail: typeof testEmail;
144
144
  queryCampaigns: typeof queryCampaigns;
@@ -37,9 +37,9 @@ var update_current_1 = require("./current/update-current");
37
37
  var update_ga_1 = require("./current/update-ga");
38
38
  var get_all_customer_emails_1 = require("./email/get-all-customer-emails");
39
39
  var get_customer_email_1 = require("./email/get-customer-email");
40
- var get_emails_by_client_1 = require("./email/get-emails-by-client");
41
40
  var get_profile_emails_summary_1 = require("./email/get-profile-emails-summary");
42
41
  var query_campaigns_1 = require("./email/query-campaigns");
42
+ var query_profile_emails_1 = require("./email/query-profile-emails");
43
43
  var resend_profile_email_1 = require("./email/resend-profile-email");
44
44
  var test_email_1 = require("./email/test-email");
45
45
  var add_email_domain_1 = require("./email-senders/add-email-domain");
@@ -161,7 +161,7 @@ exports.profilesApiDeclaration = {
161
161
  getAllCustomersEmail: get_all_customer_emails_1.getAllCustomersEmail,
162
162
  getProfileEmailsSummary: get_profile_emails_summary_1.getProfileEmailsSummary,
163
163
  getCustomerEmail: get_customer_email_1.getCustomerEmail,
164
- getEmailsByClient: get_emails_by_client_1.getEmailsByClient,
164
+ queryProfileEmails: query_profile_emails_1.queryProfileEmails,
165
165
  resendProfileEmail: resend_profile_email_1.resendProfileEmail,
166
166
  queryProfileWaivers: query_profile_waivers_1.queryProfileWaivers,
167
167
  getProfileWaiverForOrderService: get_profile_waiver_for_order_service_1.getProfileWaiverForOrderService,
@@ -46,6 +46,7 @@ import { updateSession } from './sessions/update-session';
46
46
  import { setCookie } from './set-cookie';
47
47
  import { assigneTask } from './task/assigne-task';
48
48
  import { createTask } from './task/create-task';
49
+ import { getDashboardCounters } from './task/get-dashboard-counters';
49
50
  import { queryTasks } from './task/query-tasks';
50
51
  import { removeTask } from './task/remove-task';
51
52
  import { unassigneTask } from './task/unassigne-task';
@@ -58,6 +59,7 @@ type ApiDeclaration = {
58
59
  unassigneTask: typeof unassigneTask;
59
60
  createTask: typeof createTask;
60
61
  queryTasks: typeof queryTasks;
62
+ getDashboardCounters: typeof getDashboardCounters;
61
63
  updateTask: typeof updateTask;
62
64
  removeTask: typeof removeTask;
63
65
  updateTaskStatus: typeof updateTaskStatus;
@@ -49,6 +49,7 @@ var update_session_1 = require("./sessions/update-session");
49
49
  var set_cookie_1 = require("./set-cookie");
50
50
  var assigne_task_1 = require("./task/assigne-task");
51
51
  var create_task_1 = require("./task/create-task");
52
+ var get_dashboard_counters_1 = require("./task/get-dashboard-counters");
52
53
  var query_tasks_1 = require("./task/query-tasks");
53
54
  var remove_task_1 = require("./task/remove-task");
54
55
  var unassigne_task_1 = require("./task/unassigne-task");
@@ -63,6 +64,7 @@ exports.usersApiDeclaration = {
63
64
  currentUpdateDashboard: current_update_dashboard_1.currentUpdateDashboard,
64
65
  createPosition: create_position_1.createPosition,
65
66
  queryTasks: query_tasks_1.queryTasks,
67
+ getDashboardCounters: get_dashboard_counters_1.getDashboardCounters,
66
68
  updateTaskStatus: update_task_status_1.updateTaskStatus,
67
69
  removeTask: remove_task_1.removeTask,
68
70
  updateTask: update_task_1.updateTask,
@@ -0,0 +1,8 @@
1
+ import { UserTaskDashboardCountersRO } from '@escapenavigator/types/dist/user/task/user-task-dashboard-counters.ro';
2
+ import { ApiMethodDeclaration } from '../..';
3
+ type ParamsData = {
4
+ locationId?: number;
5
+ };
6
+ type ResponseData = UserTaskDashboardCountersRO;
7
+ export declare const getDashboardCounters: ApiMethodDeclaration<ParamsData, ResponseData>;
8
+ export {};
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDashboardCounters = void 0;
4
+ var getDashboardCounters = function (params) { return ({
5
+ url: '/user-tasks/dashboard-counters',
6
+ method: 'GET',
7
+ params: params,
8
+ }); };
9
+ exports.getDashboardCounters = getDashboardCounters;
@@ -146,8 +146,35 @@ var useApiMethod = function (_a) {
146
146
  if (errorCallbackRef.current)
147
147
  errorCallbackRef.current(err);
148
148
  dispatch((0, api_1.fetchFailure)(err));
149
- // eslint-disable-next-line @typescript-eslint/no-throw-literal
150
- throw err;
149
+ // ИСТОРИЯ: раньше здесь стоял `throw err`, где `err` — plain
150
+ // object из `getServiceError` (а не Error). Это давало два
151
+ // системных бага:
152
+ //
153
+ // 1) Любой fire-and-forget `<name>Fetch(params)` (без .catch)
154
+ // превращался в global `onunhandledrejection`. На странице
155
+ // входа `loginFetch({data})` при невалидном пароле уходил
156
+ // в Sentry как `<unknown>` / «Object captured as promise
157
+ // rejection with keys: code, errorType, message,
158
+ // originalError» — без стека, без группировки.
159
+ //
160
+ // 2) Sentry группировал все такие события в один issue, теряя
161
+ // контекст конкретного endpoint'а.
162
+ //
163
+ // Теперь ошибка ВСЕГДА доходит до consumer'а тремя путями:
164
+ // • `state.<name>Error` (UI читает `error?.message`)
165
+ // • `errorCallback({code, message, ...})` (если передан)
166
+ // • Sentry — через `onError` в `initApi`/`reportApiErrorToSentry`,
167
+ // с осмысленным severity/level/fingerprint и телом ответа
168
+ // бэка в `extra`.
169
+ //
170
+ // Возврат `null` (вместо throw) семантически совместим с
171
+ // существующими call-site'ами: они уже получали `null` на
172
+ // axios.cancel и unmount-path, и большинство уже проверяют
173
+ // `if (!res?.data)`. Try/catch-блоки вокруг `await fetch()`
174
+ // продолжают работать — просто catch-ветка станет dead code
175
+ // для handled-ошибок; если в ней была реакция (setError /
176
+ // showError) — её нужно перенести на ветку `if (!res)`.
177
+ return [2 /*return*/, null];
151
178
  case 8:
152
179
  inflightRef.current = Math.max(0, inflightRef.current - 1);
153
180
  return [7 /*endfinally*/];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@escapenavigator/services",
3
- "version": "1.10.141",
3
+ "version": "1.10.142",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -12,10 +12,10 @@
12
12
  "scripts": {
13
13
  "build": "rm -rf dist && tsc --project tsconfig.json"
14
14
  },
15
- "gitHead": "e27b2a8ad0dcf2e7eda752a57a671cb805c0bc1e",
15
+ "gitHead": "e6562ef1dbf0652659bc037c3574e4f188663bf1",
16
16
  "dependencies": {
17
- "@escapenavigator/types": "^1.10.132",
18
- "@escapenavigator/utils": "^1.10.136",
17
+ "@escapenavigator/types": "^1.10.133",
18
+ "@escapenavigator/utils": "^1.10.137",
19
19
  "axios": "^0.21.4",
20
20
  "class-transformer": "^0.5.1",
21
21
  "class-validator": "^0.13.2",
@@ -1,13 +0,0 @@
1
- import { ProfileEmailCardRO } from '@escapenavigator/types/dist/profile/email/profile-email-card.ro';
2
- import { ApiMethodDeclaration } from '../..';
3
- type ParamsData = {
4
- clientId: number;
5
- };
6
- type ResponseData = ProfileEmailCardRO[];
7
- /**
8
- * Все письма, отправленные конкретному клиенту в рамках профиля.
9
- * Бек берёт по индексу `(clientId, createdAt)`, лимит 100. Используется
10
- * блоком «Письма клиенту» в модалке клиента.
11
- */
12
- export declare const getEmailsByClient: ApiMethodDeclaration<ParamsData, ResponseData>;
13
- export {};
@@ -1,16 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getEmailsByClient = void 0;
4
- /**
5
- * Все письма, отправленные конкретному клиенту в рамках профиля.
6
- * Бек берёт по индексу `(clientId, createdAt)`, лимит 100. Используется
7
- * блоком «Письма клиенту» в модалке клиента.
8
- */
9
- var getEmailsByClient = function (_a) {
10
- var clientId = _a.clientId;
11
- return ({
12
- url: "/profile-emails/by-client/".concat(clientId),
13
- method: 'GET',
14
- });
15
- };
16
- exports.getEmailsByClient = getEmailsByClient;