@arex95/vue-core 3.3.0 → 5.1.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/README.md +196 -55
- package/dist/config/axios/axiosConfig.d.ts +8 -32
- package/dist/index.mjs +228 -230
- package/dist/services/credentials.d.ts +10 -29
- package/dist/services/refreshTokens.d.ts +11 -8
- package/dist/utils/encryption.d.ts +7 -27
- package/dist/utils/storage.d.ts +29 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,97 +1,238 @@
|
|
|
1
1
|
# @arex95/vue-core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Stop rewriting the same API boilerplate in every Vue project.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- **RESTful Standard**: A standardized RESTful class (`RestStd`) that you can extend directly from your models for clean, semantic API calls (e.g., `User.getOne()`).
|
|
8
|
-
- **Fetching Agnostic**: Works with any fetching system (Axios, ofetch, fetch API, or custom fetchers).
|
|
9
|
-
- **Flexible Authentication**: JWT-based authentication system that works with any fetcher (not tied to Axios).
|
|
10
|
-
- **Enhanced Error Handling**: Custom error classes (`NetworkError`, `AuthError`, `ValidationError`, etc.) with structured error information.
|
|
11
|
-
- **Retry Logic**: Built-in retry mechanism with exponential backoff for failed requests.
|
|
12
|
-
- **Secure Storage**: Support for localStorage, sessionStorage, and cookies with encryption and security options (Secure, SameSite).
|
|
13
|
-
- **SSR/SSG Support**: Full support for server-side rendering with automatic cookie fallback.
|
|
14
|
-
- **Type Safety**: Improved generic types for better TypeScript inference and autocompletion.
|
|
15
|
-
- **Utility Functions**: A rich collection of utilities for dates, strings, validations, encryption, and more.
|
|
16
|
-
|
|
17
|
-
## Installation
|
|
5
|
+
`@arex95/vue-core` gives you a battle-tested foundation for REST communication, encrypted token auth, and session management — so you ship features instead of infrastructure.
|
|
18
6
|
|
|
19
7
|
```sh
|
|
20
8
|
npm install @arex95/vue-core
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @arex95/vue-core
|
|
21
11
|
```
|
|
22
12
|
|
|
23
|
-
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Why this library?
|
|
16
|
+
|
|
17
|
+
Every Vue project ends up with the same problem: you need authentication, you need a clean way to talk to your REST API, and you need it to work the same way across every service file. The usual answer is copying patterns between projects and hoping nothing drifts.
|
|
18
|
+
|
|
19
|
+
`@arex95/vue-core` is the answer:
|
|
24
20
|
|
|
25
|
-
|
|
21
|
+
- **`RestStd`** — one class to extend, and your entire API layer is consistent. No more hand-writing `axios.get('/users/'+id)` everywhere.
|
|
22
|
+
- **Fetcher-agnostic** — bring Axios, `ofetch`, the native Fetch API, or your own fetcher. The library doesn't care.
|
|
23
|
+
- **Encrypted token storage** — JWTs stored with AES-CBC-256 via the Web Crypto API. Not plain text in localStorage.
|
|
24
|
+
- **Automatic token refresh** — 401? The library refreshes silently and retries. Your components never see it.
|
|
25
|
+
- **Works in Nuxt SSR** — `setupAuthInterceptors: false` gives you full control for server-side environments.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Setup
|
|
26
30
|
|
|
27
31
|
```typescript
|
|
28
|
-
|
|
29
|
-
import App from './App.vue';
|
|
32
|
+
// main.ts
|
|
30
33
|
import { ArexVueCore } from '@arex95/vue-core';
|
|
31
34
|
|
|
32
|
-
const app = createApp(App);
|
|
33
|
-
|
|
34
35
|
app.use(ArexVueCore, {
|
|
35
|
-
appKey:
|
|
36
|
+
appKey: import.meta.env.VITE_APP_KEY, // used to encrypt tokens at rest
|
|
37
|
+
|
|
36
38
|
endpoints: {
|
|
37
|
-
login:
|
|
38
|
-
refresh: '/
|
|
39
|
-
logout:
|
|
39
|
+
login: 'auth/login',
|
|
40
|
+
refresh: 'auth/refresh',
|
|
41
|
+
logout: 'auth/logout',
|
|
40
42
|
},
|
|
43
|
+
|
|
41
44
|
tokenKeys: {
|
|
42
|
-
accessToken:
|
|
43
|
-
refreshToken: '
|
|
45
|
+
accessToken: 'myapp_access', // storage key name
|
|
46
|
+
refreshToken: 'myapp_refresh',
|
|
44
47
|
},
|
|
48
|
+
|
|
49
|
+
// dot-notation paths to find tokens in your API response
|
|
45
50
|
tokenPaths: {
|
|
46
|
-
accessToken:
|
|
47
|
-
refreshToken: '
|
|
51
|
+
accessToken: 'token', // response.token
|
|
52
|
+
refreshToken: 'refresh_token', // response.refresh_token
|
|
48
53
|
},
|
|
49
54
|
refreshTokenPaths: {
|
|
50
|
-
accessToken:
|
|
51
|
-
refreshToken: '
|
|
55
|
+
accessToken: 'token',
|
|
56
|
+
refreshToken: 'refresh_token',
|
|
52
57
|
},
|
|
58
|
+
|
|
53
59
|
axios: {
|
|
54
|
-
baseURL:
|
|
60
|
+
baseURL: import.meta.env.VITE_API_URL,
|
|
61
|
+
headers: { 'X-API-Key': import.meta.env.VITE_API_KEY },
|
|
62
|
+
setupAuthInterceptors: true, // false for Nuxt SSR
|
|
55
63
|
},
|
|
56
|
-
});
|
|
57
64
|
|
|
58
|
-
|
|
65
|
+
onRefreshFailed: () => router.push('/login'),
|
|
66
|
+
});
|
|
59
67
|
```
|
|
60
68
|
|
|
61
|
-
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## RestStd — the core pattern
|
|
72
|
+
|
|
73
|
+
Extend `RestStd` and get a full CRUD interface for free:
|
|
62
74
|
|
|
63
75
|
```typescript
|
|
64
76
|
import { RestStd } from '@arex95/vue-core';
|
|
65
77
|
|
|
66
|
-
export
|
|
67
|
-
|
|
68
|
-
name: string;
|
|
69
|
-
email: string;
|
|
78
|
+
export class ProductService extends RestStd {
|
|
79
|
+
static override resource = 'catalog/products';
|
|
70
80
|
}
|
|
71
81
|
|
|
72
|
-
|
|
82
|
+
// Now use it anywhere — with TanStack Query, in composables, wherever.
|
|
83
|
+
const products = await ProductService.getAll({ params: { page: 1 } });
|
|
84
|
+
const product = await ProductService.getOne({ id: 42 });
|
|
85
|
+
const created = await ProductService.create({ data: { name: 'Widget' } });
|
|
86
|
+
await ProductService.patch({ id: 42, data: { price: 9.99 } });
|
|
87
|
+
await ProductService.delete({ id: 42 });
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Need a custom endpoint? `customRequest` has you covered:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
export class CheckoutService extends RestStd {
|
|
94
|
+
static override resource = 'sales/checkouts';
|
|
95
|
+
|
|
96
|
+
static complete(data: PaymentData) {
|
|
97
|
+
return this.customRequest({
|
|
98
|
+
method: 'POST',
|
|
99
|
+
url: 'sales/checkout/complete',
|
|
100
|
+
data,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
All requests go through the same globally configured Axios instance — same base URL, same headers, same auth interceptors. Consistent by default.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Authentication
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { useAuth, verifyAuth, cleanCredentials } from '@arex95/vue-core';
|
|
114
|
+
|
|
115
|
+
const { login, logout } = useAuth();
|
|
116
|
+
|
|
117
|
+
// 'local' → localStorage (persists across browser sessions)
|
|
118
|
+
// 'session' → sessionStorage (cleared on tab close)
|
|
119
|
+
// 'cookie' → document.cookie
|
|
120
|
+
await login({ email, password }, 'local');
|
|
121
|
+
|
|
122
|
+
// Check if the user has a valid, non-expired token
|
|
123
|
+
const isAuthed = await verifyAuth(); // → boolean
|
|
124
|
+
|
|
125
|
+
// Logout — clears ALL storage locations so no token survives
|
|
126
|
+
await cleanCredentials('any');
|
|
127
|
+
await logout();
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Tokens are encrypted with **AES-CBC-256** before hitting any storage. Even if someone reads your localStorage, they get ciphertext.
|
|
131
|
+
|
|
132
|
+
### Automatic token refresh
|
|
133
|
+
|
|
134
|
+
When `setupAuthInterceptors: true`, every 401 response triggers a silent refresh:
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
Request → 401
|
|
138
|
+
→ POST /auth/refresh (with refresh_token in body)
|
|
139
|
+
→ New tokens stored
|
|
140
|
+
→ Original request retried
|
|
141
|
+
→ Response returned to your code as if nothing happened
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
If the refresh also fails, `onRefreshFailed` is called — typically a redirect to `/login`.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Fetcher-agnostic
|
|
149
|
+
|
|
150
|
+
Don't want Axios? Swap it out:
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
import { createOfetchFetcher, configAuthFetcher } from '@arex95/vue-core';
|
|
154
|
+
|
|
155
|
+
// Use ofetch globally for auth requests
|
|
156
|
+
configAuthFetcher(createOfetchFetcher('https://api.example.com'));
|
|
157
|
+
|
|
158
|
+
// Or pass a custom fetcher to a specific RestStd subclass
|
|
159
|
+
export class UserService extends RestStd {
|
|
160
|
+
static fetchFn = createOfetchFetcher('https://users.example.com');
|
|
73
161
|
static override resource = 'users';
|
|
74
|
-
// fetchFn is optional if you configured Axios with configAxios()
|
|
75
162
|
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Token Storage
|
|
76
168
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
169
|
+
| location | Stores in | Persistence | `"any"` reads it? |
|
|
170
|
+
|----------|-----------|-------------|-------------------|
|
|
171
|
+
| `'local'` | localStorage | Until explicitly cleared | ✅ |
|
|
172
|
+
| `'session'` | sessionStorage | Until tab closes | ✅ |
|
|
173
|
+
| `'cookie'` | document.cookie | Configurable expiry | ✅ (last) |
|
|
174
|
+
| `'any'` | localStorage | Until explicitly cleared | — |
|
|
175
|
+
|
|
176
|
+
`'local'` is the recommended default for SPAs — persistent, simple, and always found by the automatic interceptors.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Nuxt / SSR Integration
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
// plugins/arex-core.ts
|
|
184
|
+
export default defineNuxtPlugin({
|
|
185
|
+
enforce: 'pre',
|
|
186
|
+
setup() {
|
|
187
|
+
const config = useRuntimeConfig();
|
|
188
|
+
app.use(ArexVueCore, {
|
|
189
|
+
appKey: config.public.appKey,
|
|
190
|
+
// ...
|
|
191
|
+
axios: {
|
|
192
|
+
baseURL: config.public.apiUrl,
|
|
193
|
+
setupAuthInterceptors: false, // handle headers in your own plugin
|
|
194
|
+
},
|
|
195
|
+
onRefreshFailed: () => navigateTo('/auth/login'),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
81
198
|
});
|
|
82
199
|
```
|
|
83
200
|
|
|
84
|
-
|
|
201
|
+
With `setupAuthInterceptors: false` you control exactly how `Authorization` and other headers are attached — essential for SSR where `localStorage` doesn't exist.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Error Handling
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
import { handleError, NetworkError, AuthError, ValidationError } from '@arex95/vue-core';
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
await login(credentials, 'local');
|
|
212
|
+
} catch (error) {
|
|
213
|
+
if (error instanceof AuthError) showError('Invalid credentials');
|
|
214
|
+
if (error instanceof NetworkError) showError(`Connection error ${error.statusCode}`);
|
|
215
|
+
if (error instanceof ValidationError) error.issues.forEach(i => setFieldError(i.field, i.message));
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Full API Reference
|
|
222
|
+
|
|
223
|
+
→ [docs/authentication.md](./docs/authentication.md)
|
|
224
|
+
→ [docs/configuration.md](./docs/configuration.md)
|
|
225
|
+
→ [docs/api-reference.md](./docs/api-reference.md)
|
|
226
|
+
→ [EXAMPLES.md](./EXAMPLES.md)
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Requirements
|
|
85
231
|
|
|
86
|
-
|
|
232
|
+
- Vue 3
|
|
233
|
+
- Node.js 15+ (Web Crypto API required for token encryption)
|
|
234
|
+
- TypeScript recommended
|
|
87
235
|
|
|
88
|
-
|
|
89
|
-
- **`src/config`**: Global configuration for Axios, API endpoints, and tokens.
|
|
90
|
-
- **`src/enums`**: Enums for constants used throughout the library.
|
|
91
|
-
- **`src/fetchers`**: Optional helpers for creating fetchers (Axios, ofetch).
|
|
92
|
-
- **`src/rest`**: A standardized RESTful class (`RestStd`) for CRUD operations.
|
|
93
|
-
- **`src/services`**: Services for authentication and token management.
|
|
94
|
-
- **`src/types`**: TypeScript type definitions.
|
|
95
|
-
- **`src/utils`**: A collection of utility functions.
|
|
236
|
+
## License
|
|
96
237
|
|
|
97
|
-
|
|
238
|
+
MIT
|
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
import { AxiosInstance } from
|
|
2
|
-
import { AxiosServiceOptions } from
|
|
3
|
-
declare module
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
2
|
+
import { AxiosServiceOptions } from '@/types/AxiosServiceOptions';
|
|
3
|
+
declare module 'axios' {
|
|
4
4
|
interface InternalAxiosRequestConfig {
|
|
5
5
|
_retry?: boolean;
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
|
-
/**
|
|
9
|
-
* A service class that encapsulates a customizable Axios instance with built-in interceptors
|
|
10
|
-
* for handling authentication, token refreshing, and request cancellation. It is designed to
|
|
11
|
-
* streamline API communication by automatically attaching authorization headers and managing
|
|
12
|
-
* token refresh logic for 401 Unauthorized responses.
|
|
13
|
-
*/
|
|
14
8
|
export declare class AxiosService {
|
|
15
9
|
private readonly instance;
|
|
16
10
|
private cancelTokenSource;
|
|
@@ -18,37 +12,19 @@ export declare class AxiosService {
|
|
|
18
12
|
private readonly refreshTokenUrl;
|
|
19
13
|
private isRefreshing;
|
|
20
14
|
private failedQueue;
|
|
15
|
+
constructor(options: AxiosServiceOptions);
|
|
21
16
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
17
|
+
* Resolves or rejects all queued promises waiting for a token refresh.
|
|
18
|
+
*
|
|
19
|
+
* Fix: if both error and token are null (edge case), the queue is still
|
|
20
|
+
* cleared to avoid permanently hanging promises.
|
|
24
21
|
*/
|
|
25
|
-
constructor(options: AxiosServiceOptions);
|
|
26
22
|
private processQueue;
|
|
27
23
|
private setAuthHeader;
|
|
28
24
|
private initializeInterceptors;
|
|
29
|
-
/**
|
|
30
|
-
* Returns the number of active (in-flight) requests.
|
|
31
|
-
* @returns {number} The number of active requests.
|
|
32
|
-
*/
|
|
33
25
|
getActiveRequests(): number;
|
|
34
|
-
/**
|
|
35
|
-
* Returns the underlying Axios instance.
|
|
36
|
-
* @returns {AxiosInstance} The Axios instance.
|
|
37
|
-
*/
|
|
38
26
|
getAxiosInstance(): AxiosInstance;
|
|
39
|
-
/**
|
|
40
|
-
* Cancels all ongoing requests made by this Axios instance.
|
|
41
|
-
*/
|
|
42
27
|
cancelAllRequests(): void;
|
|
43
|
-
/**
|
|
44
|
-
* Sets a default header for all subsequent requests.
|
|
45
|
-
* @param {string} key - The header key.
|
|
46
|
-
* @param {string} value - The header value.
|
|
47
|
-
*/
|
|
48
28
|
setHeader(key: string, value: string): void;
|
|
49
|
-
/**
|
|
50
|
-
* Removes a default header.
|
|
51
|
-
* @param {string} key - The header key to remove.
|
|
52
|
-
*/
|
|
53
29
|
removeHeader(key: string): void;
|
|
54
30
|
}
|