@tpzdsp/next-toolkit 1.14.2 → 1.15.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.
- package/package.json +2 -1
- package/src/assets/styles/globals.css +5 -1
- package/src/components/ErrorBoundary/ErrorBoundary.stories.tsx +1 -1
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +1 -1
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +1 -1
- package/src/components/InfoBox/InfoBox.tsx +7 -4
- package/src/components/accordion/Accordion.test.tsx +5 -10
- package/src/components/accordion/Accordion.tsx +4 -7
- package/src/components/divider/RuleDivider.test.tsx +4 -4
- package/src/components/form/Input.test.tsx +3 -11
- package/src/components/form/Input.tsx +2 -2
- package/src/components/form/TextArea.test.tsx +3 -5
- package/src/components/form/TextArea.tsx +2 -2
- package/src/components/layout/header/Header.stories.tsx +3 -3
- package/src/components/layout/header/Header.test.tsx +3 -3
- package/src/components/layout/header/HeaderNavClient.test.tsx +3 -3
- package/src/components/select/Select.stories.tsx +5 -5
- package/src/components/select/Select.test.tsx +2 -2
- package/src/components/select/Select.tsx +3 -4
- package/src/components/select/SelectSkeleton.test.tsx +1 -2
- package/src/components/select/SelectSkeleton.tsx +3 -3
- package/src/components/select/common.ts +2 -3
- package/src/http/constants.ts +4 -0
- package/src/http/fetch.ts +2 -0
- package/src/http/logger.test.ts +346 -0
- package/src/http/logger.ts +412 -76
- package/src/map/useKeyboardDrawing.ts +8 -4
- package/src/utils/constants.ts +8 -0
- package/src/utils/utils.ts +4 -4
- package/src/components/theme/ThemeProvider.tsx +0 -30
package/src/http/logger.ts
CHANGED
|
@@ -2,60 +2,376 @@ import type { BetterFetchPlugin, RequestContext } from '@better-fetch/fetch';
|
|
|
2
2
|
|
|
3
3
|
import { Header } from './constants';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const MAX_PREVIEW_LENGTH = 150;
|
|
6
|
+
|
|
7
|
+
// Tries to print data as a string.
|
|
8
|
+
const stringify = (data: unknown): string | null => {
|
|
7
9
|
if (data == null) {
|
|
8
10
|
return null;
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
try {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if (str.length > maxLength) {
|
|
15
|
-
str = str.slice(0, maxLength) + '…';
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return str;
|
|
14
|
+
return typeof data === 'string' ? data : JSON.stringify(data);
|
|
19
15
|
} catch {
|
|
20
16
|
return '[unserializable]';
|
|
21
17
|
}
|
|
22
18
|
};
|
|
23
19
|
|
|
20
|
+
// Tries to print the request/response body as a string, and clamps the length if it's too long.
|
|
21
|
+
const preview = (data: unknown): string | null => {
|
|
22
|
+
const str = stringify(data);
|
|
23
|
+
|
|
24
|
+
if (str == null) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (str.length > MAX_PREVIEW_LENGTH) {
|
|
29
|
+
return str.slice(0, MAX_PREVIEW_LENGTH) + '…';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return str;
|
|
33
|
+
};
|
|
34
|
+
|
|
24
35
|
type RequestMeta = {
|
|
25
36
|
requestId: string;
|
|
26
37
|
};
|
|
38
|
+
|
|
39
|
+
type RequestLoggerMessageExtras = {
|
|
40
|
+
loggerOverrides?: {
|
|
41
|
+
requestFormat?: (args: { request: MetaContext }) => string;
|
|
42
|
+
|
|
43
|
+
successFormat?: (args: {
|
|
44
|
+
request: MetaContext;
|
|
45
|
+
response: Response;
|
|
46
|
+
data: unknown;
|
|
47
|
+
durationMs: number;
|
|
48
|
+
}) => string;
|
|
49
|
+
|
|
50
|
+
errorFormat?: (args: {
|
|
51
|
+
request: MetaContext;
|
|
52
|
+
response?: Response;
|
|
53
|
+
error: unknown;
|
|
54
|
+
durationMs: number;
|
|
55
|
+
}) => string;
|
|
56
|
+
|
|
57
|
+
retryFormat?: (args: { request: MetaContext; attempt: number }) => string;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
|
|
27
61
|
type MetaContext = RequestContext & {
|
|
28
62
|
meta: RequestMeta;
|
|
29
|
-
};
|
|
63
|
+
} & RequestLoggerMessageExtras;
|
|
30
64
|
|
|
31
65
|
const UNKNOWN_ID = '???';
|
|
32
66
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
67
|
+
type RequestResponseToggle = {
|
|
68
|
+
request?: boolean;
|
|
69
|
+
response?: boolean;
|
|
70
|
+
};
|
|
71
|
+
|
|
36
72
|
type RequestLoggerOptions = {
|
|
37
73
|
/**
|
|
38
|
-
* Enables/
|
|
74
|
+
* Enables/disables the logger. Can be a function/async function to toggle dynamically.
|
|
39
75
|
*/
|
|
40
76
|
enabled?: boolean | (() => Promise<boolean> | boolean);
|
|
77
|
+
|
|
41
78
|
/**
|
|
42
|
-
*
|
|
79
|
+
* Whether bodies should be truncated to `maxPreviewLength`.
|
|
80
|
+
* If false, the full body is printed and is placed on the following line.
|
|
43
81
|
*/
|
|
44
|
-
|
|
82
|
+
truncateBody?: boolean;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Built-in output toggles.
|
|
86
|
+
*/
|
|
87
|
+
showMethod?: boolean;
|
|
88
|
+
showUrl?: boolean;
|
|
89
|
+
showStatus?: boolean;
|
|
90
|
+
showDuration?: boolean;
|
|
91
|
+
showAcceptHeader?: boolean;
|
|
92
|
+
showContentType?: boolean;
|
|
93
|
+
showRetryAttempt?: boolean;
|
|
94
|
+
showBody?: RequestResponseToggle;
|
|
95
|
+
showHeaders?: RequestResponseToggle;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const defaultOptions = {
|
|
99
|
+
truncateBody: true,
|
|
100
|
+
showMethod: true,
|
|
101
|
+
showUrl: true,
|
|
102
|
+
showStatus: true,
|
|
103
|
+
showDuration: true,
|
|
104
|
+
showAcceptHeader: true,
|
|
105
|
+
showContentType: true,
|
|
106
|
+
showRetryAttempt: true,
|
|
107
|
+
showBody: {
|
|
108
|
+
request: true,
|
|
109
|
+
response: true,
|
|
110
|
+
},
|
|
111
|
+
showHeaders: {
|
|
112
|
+
request: false,
|
|
113
|
+
response: false,
|
|
114
|
+
},
|
|
115
|
+
} as const;
|
|
116
|
+
|
|
117
|
+
const getOptionOrDefault = <K extends keyof RequestLoggerOptions>(
|
|
118
|
+
options: RequestLoggerOptions | undefined,
|
|
119
|
+
key: K,
|
|
120
|
+
): NonNullable<RequestLoggerOptions[K]> => {
|
|
121
|
+
return options?.[key] ?? (defaultOptions as Required<RequestLoggerOptions>)[key];
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const formatHeaders = (headers: Headers): string => {
|
|
125
|
+
const entries = Array.from(headers.entries());
|
|
126
|
+
|
|
127
|
+
if (entries.length === 0) {
|
|
128
|
+
return 'N/A';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return entries.map(([key, value]) => `${key}: ${value}`).join(', ');
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const joinLines = (lines: (string | null | undefined)[]): string =>
|
|
135
|
+
lines.filter((line): line is string => Boolean(line)).join('\n');
|
|
136
|
+
|
|
137
|
+
const formatPrimaryLine = ({
|
|
138
|
+
icon,
|
|
139
|
+
request,
|
|
140
|
+
id,
|
|
141
|
+
showMethod,
|
|
142
|
+
showUrl,
|
|
143
|
+
suffix,
|
|
144
|
+
}: {
|
|
145
|
+
icon: string;
|
|
146
|
+
request: MetaContext;
|
|
147
|
+
id: string;
|
|
148
|
+
showMethod: boolean;
|
|
149
|
+
showUrl: boolean;
|
|
150
|
+
suffix?: string;
|
|
151
|
+
}): string => {
|
|
152
|
+
const parts = [
|
|
153
|
+
icon,
|
|
154
|
+
showMethod ? `[${request.method.toUpperCase()}]` : null,
|
|
155
|
+
`(${id})`,
|
|
156
|
+
showUrl ? `${request.url ?? ''}` : null,
|
|
157
|
+
suffix ?? null,
|
|
158
|
+
].filter(Boolean);
|
|
159
|
+
|
|
160
|
+
return parts.join(' ');
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const formatStatusDurationLine = ({
|
|
164
|
+
status,
|
|
165
|
+
durationMs,
|
|
166
|
+
showStatus,
|
|
167
|
+
showDuration,
|
|
168
|
+
}: {
|
|
169
|
+
status?: number | string;
|
|
170
|
+
durationMs: number;
|
|
171
|
+
showStatus: boolean;
|
|
172
|
+
showDuration: boolean;
|
|
173
|
+
}): string | null => {
|
|
174
|
+
const parts = [
|
|
175
|
+
showStatus ? `${status ?? 'unknown'}` : null,
|
|
176
|
+
showDuration ? `${durationMs.toFixed(1)}ms` : null,
|
|
177
|
+
].filter(Boolean);
|
|
178
|
+
|
|
179
|
+
return parts.length > 0 ? `\t↳ ${parts.join(' | ')}` : null;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const formatBodyLine = ({
|
|
183
|
+
label,
|
|
184
|
+
body,
|
|
185
|
+
truncateBody,
|
|
186
|
+
}: {
|
|
187
|
+
label: string;
|
|
188
|
+
body: unknown;
|
|
189
|
+
truncateBody: boolean;
|
|
190
|
+
}): string | null => {
|
|
191
|
+
const rendered = truncateBody ? preview(body) : stringify(body);
|
|
192
|
+
|
|
193
|
+
if (!rendered) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return truncateBody ? `\t↳ ${label}: ${rendered}` : `\t↳ ${label}:\n${rendered}`;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const getDurationMs = (timings: Map<string, number>, id: string): number =>
|
|
201
|
+
performance.now() - (timings.get(id) ?? 0);
|
|
202
|
+
|
|
203
|
+
const defaultRequestFormatter = ({
|
|
204
|
+
request,
|
|
205
|
+
options,
|
|
206
|
+
}: {
|
|
207
|
+
request: MetaContext;
|
|
208
|
+
options?: RequestLoggerOptions;
|
|
209
|
+
}): string => {
|
|
210
|
+
const id = request.meta?.requestId ?? UNKNOWN_ID;
|
|
211
|
+
|
|
212
|
+
const showMethod = getOptionOrDefault(options, 'showMethod');
|
|
213
|
+
const showUrl = getOptionOrDefault(options, 'showUrl');
|
|
214
|
+
const showAcceptHeader = getOptionOrDefault(options, 'showAcceptHeader');
|
|
215
|
+
const showRequestContentType = getOptionOrDefault(options, 'showContentType');
|
|
216
|
+
const showRequestHeaders = getOptionOrDefault(options, 'showHeaders').request;
|
|
217
|
+
const showRequestBody = getOptionOrDefault(options, 'showBody').request;
|
|
218
|
+
const truncateBody = getOptionOrDefault(options, 'truncateBody');
|
|
219
|
+
|
|
220
|
+
return joinLines([
|
|
221
|
+
formatPrimaryLine({
|
|
222
|
+
icon: '🚀',
|
|
223
|
+
request,
|
|
224
|
+
id,
|
|
225
|
+
showMethod,
|
|
226
|
+
showUrl,
|
|
227
|
+
}),
|
|
228
|
+
showRequestContentType
|
|
229
|
+
? `\t↳ Req. Content-Type: ${request.headers.get(Header.ContentType) ?? 'N/A'}`
|
|
230
|
+
: null,
|
|
231
|
+
showAcceptHeader ? `\t↳ Req. Accept: ${request.headers.get(Header.Accept) ?? 'N/A'}` : null,
|
|
232
|
+
showRequestHeaders ? `\t↳ Req. Headers: ${formatHeaders(request.headers)}` : null,
|
|
233
|
+
showRequestBody
|
|
234
|
+
? formatBodyLine({
|
|
235
|
+
label: 'Req. Body',
|
|
236
|
+
body: request.body,
|
|
237
|
+
truncateBody,
|
|
238
|
+
})
|
|
239
|
+
: null,
|
|
240
|
+
]);
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const defaultSuccessFormatter = ({
|
|
244
|
+
request,
|
|
245
|
+
response,
|
|
246
|
+
data,
|
|
247
|
+
durationMs,
|
|
248
|
+
options,
|
|
249
|
+
}: {
|
|
250
|
+
request: MetaContext;
|
|
251
|
+
response: Response;
|
|
252
|
+
data: unknown;
|
|
253
|
+
durationMs: number;
|
|
254
|
+
options?: RequestLoggerOptions;
|
|
255
|
+
}): string => {
|
|
256
|
+
const id = request.meta?.requestId ?? UNKNOWN_ID;
|
|
257
|
+
|
|
258
|
+
const showMethod = getOptionOrDefault(options, 'showMethod');
|
|
259
|
+
const showUrl = getOptionOrDefault(options, 'showUrl');
|
|
260
|
+
const showStatus = getOptionOrDefault(options, 'showStatus');
|
|
261
|
+
const showDuration = getOptionOrDefault(options, 'showDuration');
|
|
262
|
+
const showResponseContentType = getOptionOrDefault(options, 'showContentType');
|
|
263
|
+
const showResponseHeaders = getOptionOrDefault(options, 'showHeaders').response;
|
|
264
|
+
const showResponseBody = getOptionOrDefault(options, 'showBody').response;
|
|
265
|
+
const truncateBody = getOptionOrDefault(options, 'truncateBody');
|
|
266
|
+
|
|
267
|
+
return joinLines([
|
|
268
|
+
formatPrimaryLine({
|
|
269
|
+
icon: '✅',
|
|
270
|
+
request,
|
|
271
|
+
id,
|
|
272
|
+
showMethod,
|
|
273
|
+
showUrl,
|
|
274
|
+
}),
|
|
275
|
+
formatStatusDurationLine({
|
|
276
|
+
status: response.status,
|
|
277
|
+
durationMs,
|
|
278
|
+
showStatus,
|
|
279
|
+
showDuration,
|
|
280
|
+
}),
|
|
281
|
+
showResponseContentType
|
|
282
|
+
? `\t↳ Res. Content-Type: ${response.headers.get(Header.ContentType) ?? 'N/A'}`
|
|
283
|
+
: null,
|
|
284
|
+
showResponseHeaders ? `\t↳ Res. Headers: ${formatHeaders(response.headers)}` : null,
|
|
285
|
+
showResponseBody
|
|
286
|
+
? formatBodyLine({
|
|
287
|
+
label: 'Res. Body',
|
|
288
|
+
body: data,
|
|
289
|
+
truncateBody,
|
|
290
|
+
})
|
|
291
|
+
: null,
|
|
292
|
+
]);
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const defaultRetryFormatter = ({
|
|
296
|
+
request,
|
|
297
|
+
attempt,
|
|
298
|
+
options,
|
|
299
|
+
}: {
|
|
300
|
+
request: MetaContext;
|
|
301
|
+
attempt: number;
|
|
302
|
+
options?: RequestLoggerOptions;
|
|
303
|
+
}): string => {
|
|
304
|
+
const id = request.meta?.requestId ?? UNKNOWN_ID;
|
|
305
|
+
|
|
306
|
+
const showMethod = getOptionOrDefault(options, 'showMethod');
|
|
307
|
+
const showUrl = getOptionOrDefault(options, 'showUrl');
|
|
308
|
+
const showRetryAttempt = getOptionOrDefault(options, 'showRetryAttempt');
|
|
309
|
+
|
|
310
|
+
return joinLines([
|
|
311
|
+
formatPrimaryLine({
|
|
312
|
+
icon: '🔁',
|
|
313
|
+
request,
|
|
314
|
+
id,
|
|
315
|
+
showMethod,
|
|
316
|
+
showUrl,
|
|
317
|
+
suffix: 'Retrying...',
|
|
318
|
+
}),
|
|
319
|
+
showRetryAttempt ? `\t↳ Attempt: ${attempt}` : null,
|
|
320
|
+
]);
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const defaultErrorFormatter = ({
|
|
324
|
+
request,
|
|
325
|
+
response,
|
|
326
|
+
error,
|
|
327
|
+
durationMs,
|
|
328
|
+
options,
|
|
329
|
+
}: {
|
|
330
|
+
request: MetaContext;
|
|
331
|
+
response?: Response;
|
|
332
|
+
error: unknown;
|
|
333
|
+
durationMs: number;
|
|
334
|
+
options?: RequestLoggerOptions;
|
|
335
|
+
}): string => {
|
|
336
|
+
const id = request.meta?.requestId ?? UNKNOWN_ID;
|
|
337
|
+
|
|
338
|
+
const showMethod = getOptionOrDefault(options, 'showMethod');
|
|
339
|
+
const showUrl = getOptionOrDefault(options, 'showUrl');
|
|
340
|
+
const showStatus = getOptionOrDefault(options, 'showStatus');
|
|
341
|
+
const showDuration = getOptionOrDefault(options, 'showDuration');
|
|
342
|
+
const showResponseHeaders = getOptionOrDefault(options, 'showHeaders').response;
|
|
343
|
+
const truncateBody = getOptionOrDefault(options, 'truncateBody');
|
|
344
|
+
|
|
345
|
+
return joinLines([
|
|
346
|
+
formatPrimaryLine({
|
|
347
|
+
icon: '❌',
|
|
348
|
+
request,
|
|
349
|
+
id,
|
|
350
|
+
showMethod,
|
|
351
|
+
showUrl,
|
|
352
|
+
}),
|
|
353
|
+
formatStatusDurationLine({
|
|
354
|
+
status: response?.status ?? 'unknown',
|
|
355
|
+
durationMs,
|
|
356
|
+
showStatus,
|
|
357
|
+
showDuration,
|
|
358
|
+
}),
|
|
359
|
+
showResponseHeaders && response ? `\t↳ Res. Headers: ${formatHeaders(response.headers)}` : null,
|
|
360
|
+
truncateBody
|
|
361
|
+
? `\t↳ Error: ${preview(error)}`
|
|
362
|
+
: `\t↳ Error:\n${stringify(error) ?? '[unserializable]'}`,
|
|
363
|
+
]);
|
|
45
364
|
};
|
|
46
365
|
|
|
47
366
|
/**
|
|
48
|
-
* A logger for
|
|
49
|
-
* Each request is given a unique ID which is paired with
|
|
50
|
-
*
|
|
51
|
-
* It currently, prints the method, url, `Accept` and `Content-Type` headers, and the body of the request, and it prints the
|
|
52
|
-
* `Content-Type` header, status code, response body and duration of the response, for both successful and failed responses.
|
|
367
|
+
* A logger for fetch functions that prints detailed information for each request and response.
|
|
368
|
+
* Each request is given a unique ID which is paired with its response.
|
|
53
369
|
*/
|
|
54
|
-
export const requestLogger = (
|
|
370
|
+
export const requestLogger = (
|
|
371
|
+
options?: RequestLoggerOptions,
|
|
372
|
+
): BetterFetchPlugin<RequestLoggerMessageExtras> => {
|
|
55
373
|
const enabled =
|
|
56
|
-
typeof options?.enabled !== 'function' ? () => options?.enabled ?? true : options
|
|
57
|
-
|
|
58
|
-
const maxPreview = options?.maxPreviewLength ?? 150;
|
|
374
|
+
typeof options?.enabled !== 'function' ? () => options?.enabled ?? true : options.enabled;
|
|
59
375
|
|
|
60
376
|
const timings = new Map<string, number>();
|
|
61
377
|
|
|
@@ -64,99 +380,119 @@ export const requestLogger = (options?: RequestLoggerOptions): BetterFetchPlugin
|
|
|
64
380
|
return {
|
|
65
381
|
id: 'logger',
|
|
66
382
|
name: 'Logger',
|
|
67
|
-
version: '0.
|
|
383
|
+
version: '0.3.0',
|
|
68
384
|
hooks: {
|
|
69
385
|
hookOptions: {
|
|
70
386
|
cloneResponse: true,
|
|
71
387
|
},
|
|
72
388
|
|
|
73
389
|
onRequest: async (context) => {
|
|
74
|
-
if (!(await enabled
|
|
390
|
+
if (!(await enabled())) {
|
|
75
391
|
return;
|
|
76
392
|
}
|
|
77
393
|
|
|
78
|
-
const
|
|
394
|
+
const request = context as MetaContext;
|
|
79
395
|
|
|
80
|
-
|
|
396
|
+
request.meta = request.meta ?? ({} as RequestMeta);
|
|
397
|
+
request.loggerOverrides = request.loggerOverrides ?? {};
|
|
81
398
|
|
|
82
399
|
const id = genId();
|
|
83
400
|
|
|
84
|
-
|
|
85
|
-
|
|
401
|
+
request.meta.requestId = id;
|
|
402
|
+
request.headers.set(Header.XRequestId, id);
|
|
86
403
|
|
|
87
404
|
timings.set(id, performance.now());
|
|
88
405
|
|
|
89
|
-
const { url, method, body, headers } = context;
|
|
90
|
-
|
|
91
|
-
const bodyPreview = body ? preview(body, maxPreview) : '';
|
|
92
|
-
const contentType = headers.get(Header.ContentType) ?? 'N/A';
|
|
93
|
-
const accept = headers.get(Header.Accept) ?? 'N/A';
|
|
94
|
-
|
|
95
406
|
const message =
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
407
|
+
request.loggerOverrides.requestFormat?.({ request }) ??
|
|
408
|
+
defaultRequestFormatter({
|
|
409
|
+
request,
|
|
410
|
+
options,
|
|
411
|
+
});
|
|
100
412
|
|
|
101
|
-
console.log(message);
|
|
413
|
+
console.log(`${message}\n`);
|
|
102
414
|
},
|
|
103
415
|
|
|
104
416
|
onSuccess: async (context) => {
|
|
105
|
-
if (!(await enabled
|
|
417
|
+
if (!(await enabled())) {
|
|
106
418
|
return;
|
|
107
419
|
}
|
|
108
420
|
|
|
109
|
-
const
|
|
421
|
+
const request = context.request as MetaContext;
|
|
422
|
+
const { response, data } = context;
|
|
110
423
|
|
|
111
|
-
const id =
|
|
112
|
-
|
|
113
|
-
const status = response.status;
|
|
114
|
-
const bodyPreview = preview(data, maxPreview);
|
|
115
|
-
const contentType = response.headers.get(Header.ContentType) ?? 'N/A';
|
|
116
|
-
const duration = (performance.now() - (timings.get(id) ?? 0)).toFixed(1);
|
|
424
|
+
const id = request.meta?.requestId ?? UNKNOWN_ID;
|
|
425
|
+
const durationMs = getDurationMs(timings, id);
|
|
117
426
|
|
|
118
427
|
const message =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
428
|
+
request.loggerOverrides?.successFormat?.({
|
|
429
|
+
request,
|
|
430
|
+
response,
|
|
431
|
+
data,
|
|
432
|
+
durationMs,
|
|
433
|
+
}) ??
|
|
434
|
+
defaultSuccessFormatter({
|
|
435
|
+
request,
|
|
436
|
+
response,
|
|
437
|
+
data,
|
|
438
|
+
durationMs,
|
|
439
|
+
options,
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
console.log(`${message}\n`);
|
|
443
|
+
timings.delete(id);
|
|
125
444
|
},
|
|
126
445
|
|
|
127
|
-
onRetry: async (
|
|
128
|
-
if (!(await enabled
|
|
446
|
+
onRetry: async (context) => {
|
|
447
|
+
if (!(await enabled())) {
|
|
129
448
|
return;
|
|
130
449
|
}
|
|
131
450
|
|
|
132
|
-
const
|
|
451
|
+
const request = context.request as MetaContext;
|
|
452
|
+
const attempt = (request.retryAttempt ?? 0) + 1;
|
|
133
453
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
454
|
+
const message =
|
|
455
|
+
request.loggerOverrides?.retryFormat?.({
|
|
456
|
+
request,
|
|
457
|
+
attempt,
|
|
458
|
+
}) ??
|
|
459
|
+
defaultRetryFormatter({
|
|
460
|
+
request,
|
|
461
|
+
attempt,
|
|
462
|
+
options,
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
console.warn(`${message}\n`);
|
|
139
466
|
},
|
|
140
467
|
|
|
141
468
|
onError: async (context) => {
|
|
142
|
-
if (!(await enabled
|
|
469
|
+
if (!(await enabled())) {
|
|
143
470
|
return;
|
|
144
471
|
}
|
|
145
472
|
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
const id = (request as MetaContext).meta.requestId ?? UNKNOWN_ID;
|
|
473
|
+
const request = context.request as MetaContext;
|
|
474
|
+
const { response, error } = context;
|
|
149
475
|
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
const duration = (performance.now() - (timings.get(id) ?? 0)).toFixed(1);
|
|
476
|
+
const id = request.meta?.requestId ?? UNKNOWN_ID;
|
|
477
|
+
const durationMs = getDurationMs(timings, id);
|
|
153
478
|
|
|
154
479
|
const message =
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
480
|
+
request.loggerOverrides?.errorFormat?.({
|
|
481
|
+
request,
|
|
482
|
+
response,
|
|
483
|
+
error,
|
|
484
|
+
durationMs,
|
|
485
|
+
}) ??
|
|
486
|
+
defaultErrorFormatter({
|
|
487
|
+
request,
|
|
488
|
+
response,
|
|
489
|
+
error,
|
|
490
|
+
durationMs,
|
|
491
|
+
options,
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
console.error(`${message}\n`);
|
|
495
|
+
timings.delete(id);
|
|
160
496
|
},
|
|
161
497
|
},
|
|
162
498
|
};
|
|
@@ -70,19 +70,23 @@ export const useKeyboardDrawing = ({
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
switch (event.key) {
|
|
73
|
-
case KeyboardKeys.
|
|
73
|
+
case KeyboardKeys.W:
|
|
74
|
+
case KeyboardKeys.I:
|
|
74
75
|
moveCursor(0, -1);
|
|
75
76
|
event.preventDefault();
|
|
76
77
|
break;
|
|
77
|
-
case KeyboardKeys.
|
|
78
|
+
case KeyboardKeys.S:
|
|
79
|
+
case KeyboardKeys.K:
|
|
78
80
|
moveCursor(0, 1);
|
|
79
81
|
event.preventDefault();
|
|
80
82
|
break;
|
|
81
|
-
case KeyboardKeys.
|
|
83
|
+
case KeyboardKeys.A:
|
|
84
|
+
case KeyboardKeys.J:
|
|
82
85
|
moveCursor(-1, 0);
|
|
83
86
|
event.preventDefault();
|
|
84
87
|
break;
|
|
85
|
-
case KeyboardKeys.
|
|
88
|
+
case KeyboardKeys.D:
|
|
89
|
+
case KeyboardKeys.L:
|
|
86
90
|
moveCursor(1, 0);
|
|
87
91
|
event.preventDefault();
|
|
88
92
|
break;
|
package/src/utils/constants.ts
CHANGED
package/src/utils/utils.ts
CHANGED
|
@@ -3,10 +3,10 @@ import { twMerge } from 'tailwind-merge';
|
|
|
3
3
|
|
|
4
4
|
import type { Credentials, DecodedJWT } from '../types/auth';
|
|
5
5
|
|
|
6
|
-
// eslint-disable-next-line
|
|
7
|
-
const isDecodedJWT = (value: object): value is DecodedJWT => {
|
|
8
|
-
|
|
9
|
-
};
|
|
6
|
+
// eslint-disable-next-line sonarjs/no-commented-code
|
|
7
|
+
/* const isDecodedJWT = (value: object): value is DecodedJWT => {
|
|
8
|
+
return 'email' in value && 'name' in value && 'groupInfoIds' in value;
|
|
9
|
+
}; */
|
|
10
10
|
|
|
11
11
|
export const decodeAuthToken = async (token: string): Promise<Credentials | null> => {
|
|
12
12
|
if (!token) {
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import type { ReactNode } from 'react';
|
|
4
|
-
|
|
5
|
-
import { ThemeContext } from '../../contexts';
|
|
6
|
-
import { useLocalStorage } from '../../hooks';
|
|
7
|
-
|
|
8
|
-
export type ThemeProviderProps = {
|
|
9
|
-
children: ReactNode;
|
|
10
|
-
defaultTheme?: 'light' | 'dark';
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export const ThemeProvider = ({ children, defaultTheme = 'light' }: ThemeProviderProps) => {
|
|
14
|
-
const [theme, setTheme] = useLocalStorage<'light' | 'dark'>('theme', defaultTheme);
|
|
15
|
-
|
|
16
|
-
const toggleTheme = () => {
|
|
17
|
-
setTheme(theme === 'light' ? 'dark' : 'light');
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const value = {
|
|
21
|
-
theme,
|
|
22
|
-
toggleTheme,
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<ThemeContext.Provider value={value}>
|
|
27
|
-
<div className={theme === 'dark' ? 'dark' : ''}>{children}</div>
|
|
28
|
-
</ThemeContext.Provider>
|
|
29
|
-
);
|
|
30
|
-
};
|