mbt-api-client 1.0.4 → 1.1.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/.ai/agent-examples/README.md +77 -0
- package/.ai/agent-reference/README.md +43 -0
- package/.ai/agent-rules/README.md +32 -0
- package/.ai/agent-rules/maintenance.md +20 -0
- package/AGENTS.md +34 -0
- package/README.md +83 -0
- package/dist/index.cjs +1 -142
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -834
- package/dist/index.d.ts +0 -834
- package/dist/index.js +1 -142
- package/dist/index.js.map +1 -1
- package/package.json +26 -21
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Agent Examples
|
|
2
|
+
|
|
3
|
+
Use these examples when a consumer app needs package-level usage patterns for `mbt-api-client`.
|
|
4
|
+
|
|
5
|
+
## App Client Setup
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { createApiClient } from 'mbt-api-client';
|
|
9
|
+
|
|
10
|
+
export const apiClient = createApiClient({
|
|
11
|
+
urls: {
|
|
12
|
+
authService: 'https://api.example.com/auth',
|
|
13
|
+
userProgressService: 'https://api.example.com/progress',
|
|
14
|
+
recommendationService: 'https://api.example.com/recommendations',
|
|
15
|
+
partnerManager: 'https://api.example.com/partners',
|
|
16
|
+
adventureManager: 'https://api.example.com/adventures',
|
|
17
|
+
bff: 'https://api.example.com/bff',
|
|
18
|
+
puzzleController: 'https://api.example.com/puzzles',
|
|
19
|
+
micromodeController: 'https://api.example.com/micromodes',
|
|
20
|
+
userManager: 'https://api.example.com/users',
|
|
21
|
+
monolith: 'https://api.example.com',
|
|
22
|
+
},
|
|
23
|
+
onNavigateToLogin: () => {
|
|
24
|
+
window.location.href = '#/login';
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## React Provider
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { ApiClientProvider } from 'mbt-api-client';
|
|
33
|
+
|
|
34
|
+
<ApiClientProvider value={apiClient}>
|
|
35
|
+
<App />
|
|
36
|
+
</ApiClientProvider>;
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Hook Usage
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { useApiClient } from 'mbt-api-client';
|
|
43
|
+
|
|
44
|
+
function ProfileScreen() {
|
|
45
|
+
const { userManager } = useApiClient();
|
|
46
|
+
|
|
47
|
+
const loadProfile = async () => {
|
|
48
|
+
return userManager.get('/me');
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return <button onClick={() => void loadProfile()}>Load profile</button>;
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Standalone Token Refresh Manager
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { createTokenRefreshManager } from 'mbt-api-client';
|
|
59
|
+
|
|
60
|
+
const tokenManager = createTokenRefreshManager({
|
|
61
|
+
refreshTokenFn: async () => {
|
|
62
|
+
const refreshToken = localStorage.getItem('refreshToken');
|
|
63
|
+
|
|
64
|
+
if (!refreshToken) {
|
|
65
|
+
return { ok: false };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
ok: true,
|
|
70
|
+
accessToken: 'new-access-token',
|
|
71
|
+
refreshToken,
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const nextToken = await tokenManager.handle401();
|
|
77
|
+
```
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Agent Reference
|
|
2
|
+
|
|
3
|
+
This folder holds factual package reference for `mbt-api-client`.
|
|
4
|
+
|
|
5
|
+
## Public Exports
|
|
6
|
+
|
|
7
|
+
- `createApiClient`
|
|
8
|
+
- `ApiClientContext`
|
|
9
|
+
- `ApiClientProvider`
|
|
10
|
+
- `useApiClient`
|
|
11
|
+
- `TokenRefreshManager`
|
|
12
|
+
- `createTokenRefreshManager`
|
|
13
|
+
|
|
14
|
+
## Public Types
|
|
15
|
+
|
|
16
|
+
- `ApiClient`
|
|
17
|
+
- `ApiClientConfig`
|
|
18
|
+
- `ApiClientUrls`
|
|
19
|
+
- `ApiService`
|
|
20
|
+
- `TokenRefreshConfig`
|
|
21
|
+
- `TokenRefreshResult`
|
|
22
|
+
|
|
23
|
+
## Required `ApiClientUrls` Keys
|
|
24
|
+
|
|
25
|
+
- `authService`
|
|
26
|
+
- `userProgressService`
|
|
27
|
+
- `recommendationService`
|
|
28
|
+
- `partnerManager`
|
|
29
|
+
- `adventureManager`
|
|
30
|
+
- `bff`
|
|
31
|
+
- `puzzleController`
|
|
32
|
+
- `micromodeController`
|
|
33
|
+
- `userManager`
|
|
34
|
+
- `monolith`
|
|
35
|
+
|
|
36
|
+
## Maintainer Sources
|
|
37
|
+
|
|
38
|
+
- `README.md` - human quick-start
|
|
39
|
+
- `src/api-client.ts` - client, provider, and config truth
|
|
40
|
+
- `src/token-refresh.ts` - token refresh truth
|
|
41
|
+
- `src/index.ts` - public export surface
|
|
42
|
+
|
|
43
|
+
Reference docs should stay factual. Policy and maintenance rules belong in `.ai/agent-rules/`.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Agent Rules
|
|
2
|
+
|
|
3
|
+
These files are the canonical package-owned rules for `mbt-api-client`.
|
|
4
|
+
|
|
5
|
+
## Rule Map
|
|
6
|
+
|
|
7
|
+
- `.ai/agent-rules/README.md` - normative setup and usage rules.
|
|
8
|
+
- `.ai/agent-rules/maintenance.md` - required rule for keeping docs in sync with public changes.
|
|
9
|
+
|
|
10
|
+
## Usage Rules
|
|
11
|
+
|
|
12
|
+
- Create one configured API client per app shell unless the consumer has a deliberate multi-client design.
|
|
13
|
+
- Wrap React apps with `ApiClientProvider` when component-level access through `useApiClient` is needed.
|
|
14
|
+
- Supply every required `ApiClientUrls` key when creating the client.
|
|
15
|
+
- Keep login redirect behavior explicit through `onNavigateToLogin` when the consumer app has a custom auth flow.
|
|
16
|
+
- Use `createTokenRefreshManager` when a consumer needs standalone 401 retry orchestration outside the built-in client flow.
|
|
17
|
+
- Keep package examples focused on public setup and service usage, not app-specific Redux or router structure.
|
|
18
|
+
- Use kebab-case for file names and folder names across this repo.
|
|
19
|
+
- Use `pnpm` as the package manager for this repo and keep a single lockfile.
|
|
20
|
+
|
|
21
|
+
## Required URL Keys
|
|
22
|
+
|
|
23
|
+
- `authService`
|
|
24
|
+
- `userProgressService`
|
|
25
|
+
- `recommendationService`
|
|
26
|
+
- `partnerManager`
|
|
27
|
+
- `adventureManager`
|
|
28
|
+
- `bff`
|
|
29
|
+
- `puzzleController`
|
|
30
|
+
- `micromodeController`
|
|
31
|
+
- `userManager`
|
|
32
|
+
- `monolith`
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Maintenance Rule
|
|
2
|
+
|
|
3
|
+
Agents working in `mbt-api` must review and update package guidance whenever a change affects public behavior.
|
|
4
|
+
|
|
5
|
+
## Update The Docs When
|
|
6
|
+
|
|
7
|
+
- exported functions, types, or providers change
|
|
8
|
+
- required URL keys change
|
|
9
|
+
- token refresh behavior or config changes
|
|
10
|
+
- setup expectations change
|
|
11
|
+
- canonical examples need to change to stay correct
|
|
12
|
+
|
|
13
|
+
## Required Files To Review
|
|
14
|
+
|
|
15
|
+
- `AGENTS.md`
|
|
16
|
+
- `.ai/agent-rules/README.md`
|
|
17
|
+
- `.ai/agent-reference/README.md`
|
|
18
|
+
- `.ai/agent-examples/README.md`
|
|
19
|
+
|
|
20
|
+
Do not finish public package work without reviewing these files.
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
- This repo owns the canonical agent guidance for the MBT API client package.
|
|
6
|
+
- Repo name: `mbt-api`
|
|
7
|
+
- npm package name: `mbt-api-client`
|
|
8
|
+
- Consumer apps should read the installed package docs from `node_modules/mbt-api-client/...`.
|
|
9
|
+
|
|
10
|
+
## Read This First
|
|
11
|
+
|
|
12
|
+
1. `.ai/agent-rules/README.md`
|
|
13
|
+
2. `.ai/agent-rules/maintenance.md`
|
|
14
|
+
3. `.ai/agent-reference/README.md`
|
|
15
|
+
4. `.ai/agent-examples/README.md`
|
|
16
|
+
|
|
17
|
+
## Guidance Map
|
|
18
|
+
|
|
19
|
+
- `.ai/agent-rules/README.md` - package-owned setup and usage rules.
|
|
20
|
+
- `.ai/agent-rules/maintenance.md` - required doc-sync rule.
|
|
21
|
+
- `.ai/agent-reference/README.md` - exported surfaces and required config map.
|
|
22
|
+
- `.ai/agent-examples/README.md` - package-safe examples for app setup and usage.
|
|
23
|
+
|
|
24
|
+
## Maintenance Rule
|
|
25
|
+
|
|
26
|
+
- If a change affects public exports, required URLs, provider setup, token refresh behavior, or example usage, update the guidance docs before considering the work complete.
|
|
27
|
+
|
|
28
|
+
## Guardrails
|
|
29
|
+
|
|
30
|
+
- Keep docs aligned with the actual `ApiClientUrls` contract.
|
|
31
|
+
- Keep examples focused on public package setup, not one consumer app's internal architecture.
|
|
32
|
+
- Use kebab-case for file names and folder names across this repo.
|
|
33
|
+
- Use `pnpm` as the package manager for this repo.
|
|
34
|
+
- Keep package guidance canonical in `AGENTS.md` and `.ai/` rather than tool-specific wrapper folders.
|
package/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# MBT API Client
|
|
2
|
+
|
|
3
|
+
Typed MBT API client with React context integration and token refresh helpers.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add mbt-api-client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Package Surfaces
|
|
12
|
+
|
|
13
|
+
- `mbt-api-client` - API client factory, React provider, hooks, and token refresh helpers
|
|
14
|
+
|
|
15
|
+
## Agent Guidance
|
|
16
|
+
|
|
17
|
+
- `AGENTS.md` is the package entrypoint for coding agents.
|
|
18
|
+
- Canonical rules live in `.ai/agent-rules/`.
|
|
19
|
+
- Factual reference lives in `.ai/agent-reference/`.
|
|
20
|
+
- Canonical usage examples live in `.ai/agent-examples/`.
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { ApiClientProvider, createApiClient } from 'mbt-api-client';
|
|
26
|
+
|
|
27
|
+
const apiClient = createApiClient({
|
|
28
|
+
urls: {
|
|
29
|
+
authService: 'https://api.example.com/auth',
|
|
30
|
+
userProgressService: 'https://api.example.com/progress',
|
|
31
|
+
recommendationService: 'https://api.example.com/recommendations',
|
|
32
|
+
partnerManager: 'https://api.example.com/partners',
|
|
33
|
+
adventureManager: 'https://api.example.com/adventures',
|
|
34
|
+
bff: 'https://api.example.com/bff',
|
|
35
|
+
puzzleController: 'https://api.example.com/puzzles',
|
|
36
|
+
micromodeController: 'https://api.example.com/micromodes',
|
|
37
|
+
userManager: 'https://api.example.com/users',
|
|
38
|
+
monolith: 'https://api.example.com',
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
<ApiClientProvider value={apiClient}>
|
|
43
|
+
<App />
|
|
44
|
+
</ApiClientProvider>;
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Public Exports
|
|
48
|
+
|
|
49
|
+
- `createApiClient`
|
|
50
|
+
- `ApiClientContext`
|
|
51
|
+
- `ApiClientProvider`
|
|
52
|
+
- `useApiClient`
|
|
53
|
+
- `TokenRefreshManager`
|
|
54
|
+
- `createTokenRefreshManager`
|
|
55
|
+
|
|
56
|
+
## Required URL Keys
|
|
57
|
+
|
|
58
|
+
- `authService`
|
|
59
|
+
- `userProgressService`
|
|
60
|
+
- `recommendationService`
|
|
61
|
+
- `partnerManager`
|
|
62
|
+
- `adventureManager`
|
|
63
|
+
- `bff`
|
|
64
|
+
- `puzzleController`
|
|
65
|
+
- `micromodeController`
|
|
66
|
+
- `userManager`
|
|
67
|
+
- `monolith`
|
|
68
|
+
|
|
69
|
+
## Development
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pnpm install
|
|
73
|
+
pnpm lint
|
|
74
|
+
pnpm format
|
|
75
|
+
pnpm build
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
This repo standardizes on `pnpm` only.
|
|
79
|
+
|
|
80
|
+
## Notes
|
|
81
|
+
|
|
82
|
+
- Package-owned agent guidance is versioned with the library and published with the package.
|
|
83
|
+
- The consumer app should read the installed package docs from `node_modules/mbt-api-client/...`.
|
package/dist/index.cjs
CHANGED
|
@@ -61,19 +61,6 @@ var defaultNavigateToLogin = () => {
|
|
|
61
61
|
window.location.href = "#/login";
|
|
62
62
|
};
|
|
63
63
|
var TokenRefreshManager = class {
|
|
64
|
-
/**
|
|
65
|
-
* Создаёт новый экземпляр TokenRefreshManager
|
|
66
|
-
*
|
|
67
|
-
* @param config - Конфигурация менеджера
|
|
68
|
-
*
|
|
69
|
-
* @example
|
|
70
|
-
* ```typescript
|
|
71
|
-
* const manager = new TokenRefreshManager({
|
|
72
|
-
* refreshTokenFn: myRefreshFunction,
|
|
73
|
-
* onNavigateToLogin: () => router.push('/login'),
|
|
74
|
-
* });
|
|
75
|
-
* ```
|
|
76
|
-
*/
|
|
77
64
|
constructor(config) {
|
|
78
65
|
this.isRefreshing = false;
|
|
79
66
|
this.refreshPromise = null;
|
|
@@ -87,96 +74,20 @@ var TokenRefreshManager = class {
|
|
|
87
74
|
clearTokens: config.clearTokens ?? defaultClearTokens
|
|
88
75
|
};
|
|
89
76
|
}
|
|
90
|
-
/**
|
|
91
|
-
* Получает текущий access токен
|
|
92
|
-
*
|
|
93
|
-
* @returns Access токен или null, если токен отсутствует
|
|
94
|
-
*
|
|
95
|
-
* @example
|
|
96
|
-
* ```typescript
|
|
97
|
-
* const token = manager.getAccessToken();
|
|
98
|
-
* if (token) {
|
|
99
|
-
* headers.Authorization = `Bearer ${token}`;
|
|
100
|
-
* }
|
|
101
|
-
* ```
|
|
102
|
-
*/
|
|
103
77
|
getAccessToken() {
|
|
104
78
|
return this.config.getAccessToken();
|
|
105
79
|
}
|
|
106
|
-
/**
|
|
107
|
-
* Получает текущий refresh токен
|
|
108
|
-
*
|
|
109
|
-
* @returns Refresh токен или null, если токен отсутствует
|
|
110
|
-
*/
|
|
111
80
|
getRefreshToken() {
|
|
112
81
|
return this.config.getRefreshToken();
|
|
113
82
|
}
|
|
114
|
-
/**
|
|
115
|
-
* Проверяет, идёт ли в данный момент процесс обновления токена
|
|
116
|
-
*
|
|
117
|
-
* Используйте для определения, нужно ли ставить запрос в очередь
|
|
118
|
-
* или инициировать новое обновление.
|
|
119
|
-
*
|
|
120
|
-
* @returns `true` если обновление в процессе, `false` если нет
|
|
121
|
-
*
|
|
122
|
-
* @example
|
|
123
|
-
* ```typescript
|
|
124
|
-
* if (manager.isRefreshInProgress()) {
|
|
125
|
-
* // Ждём завершения текущего обновления
|
|
126
|
-
* const newToken = await manager.waitForTokenRefresh();
|
|
127
|
-
* } else {
|
|
128
|
-
* // Инициируем новое обновление
|
|
129
|
-
* await manager.refreshToken();
|
|
130
|
-
* }
|
|
131
|
-
* ```
|
|
132
|
-
*/
|
|
133
83
|
isRefreshInProgress() {
|
|
134
84
|
return this.isRefreshing;
|
|
135
85
|
}
|
|
136
|
-
/**
|
|
137
|
-
* Добавляет запрос в очередь ожидания обновления токена
|
|
138
|
-
*
|
|
139
|
-
* Возвращает промис, который разрешится с новым токеном после
|
|
140
|
-
* успешного обновления или будет отклонён при ошибке.
|
|
141
|
-
*
|
|
142
|
-
* @returns Промис с новым access токеном
|
|
143
|
-
*
|
|
144
|
-
* @example
|
|
145
|
-
* ```typescript
|
|
146
|
-
* // В response interceptor при множественных 401
|
|
147
|
-
* if (manager.isRefreshInProgress()) {
|
|
148
|
-
* try {
|
|
149
|
-
* const newToken = await manager.waitForTokenRefresh();
|
|
150
|
-
* // Повторить запрос с новым токеном
|
|
151
|
-
* } catch {
|
|
152
|
-
* // Обновление не удалось
|
|
153
|
-
* }
|
|
154
|
-
* }
|
|
155
|
-
* ```
|
|
156
|
-
*/
|
|
157
86
|
waitForTokenRefresh() {
|
|
158
87
|
return new Promise((resolve, reject) => {
|
|
159
88
|
this.queuedRequests.push({ resolve, reject });
|
|
160
89
|
});
|
|
161
90
|
}
|
|
162
|
-
/**
|
|
163
|
-
* Выполняет обновление токена
|
|
164
|
-
*
|
|
165
|
-
* Если обновление уже в процессе, вернёт существующий промис.
|
|
166
|
-
* После обновления уведомляет все запросы в очереди.
|
|
167
|
-
*
|
|
168
|
-
* @returns Промис с результатом обновления
|
|
169
|
-
*
|
|
170
|
-
* @example
|
|
171
|
-
* ```typescript
|
|
172
|
-
* const result = await manager.refreshToken();
|
|
173
|
-
* if (result.ok) {
|
|
174
|
-
* console.log('Новый токен:', result.accessToken);
|
|
175
|
-
* } else {
|
|
176
|
-
* console.log('Ошибка обновления токена');
|
|
177
|
-
* }
|
|
178
|
-
* ```
|
|
179
|
-
*/
|
|
180
91
|
async refreshToken() {
|
|
181
92
|
if (this.refreshPromise) {
|
|
182
93
|
return this.refreshPromise;
|
|
@@ -189,33 +100,6 @@ var TokenRefreshManager = class {
|
|
|
189
100
|
});
|
|
190
101
|
return this.refreshPromise;
|
|
191
102
|
}
|
|
192
|
-
/**
|
|
193
|
-
* Обрабатывает ошибку 401 — либо обновляет токен, либо перенаправляет на логин
|
|
194
|
-
*
|
|
195
|
-
* Основной метод для использования в interceptors. Автоматически:
|
|
196
|
-
* - Перенаправляет на логин при невалидном токене
|
|
197
|
-
* - Ставит запрос в очередь, если обновление уже идёт
|
|
198
|
-
* - Инициирует обновление, если ещё не запущено
|
|
199
|
-
*
|
|
200
|
-
* @param isInvalidToken - Если true, токен считается невалидным и будет выполнен переход на логин
|
|
201
|
-
* @returns Промис с новым access токеном или null при ошибке
|
|
202
|
-
*
|
|
203
|
-
* @example
|
|
204
|
-
* ```typescript
|
|
205
|
-
* // В axios response interceptor
|
|
206
|
-
* if (error.response?.status === 401) {
|
|
207
|
-
* const isInvalid = error.response.data?.message === 'Invalid token';
|
|
208
|
-
* const newToken = await manager.handle401(isInvalid);
|
|
209
|
-
*
|
|
210
|
-
* if (newToken) {
|
|
211
|
-
* // Повторить запрос с новым токеном
|
|
212
|
-
* error.config.headers.Authorization = `Bearer ${newToken}`;
|
|
213
|
-
* return axios(error.config);
|
|
214
|
-
* }
|
|
215
|
-
* // newToken === null означает переход на логин
|
|
216
|
-
* }
|
|
217
|
-
* ```
|
|
218
|
-
*/
|
|
219
103
|
async handle401(isInvalidToken = false) {
|
|
220
104
|
if (isInvalidToken) {
|
|
221
105
|
this.config.onNavigateToLogin();
|
|
@@ -231,34 +115,9 @@ var TokenRefreshManager = class {
|
|
|
231
115
|
}
|
|
232
116
|
return result.accessToken || this.config.getAccessToken();
|
|
233
117
|
}
|
|
234
|
-
/**
|
|
235
|
-
* Принудительно перенаправляет на страницу логина
|
|
236
|
-
*
|
|
237
|
-
* Вызывает колбэк onNavigateToLogin из конфигурации.
|
|
238
|
-
*
|
|
239
|
-
* @example
|
|
240
|
-
* ```typescript
|
|
241
|
-
* // При необходимости принудительного выхода
|
|
242
|
-
* manager.navigateToLogin();
|
|
243
|
-
* ```
|
|
244
|
-
*/
|
|
245
118
|
navigateToLogin() {
|
|
246
119
|
this.config.onNavigateToLogin();
|
|
247
120
|
}
|
|
248
|
-
/**
|
|
249
|
-
* Сбрасывает внутреннее состояние менеджера
|
|
250
|
-
*
|
|
251
|
-
* Полезно для тестирования или при необходимости
|
|
252
|
-
* принудительно сбросить очередь запросов.
|
|
253
|
-
*
|
|
254
|
-
* @example
|
|
255
|
-
* ```typescript
|
|
256
|
-
* // В тестах
|
|
257
|
-
* beforeEach(() => {
|
|
258
|
-
* manager.reset();
|
|
259
|
-
* });
|
|
260
|
-
* ```
|
|
261
|
-
*/
|
|
262
121
|
reset() {
|
|
263
122
|
this.isRefreshing = false;
|
|
264
123
|
this.refreshPromise = null;
|
|
@@ -306,7 +165,7 @@ var defaultDecodeJwt = (token) => {
|
|
|
306
165
|
const base64Url = token.split(".")[1];
|
|
307
166
|
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
|
|
308
167
|
const jsonPayload = decodeURIComponent(
|
|
309
|
-
atob(base64).split("").map((c) =>
|
|
168
|
+
atob(base64).split("").map((c) => `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`).join("")
|
|
310
169
|
);
|
|
311
170
|
return JSON.parse(jsonPayload);
|
|
312
171
|
} catch {
|