@ryuzaki13/react-foundation-lib 1.9.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/error-report/breadcrumbs.d.ts.map +1 -1
- package/dist/error-report/capture.d.ts.map +1 -1
- package/dist/error-report/diagnostics.d.ts.map +1 -1
- package/dist/error-report/drafts.d.ts +2 -11
- package/dist/error-report/drafts.d.ts.map +1 -1
- package/dist/error-report/errorInfo.d.ts.map +1 -1
- package/dist/error-report/index.d.ts +5 -0
- package/dist/error-report/index.d.ts.map +1 -1
- package/dist/error-report/index.js +923 -210
- package/dist/error-report/index.js.map +1 -1
- package/dist/error-report/payload.d.ts +13 -0
- package/dist/error-report/payload.d.ts.map +1 -0
- package/dist/error-report/queue.d.ts +15 -0
- package/dist/error-report/queue.d.ts.map +1 -0
- package/dist/error-report/queueContract.d.ts +62 -0
- package/dist/error-report/queueContract.d.ts.map +1 -0
- package/dist/error-report/queueRecord.d.ts +9 -0
- package/dist/error-report/queueRecord.d.ts.map +1 -0
- package/dist/error-report/safeValue.d.ts +26 -2
- package/dist/error-report/safeValue.d.ts.map +1 -1
- package/dist/error-report/schema.d.ts +14 -0
- package/dist/error-report/schema.d.ts.map +1 -0
- package/dist/error-report/transport.d.ts +0 -2
- package/dist/error-report/transport.d.ts.map +1 -1
- package/dist/error-report/types.d.ts +26 -1
- package/dist/error-report/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/error-report/README.mdx +133 -27
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { QueryKey } from "@tanstack/react-query";
|
|
2
2
|
export type ErrorReportCategory = "query" | "mutation" | "runtime" | "react";
|
|
3
3
|
export type ErrorReportStatus = "pending" | "sending" | "sent" | "failed";
|
|
4
|
+
export type ErrorReportPayloadVersion = 1;
|
|
4
5
|
export type ErrorReportSafeValue = string | number | boolean | null | ErrorReportSafeValue[] | {
|
|
5
6
|
[key: string]: ErrorReportSafeValue;
|
|
6
7
|
};
|
|
@@ -43,7 +44,6 @@ export type ErrorReportMutationDiagnostics = {
|
|
|
43
44
|
error?: ErrorReportErrorInfo;
|
|
44
45
|
};
|
|
45
46
|
export type ErrorReportPersistedQueryDiagnostics = {
|
|
46
|
-
storageKey: string;
|
|
47
47
|
buster?: string;
|
|
48
48
|
queryHash?: string;
|
|
49
49
|
queryKey?: ErrorReportSafeValue;
|
|
@@ -55,6 +55,8 @@ export type ErrorReportPersistedQueryDiagnostics = {
|
|
|
55
55
|
error?: ErrorReportErrorInfo;
|
|
56
56
|
};
|
|
57
57
|
export type ErrorReportPayload = {
|
|
58
|
+
payloadVersion: ErrorReportPayloadVersion;
|
|
59
|
+
application: string;
|
|
58
60
|
reportId: string;
|
|
59
61
|
sessionId: string;
|
|
60
62
|
createdUtc: string;
|
|
@@ -91,6 +93,12 @@ export type ErrorReportPayload = {
|
|
|
91
93
|
persistedQueries?: ErrorReportPersistedQueryDiagnostics[];
|
|
92
94
|
breadcrumbs: ErrorReportBreadcrumb[];
|
|
93
95
|
context?: Record<string, ErrorReportSafeValue>;
|
|
96
|
+
truncation?: {
|
|
97
|
+
reason: "payload-size";
|
|
98
|
+
limitBytes: number;
|
|
99
|
+
originalBytes: number;
|
|
100
|
+
droppedSections: string[];
|
|
101
|
+
};
|
|
94
102
|
};
|
|
95
103
|
export type ErrorReportDraft = {
|
|
96
104
|
reportId: string;
|
|
@@ -102,6 +110,12 @@ export type ErrorReportDraft = {
|
|
|
102
110
|
failedReason?: string;
|
|
103
111
|
payload: ErrorReportPayload;
|
|
104
112
|
};
|
|
113
|
+
/** Единственные поля draft, которые delivery lifecycle вправе изменять. */
|
|
114
|
+
export type ErrorReportDraftLifecyclePatch = {
|
|
115
|
+
status?: ErrorReportStatus;
|
|
116
|
+
sentUtc?: string;
|
|
117
|
+
failedReason?: string;
|
|
118
|
+
};
|
|
105
119
|
export type ErrorReportRuntimeContext = {
|
|
106
120
|
category?: Extract<ErrorReportCategory, "runtime" | "react">;
|
|
107
121
|
source?: string;
|
|
@@ -120,4 +134,15 @@ export type ErrorReportMutationContext = {
|
|
|
120
134
|
errorInfo?: ErrorReportErrorInfo;
|
|
121
135
|
detail?: Record<string, unknown>;
|
|
122
136
|
};
|
|
137
|
+
export type ErrorReportSanitizationScope = "breadcrumb-detail" | "context" | "draft-failed-reason" | "error-message" | "mutation-key" | "mutation-meta" | "persisted-query-key" | "query-data-shape" | "query-key" | "query-meta" | "stack-trace";
|
|
138
|
+
export type ErrorReportSanitizationContext = {
|
|
139
|
+
scope: ErrorReportSanitizationScope;
|
|
140
|
+
source?: string;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* App-level callback получает уже bounded JSON-совместимое значение и может
|
|
144
|
+
* применить предметный allow-list. Foundation повторно нормализует результат,
|
|
145
|
+
* поэтому callback не может обойти ограничения глубины, размера строк и циклов.
|
|
146
|
+
*/
|
|
147
|
+
export type ErrorReportSanitizer = (value: ErrorReportSafeValue, context: ErrorReportSanitizationContext) => ErrorReportSafeValue | undefined;
|
|
123
148
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/error-report/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC;AAE7E,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE1E,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,oBAAoB,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CAAA;CAAE,CAAC;AAEvI,MAAM,MAAM,oBAAoB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,YAAY,GAAG,aAAa,GAAG,gBAAgB,GAAG,eAAe,CAAC;IAC5F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAC5C,SAAS,CAAC,EAAE,oBAAoB,CAAC;IACjC,KAAK,CAAC,EAAE,oBAAoB,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC5C,WAAW,CAAC,EAAE,oBAAoB,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAC5C,KAAK,CAAC,EAAE,oBAAoB,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,oCAAoC,GAAG;IAClD,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/error-report/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ import { Meta } from "@storybook/addon-docs/blocks";
|
|
|
4
4
|
|
|
5
5
|
# Error Report
|
|
6
6
|
|
|
7
|
-
Модуль `@ryuzaki13/react-foundation-lib/error-report` собирает клиентскую диагностику ошибки в
|
|
7
|
+
Модуль `@ryuzaki13/react-foundation-lib/error-report` собирает клиентскую диагностику ошибки в versioned bounded draft, временно хранит последние drafts в `sessionStorage` и предоставляет durable IndexedDB queue для отложенной доставки.
|
|
8
8
|
|
|
9
9
|
Он умеет объединить:
|
|
10
10
|
|
|
@@ -15,9 +15,10 @@ import { Meta } from "@storybook/addon-docs/blocks";
|
|
|
15
15
|
- состояние TanStack Query без полного query data;
|
|
16
16
|
- технические сведения о persisted queries;
|
|
17
17
|
- контекст React/runtime/transport;
|
|
18
|
-
- lifecycle
|
|
18
|
+
- локальный draft lifecycle: `pending`, `sending`, `sent`, `failed`;
|
|
19
|
+
- durable queue с TTL, bounded capacity и атомарным межвкладочным lease.
|
|
19
20
|
|
|
20
|
-
Важная граница: **lib-пакет не отправляет отчёт на сервер**. Он формирует draft и предоставляет
|
|
21
|
+
Важная граница: **lib-пакет не отправляет отчёт на сервер**. Он формирует draft, хранит queue и предоставляет атомарные переходы. Авторизация, HTTP/API delivery, retry, пользовательское согласие и UI принадлежат приложению либо API-пакету.
|
|
21
22
|
|
|
22
23
|
## Установка
|
|
23
24
|
|
|
@@ -26,15 +27,18 @@ import { Meta } from "@storybook/addon-docs/blocks";
|
|
|
26
27
|
```bash
|
|
27
28
|
npm install @ryuzaki13/react-foundation-lib \
|
|
28
29
|
@tanstack/react-query \
|
|
29
|
-
@tanstack/query-persist-client-core
|
|
30
|
+
@tanstack/query-persist-client-core \
|
|
31
|
+
zod
|
|
30
32
|
```
|
|
31
33
|
|
|
32
34
|
```ts
|
|
33
35
|
import {
|
|
34
36
|
captureRuntimeErrorReport,
|
|
37
|
+
createErrorReportQueue,
|
|
35
38
|
createErrorInfo,
|
|
36
39
|
installErrorReportBrowserBreadcrumbs,
|
|
37
40
|
setErrorReportRuntimeErrorReporter,
|
|
41
|
+
setErrorReportSanitizer,
|
|
38
42
|
type ErrorReportDraft
|
|
39
43
|
} from "@ryuzaki13/react-foundation-lib/error-report";
|
|
40
44
|
```
|
|
@@ -47,7 +51,8 @@ import {
|
|
|
47
51
|
→ нормализация + diagnostics + breadcrumbs
|
|
48
52
|
→ ErrorReportDraft(status = pending)
|
|
49
53
|
→ sessionStorage, максимум 10 drafts
|
|
50
|
-
→
|
|
54
|
+
→ enqueue payload в IndexedDB, максимум 50 записей / 7 суток
|
|
55
|
+
→ app/API delivery через lease
|
|
51
56
|
→ updateErrorReportDraft(status = sending/sent/failed)
|
|
52
57
|
```
|
|
53
58
|
|
|
@@ -80,15 +85,15 @@ declare const __APP_ID__: string;
|
|
|
80
85
|
declare const __APP_BUILD_ID__: string;
|
|
81
86
|
```
|
|
82
87
|
|
|
83
|
-
`__APP_ID__` входит в ключи `sessionStorage`, а `__APP_BUILD_ID__` помогает связать ошибку с деплоем.
|
|
88
|
+
`__APP_ID__` входит в payload как `application` и в ключи `sessionStorage`, а `__APP_BUILD_ID__` помогает связать ошибку с деплоем. Приложение обязано использовать устойчивый deployment identifier, а не display name. Например, KTK-приложения используют `ru.education-system` и `ru.ktk-website`.
|
|
84
89
|
|
|
85
90
|
Пример Vite:
|
|
86
91
|
|
|
87
92
|
```ts
|
|
88
93
|
define: {
|
|
89
94
|
__DEV__: JSON.stringify(mode !== "production"),
|
|
90
|
-
__APP_ID__: JSON.stringify("education-
|
|
91
|
-
__APP_BUILD_ID__: JSON.stringify(process.env.
|
|
95
|
+
__APP_ID__: JSON.stringify("ru.education-system"),
|
|
96
|
+
__APP_BUILD_ID__: JSON.stringify(process.env.GIT_COMMIT_SHA ?? "local"),
|
|
92
97
|
}
|
|
93
98
|
```
|
|
94
99
|
|
|
@@ -165,7 +170,7 @@ type ErrorReportErrorInfo = {
|
|
|
165
170
|
};
|
|
166
171
|
```
|
|
167
172
|
|
|
168
|
-
Для обычного `Error` сохраняются `name`, непустой `message` и
|
|
173
|
+
Для обычного `Error` сохраняются `name`, непустой `message` и `stack` до 64 KiB.
|
|
169
174
|
|
|
170
175
|
```ts
|
|
171
176
|
createErrorInfo(new Error("Не удалось сохранить"));
|
|
@@ -189,16 +194,34 @@ createErrorInfo(new Error("Не удалось сохранить"));
|
|
|
189
194
|
- максимальная глубина — 4;
|
|
190
195
|
- максимум 20 элементов массива;
|
|
191
196
|
- максимум 40 ключей объекта;
|
|
197
|
+
- строка — не более 4096 символов;
|
|
192
198
|
- `undefined`, function, symbol и усечённая ветка получают специальные объекты `{ type: ... }`;
|
|
199
|
+
- циклическая ссылка получает `{ type: "circular" }`;
|
|
193
200
|
- `bigint` превращается в строку;
|
|
194
201
|
- `Date` — в ISO-строку.
|
|
202
|
+
- известные secret-like keys (`authorization`, `cookie`, `password`, `token`, `secret`, CSRF/API keys) получают `[REDACTED]`;
|
|
203
|
+
- email в свободной строке заменяется на `[REDACTED_EMAIL]`;
|
|
204
|
+
- значения полей с именем `*Url` сохраняются без search/hash.
|
|
195
205
|
|
|
196
206
|
```ts
|
|
197
207
|
createDiagnosticValue({ retry: 2, handler: () => undefined });
|
|
198
208
|
// { retry: 2, handler: { type: "function" } }
|
|
199
209
|
```
|
|
200
210
|
|
|
201
|
-
|
|
211
|
+
Built-in redaction является страховочной сеткой и не распознаёт ФИО, телефоны и все предметные идентификаторы. Он не заменяет app-level allow-list.
|
|
212
|
+
|
|
213
|
+
### `setErrorReportSanitizer(sanitizer?)`
|
|
214
|
+
|
|
215
|
+
Регистрирует единственный sanitizer приложения для error message/stack, delivery failure reason, query/mutation keys, meta, context, data shape и breadcrumb detail. Callback получает уже bounded `ErrorReportSafeValue` и scope, возвращает разрешённое значение либо `undefined`, чтобы удалить секцию. Результат повторно нормализуется foundation-ограничителями.
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
setErrorReportSanitizer((value, context) => {
|
|
219
|
+
if (context.scope !== "context" || !value || Array.isArray(value) || typeof value !== "object") return value;
|
|
220
|
+
return "operation" in value ? { operation: value.operation } : undefined;
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Callback должен быть чистым и идемпотентным: capture может повторно применить его к уже подготовленной секции перед storage write. Если callback бросает исключение, рискованная секция удаляется. При teardown/hot reload вызовите `setErrorReportSanitizer(undefined)`.
|
|
202
225
|
|
|
203
226
|
### `sanitizeDetail(detail)`
|
|
204
227
|
|
|
@@ -260,7 +283,7 @@ getErrorReportBreadcrumbs();
|
|
|
260
283
|
clearErrorReportBreadcrumbs();
|
|
261
284
|
```
|
|
262
285
|
|
|
263
|
-
Возвращаемый массив является
|
|
286
|
+
Возвращаемый массив является shallow copy. Вложенные элементы считайте readonly и не меняйте вручную.
|
|
264
287
|
|
|
265
288
|
### Автоматические browser breadcrumbs
|
|
266
289
|
|
|
@@ -276,9 +299,9 @@ uninstallBreadcrumbs();
|
|
|
276
299
|
- click в capture phase;
|
|
277
300
|
- `document.visibilitychange`.
|
|
278
301
|
|
|
279
|
-
Для click сохраняются координаты/модификаторы и ограниченное описание meaningful target: tag,
|
|
302
|
+
Для click сохраняются координаты/модификаторы и ограниченное техническое описание meaningful target: tag, role, input type и цепочка родителей. Приложение может явно добавить slug-like `data-error-report-target`, например `student-save`. Если этот атрибут отсутствует или не проходит проверку, breadcrumbs используют уже распространённые в host-проектах `data-ui` и `data-action` как fallback. Глубина DOM-цепочки ограничена 20, каждый атрибут — 120 символами.
|
|
280
303
|
|
|
281
|
-
Код не читает `input.value` и
|
|
304
|
+
Код не читает `input.value`, DOM text, `id`, `title`, `aria-*`, CSS classes и произвольные `data-*`. Значения `data-error-report-target`, fallback `data-ui` и `data-action` принимаются только из латинских букв, цифр и символов `._:/-`; свободный текст в них помещать нельзя.
|
|
282
305
|
|
|
283
306
|
На сервере установщик возвращает пустую cleanup-функцию.
|
|
284
307
|
|
|
@@ -288,7 +311,7 @@ uninstallBreadcrumbs();
|
|
|
288
311
|
|
|
289
312
|
Собирает:
|
|
290
313
|
|
|
291
|
-
-
|
|
314
|
+
- безопасный hash уже очищенного query key и его диагностическое представление;
|
|
292
315
|
- status/fetchStatus;
|
|
293
316
|
- timestamps обновления data/error;
|
|
294
317
|
- failure count, invalidation и observer count;
|
|
@@ -296,7 +319,7 @@ uninstallBreadcrumbs();
|
|
|
296
319
|
- `dataShape` вместо data;
|
|
297
320
|
- нормализованную query error.
|
|
298
321
|
|
|
299
|
-
Значения `queryKey` и `meta`
|
|
322
|
+
Значения `queryKey` и `meta` проходят built-in redaction и app sanitizer. Предметные персональные идентификаторы всё равно должны удаляться policy приложения.
|
|
300
323
|
|
|
301
324
|
### `collectMutationDiagnostics(mutation)`
|
|
302
325
|
|
|
@@ -310,7 +333,7 @@ uninstallBreadcrumbs();
|
|
|
310
333
|
|
|
311
334
|
Асинхронно читает IndexedDB persistence из `/query-client` и возвращает максимум 40 записей.
|
|
312
335
|
|
|
313
|
-
Сохраняются
|
|
336
|
+
Сохраняются buster, безопасный hash/key, statuses, timestamps, failure count и error. IndexedDB storage key и `state.data` намеренно исключены полностью.
|
|
314
337
|
|
|
315
338
|
Если IndexedDB/persistence недоступны или чтение бросает ошибку, возвращается пустой массив — сбор отчёта не падает.
|
|
316
339
|
|
|
@@ -349,7 +372,7 @@ const draft = await captureQueryErrorReport(error, query, queryClient, {
|
|
|
349
372
|
const draft = await captureMutationErrorReport(error, mutation, queryClient, { source: "save-order", detail: { form: "order-edit" } });
|
|
350
373
|
```
|
|
351
374
|
|
|
352
|
-
Текущая реализация включает `detail` в breadcrumb,
|
|
375
|
+
Текущая реализация включает очищенный `detail` и в breadcrumb, и в `payload.context` mutation draft.
|
|
353
376
|
|
|
354
377
|
## Capture runtime/React-ошибки
|
|
355
378
|
|
|
@@ -395,14 +418,96 @@ type ErrorReportDraft = {
|
|
|
395
418
|
|
|
396
419
|
Payload содержит category/source/error, build environment, client, location, viewport, breadcrumbs и optional секции query/mutation/react/context.
|
|
397
420
|
|
|
421
|
+
`ErrorReportPayload` содержит обязательные `payloadVersion: 1` и `application`. Runtime parser принимает только текущую версию; unversioned и будущие формы не интерпретируются эвристически.
|
|
422
|
+
|
|
423
|
+
### Runtime parsing
|
|
424
|
+
|
|
425
|
+
```ts
|
|
426
|
+
parseErrorReportPayload(value);
|
|
427
|
+
parseErrorReportDraft(value);
|
|
428
|
+
parseErrorReportDrafts(value);
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
Первые две функции возвращают текущий typed contract либо `undefined`. Parser массива независимо отбрасывает повреждённые элементы, сохраняя соседние валидные drafts. Дополнительно проверяется совпадение identity полей draft и payload и фактический размер payload.
|
|
432
|
+
|
|
433
|
+
### Bounded payload
|
|
434
|
+
|
|
435
|
+
`limitErrorReportPayload(payload)` сначала повторно применяет sanitization, затем проверяет UTF-8 размер `JSON.stringify`. Предел — `ERROR_REPORT_PAYLOAD_MAX_BYTES` (256 KiB). При превышении optional diagnostics удаляются в фиксированном порядке, а `payload.truncation` фиксирует исходный размер, limit и список удалённых секций. `getErrorReportPayloadSize(payload)` возвращает фактический сериализованный размер в байтах.
|
|
436
|
+
|
|
398
437
|
### Хранение
|
|
399
438
|
|
|
400
|
-
|
|
439
|
+
Versioned drafts сохраняются под ключом `${__APP_ID__}.errorReport.drafts.v2`. В одной вкладке остаются последние 10 элементов. Старые удаляются с начала массива. Предыдущая unversioned storage-форма не загружается.
|
|
401
440
|
|
|
402
441
|
Storage errors и некорректный JSON подавляются. При переполнении sessionStorage текущее in-memory состояние обновится, но запись на диск может не сохраниться.
|
|
403
442
|
|
|
404
443
|
Модуль загружает drafts при первом импорте. Это модульное состояние, общее для всех consumers одного bundle.
|
|
405
444
|
|
|
445
|
+
## Durable IndexedDB queue
|
|
446
|
+
|
|
447
|
+
`sessionStorage` draft остаётся кратким диагностическим lifecycle текущей вкладки. Надёжную отложенную доставку обеспечивает отдельная queue:
|
|
448
|
+
|
|
449
|
+
```ts
|
|
450
|
+
const queue = createErrorReportQueue();
|
|
451
|
+
if (!queue) {
|
|
452
|
+
// IndexedDB отсутствует: app фиксирует безопасную деградацию без рекурсии report flow.
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const draft = await captureRuntimeErrorReport(error, context, queryClient);
|
|
457
|
+
if (draft) {
|
|
458
|
+
await queue.enqueue(draft.payload);
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
Создание возвращает `undefined`, когда IndexedDB недоступен. Ошибки открывания, quota и транзакций не поглощаются: coordinator обязан отделить storage failure от исходной пользовательской ошибки и применить безопасную recovery policy.
|
|
463
|
+
|
|
464
|
+
### Storage schema и limits
|
|
465
|
+
|
|
466
|
+
- база: `${__APP_ID__}.error-report.queue.v1`;
|
|
467
|
+
- object store: `records`;
|
|
468
|
+
- атомарная envelope-запись: `queue.v1`;
|
|
469
|
+
- queue schema: `queueVersion: 1`;
|
|
470
|
+
- максимум: 50 записей;
|
|
471
|
+
- TTL каждой записи: 7 суток от `enqueue`, независимо от client `createdUtc`;
|
|
472
|
+
- lease по умолчанию: 30 секунд.
|
|
473
|
+
|
|
474
|
+
Одна bounded envelope используется намеренно: `createIndexedDbQueryStorage.updateItem` изменяет её в одной `readwrite` транзакции, поэтому две вкладки не могут одновременно получить один report. Payload уже ограничен 256 KiB, а число записей — 50; отдельная база не смешивается с persisted Query cache.
|
|
475
|
+
|
|
476
|
+
При заполнении удаляется самый старый отчёт без активного lease, чтобы сохранить более свежую диагностику. Активные leases не вытесняются; если все 50 записей leased, `enqueue` возвращает `capacity-exceeded`. Повторный `reportId` возвращает `duplicate` и не заменяет сохранённый payload.
|
|
477
|
+
|
|
478
|
+
### Lease flow
|
|
479
|
+
|
|
480
|
+
```ts
|
|
481
|
+
const lease = await queue.leaseNext({ ownerId: tabId });
|
|
482
|
+
if (!lease) return;
|
|
483
|
+
|
|
484
|
+
try {
|
|
485
|
+
await deliver(lease.payload);
|
|
486
|
+
await queue.acknowledge(lease.reportId, tabId);
|
|
487
|
+
} catch {
|
|
488
|
+
await queue.reschedule(lease.reportId, tabId, {
|
|
489
|
+
nextAttemptUtc,
|
|
490
|
+
failure: { utc: new Date().toISOString(), kind: "network" }
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
`acknowledge`, `reschedule` и `discard` изменяют запись только при совпадении `ownerId`. После crash/закрытия вкладки lease автоматически снова становится доступным по `expiresUtc`. Если server принял отчёт, а acknowledgement потерялся, клиент повторит тот же `reportId`; backend обязан быть идемпотентным.
|
|
496
|
+
|
|
497
|
+
`lastFailure` хранит только технический класс, UTC и optional HTTP status. Текст transport error не попадает в durable storage.
|
|
498
|
+
|
|
499
|
+
### Queue API
|
|
500
|
+
|
|
501
|
+
- `enqueue(payload, now?)` — runtime-валидирует payload, удаляет expired и добавляет запись;
|
|
502
|
+
- `leaseNext({ ownerId, now?, leaseDurationMs? })` — атомарно выбирает ближайшую due-запись;
|
|
503
|
+
- `acknowledge(reportId, ownerId)` — удаляет подтверждённую запись;
|
|
504
|
+
- `reschedule(reportId, ownerId, retry)` — снимает lease и задаёт следующую попытку;
|
|
505
|
+
- `discard(reportId, ownerId)` — удаляет terminal-запись;
|
|
506
|
+
- `getRecords(now?)` — bounded диагностический snapshot;
|
|
507
|
+
- `getNextWakeUtc(now?)` — ближайшее due/lease-expiry время для scheduler-а.
|
|
508
|
+
|
|
509
|
+
Queue не использует `navigator.onLine` как источник истины и не владеет timer/event lifecycle. Этим занимается delivery coordinator из `@ryuzaki13/react-foundation-api/error-report`.
|
|
510
|
+
|
|
406
511
|
## Низкоуровневый `captureErrorReportDraft(args)`
|
|
407
512
|
|
|
408
513
|
Используйте, когда все diagnostic sections уже подготовлены вручную:
|
|
@@ -445,7 +550,7 @@ try {
|
|
|
445
550
|
}
|
|
446
551
|
```
|
|
447
552
|
|
|
448
|
-
Для неизвестного ID update возвращает `undefined`.
|
|
553
|
+
Для неизвестного ID update возвращает `undefined`. Функция принимает только lifecycle-поля `status`, `sentUtc`, `failedReason`; identity и payload изменить через неё нельзя. Runtime parser повторно проверяет draft, а свободный текст причины ограничивается и очищается перед storage write. Семантику разрешённых переходов статусов задаст delivery coordinator.
|
|
449
554
|
|
|
450
555
|
## Environment API
|
|
451
556
|
|
|
@@ -486,7 +591,7 @@ setErrorReportTransportErrorReporter(async (error, context) => {
|
|
|
486
591
|
|
|
487
592
|
### `reportTransportError(error, context)`
|
|
488
593
|
|
|
489
|
-
Context имеет source `fetch.parseResponse` или `fetch.buildHttpError` и может содержать URL, method, SAP client, status/content type, redirect/response type и
|
|
594
|
+
Context имеет source `fetch.parseResponse` или `fetch.buildHttpError` и может содержать URL, method, SAP client, status/content type, redirect/response type и только техническое summary HTML-response. HTML title и text preview больше не входят в контракт, потому что являются содержимым response body.
|
|
490
595
|
|
|
491
596
|
Вызов планирует reporter через 250 мс. Задержка позволяет TanStack Query перехватить тот же `Error` и создать более полезный query/mutation report.
|
|
492
597
|
|
|
@@ -550,11 +655,10 @@ export function installErrorReporting(queryClient: QueryClient) {
|
|
|
550
655
|
- queryKey и mutationKey;
|
|
551
656
|
- query/mutation meta;
|
|
552
657
|
- `context.detail`;
|
|
553
|
-
-
|
|
658
|
+
- pathname без search/hash;
|
|
554
659
|
- error message/stack;
|
|
555
|
-
-
|
|
660
|
+
- app-provided context, если policy ошибочно разрешила предметные данные;
|
|
556
661
|
- user agent, language, timezone;
|
|
557
|
-
- имена persisted storage keys.
|
|
558
662
|
|
|
559
663
|
Поэтому:
|
|
560
664
|
|
|
@@ -570,11 +674,11 @@ export function installErrorReporting(queryClient: QueryClient) {
|
|
|
570
674
|
|
|
571
675
|
### Ожидать автоматическую отправку
|
|
572
676
|
|
|
573
|
-
Capture только создаёт draft.
|
|
677
|
+
Capture только создаёт draft. Приложение должно enqueue-ить его payload и один раз запустить API coordinator в client lifecycle.
|
|
574
678
|
|
|
575
|
-
###
|
|
679
|
+
### Считать built-in redaction полной privacy policy
|
|
576
680
|
|
|
577
|
-
|
|
681
|
+
Foundation удаляет известные secret-like keys и email, но не умеет надёжно распознавать ФИО, телефоны и предметные IDs. Настройте app sanitizer по allow-list.
|
|
578
682
|
|
|
579
683
|
### Вызывать production capture в development и ждать draft
|
|
580
684
|
|
|
@@ -602,7 +706,9 @@ Capture только создаёт draft. Delivery нужно явно подк
|
|
|
602
706
|
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
603
707
|
| Capture | `captureQueryErrorReport`, `captureMutationErrorReport`, `captureRuntimeErrorReport`, `captureErrorReportDraft` |
|
|
604
708
|
| Errors | `createErrorInfo` |
|
|
605
|
-
| Safe values | `createDiagnosticValue`, `createDataShape`, `sanitizeDetail`
|
|
709
|
+
| Safe values | `createDiagnosticValue`, `createDataShape`, `sanitizeDetail`, `sanitizeErrorReportValue`, `setErrorReportSanitizer` |
|
|
710
|
+
| Payload contract | `ERROR_REPORT_PAYLOAD_VERSION`, `ERROR_REPORT_PAYLOAD_MAX_BYTES`, `limitErrorReportPayload`, `getErrorReportPayloadSize`, parsers |
|
|
711
|
+
| Durable queue | `createErrorReportQueue`, queue limits, record/parser и atomic lease transitions |
|
|
606
712
|
| Breadcrumbs | `addErrorReportBreadcrumb`, `getErrorReportBreadcrumbs`, `clearErrorReportBreadcrumbs`, `installErrorReportBrowserBreadcrumbs` |
|
|
607
713
|
| Diagnostics | `collectQueryDiagnostics`, `collectMutationDiagnostics`, `collectQueryClientDiagnostics`, `collectPersistedQueryDiagnostics` |
|
|
608
714
|
| Drafts | `getErrorReportDraft`, `getErrorReportDrafts`, `updateErrorReportDraft` |
|