volter 0.0.174 → 0.0.177
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 +308 -1
- package/dist/crypto.js +1 -2
- package/dist/error.js +31 -15
- package/dist/index.js +116 -19
- package/dist/network.js +0 -0
- package/dist/sessions.js +116 -17
- package/dist/utils.js +32 -16
- package/package.json +6 -4
- package/types/error.d.ts +15 -8
- package/types/index.d.ts +0 -1
- package/types/network.d.ts +6 -0
- package/types/sessions.d.ts +1 -1
- package/types/utils.d.ts +24 -19
- package/dist/monitor.js +0 -4
- package/types/monitor.d.ts +0 -12
package/README.md
CHANGED
|
@@ -1,2 +1,309 @@
|
|
|
1
|
+
<img src="/public/cover.png" alt="Volter" />
|
|
2
|
+
|
|
1
3
|
# Volter
|
|
2
|
-
|
|
4
|
+
|
|
5
|
+
Secure, lightweight session management and authentication library for modern JavaScript applications. Built with Bun runtime optimization, it provides Redis-backed session storage, email verification, encryption utilities, and structured error handling.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Session Management** - Redis-backed sessions with secure token validation
|
|
10
|
+
- **Email Verification** - PIN-based verification with Resend integration
|
|
11
|
+
- **Cryptography** - AES-GCM encryption, RSA key pairs, ECDSA signatures
|
|
12
|
+
- **Security Utilities** - Secure ID generation, hashing, and validation
|
|
13
|
+
- **Error Handling** - Structured error codes and custom error classes
|
|
14
|
+
- **TypeScript Support** - Full type definitions included
|
|
15
|
+
- **Bun Optimized** - Leverages Bun's built-in Redis and crypto APIs
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install volter
|
|
21
|
+
```
|
|
22
|
+
```bash
|
|
23
|
+
bun add volter
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Required Dependencies
|
|
27
|
+
|
|
28
|
+
- **Redis Server** - For session storage
|
|
29
|
+
- **Resend API Key** - For email verification (optional)
|
|
30
|
+
- **Bun Runtime** - Recommended for optimal performance
|
|
31
|
+
- **Node.js** - Also supported (v18+)
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { Sessions } from "volter";
|
|
37
|
+
|
|
38
|
+
// Initialize sessions with Redis
|
|
39
|
+
const sessions = new Sessions({
|
|
40
|
+
expiry: 2592000, // 30 days in seconds
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Create session for user
|
|
44
|
+
const session = await sessions.create("user-123");
|
|
45
|
+
console.log("Session token:", session.token);
|
|
46
|
+
|
|
47
|
+
// Validate session
|
|
48
|
+
const userId = await sessions.validate(session.token);
|
|
49
|
+
console.log("User ID:", userId); // 'user-123'
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## API Overview
|
|
53
|
+
|
|
54
|
+
### Sessions Class
|
|
55
|
+
|
|
56
|
+
Complete session lifecycle management with Redis backend.
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { RedisClient } from "bun"
|
|
60
|
+
import { Sessions } from "volter";
|
|
61
|
+
|
|
62
|
+
const redis = new RedisClient(Bun.env.REDIS_URL)
|
|
63
|
+
|
|
64
|
+
const sessions = new Sessions({
|
|
65
|
+
store: redis, // Optional, defaults to Bun.redis
|
|
66
|
+
expiry: 2592000, // 30 days in seconds
|
|
67
|
+
createID: crypto.randomUUID, // Optional custom ID generator
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Create new session
|
|
71
|
+
const session = await sessions.create("user-id");
|
|
72
|
+
|
|
73
|
+
// Validate session token
|
|
74
|
+
const userId = await sessions.validate(token);
|
|
75
|
+
|
|
76
|
+
// Get session details
|
|
77
|
+
const details = await sessions.get(token);
|
|
78
|
+
|
|
79
|
+
// List all user sessions
|
|
80
|
+
const userSessions = await sessions.list("user-id");
|
|
81
|
+
|
|
82
|
+
// Revoke session
|
|
83
|
+
await sessions.revoke(token);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Email Verification (e1T)
|
|
87
|
+
|
|
88
|
+
PIN-based email verification with Resend integration.
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { e1T } from "volter";
|
|
92
|
+
import { Resend } from "resend";
|
|
93
|
+
|
|
94
|
+
const e1t = new e1T({
|
|
95
|
+
resend: new Resend("re_xxxxxxxxx"),
|
|
96
|
+
expiry: 300, // 5 minutes
|
|
97
|
+
attempts: 5, // Max verification attempts
|
|
98
|
+
template: (email, code) => ({
|
|
99
|
+
from: "noreply@app.dev",
|
|
100
|
+
to: email,
|
|
101
|
+
subject: `${code}: is your verification code.`,
|
|
102
|
+
text: `Enter ${code} to verify your email. For your security, do not share.`,
|
|
103
|
+
}),
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Send verification code
|
|
107
|
+
const result = await e1t.send("user@app.dev");
|
|
108
|
+
console.log("Verification code:", result.code);
|
|
109
|
+
|
|
110
|
+
// Verify code
|
|
111
|
+
const isValid = await e1t.verify("user@app.dev", "855 004");
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Cryptography Utilities
|
|
115
|
+
|
|
116
|
+
Secure encryption, signing, and key management.
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import {
|
|
120
|
+
cipher,
|
|
121
|
+
decipher,
|
|
122
|
+
sign,
|
|
123
|
+
verifySign,
|
|
124
|
+
generateECDSAKeyPair,
|
|
125
|
+
hash,
|
|
126
|
+
} from "volter";
|
|
127
|
+
|
|
128
|
+
// AES-GCM encryption
|
|
129
|
+
const encrypted = await cipher("secret data", "encryption-key");
|
|
130
|
+
const decrypted = await decipher(encrypted, "encryption-key");
|
|
131
|
+
|
|
132
|
+
// Digital signatures
|
|
133
|
+
const keyPair = await generateECDSAKeyPair();
|
|
134
|
+
const signature = await sign("message to sign", keyPair.privateKey);
|
|
135
|
+
const isValid = await verifySign(
|
|
136
|
+
"message to sign",
|
|
137
|
+
signature,
|
|
138
|
+
keyPair.publicKey,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// Secure hashing
|
|
142
|
+
const hashed = hash("password", "salt");
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Error Handling
|
|
146
|
+
|
|
147
|
+
Structured error codes and custom error classes.
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { ServerError, ErrorCodes, ValidationError } from "volter";
|
|
151
|
+
|
|
152
|
+
// Custom server error
|
|
153
|
+
throw new ServerError("User not found", {
|
|
154
|
+
code: ErrorCodes.RESOURCE_NOT_FOUND,
|
|
155
|
+
at: ["UserService", "getUserById"],
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Validation with Zod
|
|
159
|
+
import { z } from "zod";
|
|
160
|
+
const schema = z.string().email();
|
|
161
|
+
try {
|
|
162
|
+
schema.parse("invalid-email");
|
|
163
|
+
} catch (error) {
|
|
164
|
+
throw new ValidationError("Invalid email format");
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Configuration
|
|
169
|
+
|
|
170
|
+
### Redis Setup
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
// Using default Bun Redis
|
|
174
|
+
const sessions = new Sessions();
|
|
175
|
+
|
|
176
|
+
// Using custom Redis client
|
|
177
|
+
const sessions = new Sessions({
|
|
178
|
+
store: new RedisClient({
|
|
179
|
+
host: "localhost",
|
|
180
|
+
port: 6379,
|
|
181
|
+
password: "your-password",
|
|
182
|
+
}),
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Resend Email Setup
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
import { Resend } from "resend";
|
|
190
|
+
|
|
191
|
+
const emailVerify = new e1T({
|
|
192
|
+
resend: new Resend(process.env.RESEND_API_KEY),
|
|
193
|
+
template: (email, code) => ({
|
|
194
|
+
from: process.env.FROM_EMAIL,
|
|
195
|
+
to: email,
|
|
196
|
+
subject: `Verify your account - Code: ${code}`,
|
|
197
|
+
html: `<h1>Your verification code is: <strong>${code}</strong></h1>`,
|
|
198
|
+
}),
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Security Best Practices
|
|
203
|
+
|
|
204
|
+
- Always use HTTPS in production
|
|
205
|
+
- Store sensitive keys in environment variables
|
|
206
|
+
- Set appropriate session expiry times
|
|
207
|
+
- Implement rate limiting for verification attempts
|
|
208
|
+
- Use secure, random session tokens
|
|
209
|
+
- Regularly rotate encryption keys
|
|
210
|
+
|
|
211
|
+
## TypeScript Support
|
|
212
|
+
|
|
213
|
+
Volter includes full TypeScript definitions:
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import {
|
|
217
|
+
Sessions,
|
|
218
|
+
Session,
|
|
219
|
+
SessionPayload,
|
|
220
|
+
e1T,
|
|
221
|
+
ServerError,
|
|
222
|
+
ErrorCodes,
|
|
223
|
+
} from "volter";
|
|
224
|
+
|
|
225
|
+
// Full type safety and IntelliSense support
|
|
226
|
+
const sessions: Sessions = new Sessions();
|
|
227
|
+
const session: Session = await sessions.get(token);
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Examples
|
|
231
|
+
|
|
232
|
+
### Web Server Integration
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
import { Sessions } from "volter";
|
|
236
|
+
|
|
237
|
+
const sessions = new Sessions();
|
|
238
|
+
|
|
239
|
+
// Middleware for session validation
|
|
240
|
+
export async function requireAuth(req: Request) {
|
|
241
|
+
const token = req.headers.get("authorization")?.replace("Bearer ", "");
|
|
242
|
+
if (!token)
|
|
243
|
+
throw new ServerError("No token provided", {
|
|
244
|
+
code: ErrorCodes.AUTHENTICATION_FAILED,
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
const userId = await sessions.validate(token);
|
|
248
|
+
if (!userId)
|
|
249
|
+
throw new ServerError("Invalid token", { code: ErrorCodes.INVALID_TOKEN });
|
|
250
|
+
|
|
251
|
+
return userId;
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Complete Auth Flow
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
import { Sessions, e1T } from "volter";
|
|
259
|
+
|
|
260
|
+
const sessions = new Sessions();
|
|
261
|
+
const emailVerify = new e1T();
|
|
262
|
+
|
|
263
|
+
// 1. Send verification code
|
|
264
|
+
await emailVerify.send(email);
|
|
265
|
+
|
|
266
|
+
// 2. Verify email and create session
|
|
267
|
+
const isValid = await emailVerify.verify(email, code);
|
|
268
|
+
if (isValid) {
|
|
269
|
+
const session = await sessions.create(userId);
|
|
270
|
+
return { token: session.token };
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Contributing
|
|
275
|
+
|
|
276
|
+
We welcome contributions! Please follow these steps:
|
|
277
|
+
|
|
278
|
+
1. Fork the repository
|
|
279
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
280
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
281
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
282
|
+
5. Open a Pull Request
|
|
283
|
+
|
|
284
|
+
### Development Setup
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
# Clone the repository
|
|
288
|
+
git clone https://github.com/your-org/volter.git
|
|
289
|
+
cd volter
|
|
290
|
+
|
|
291
|
+
# Install dependencies
|
|
292
|
+
bun install
|
|
293
|
+
|
|
294
|
+
# Run tests
|
|
295
|
+
bun test
|
|
296
|
+
|
|
297
|
+
# Build the project
|
|
298
|
+
bun run build
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## License
|
|
302
|
+
|
|
303
|
+
The Modlin Distributable License (MDL) © [Modlin](https://modlin.dev) - See [LICENSE](LICENSE) file for details
|
|
304
|
+
|
|
305
|
+
## Support
|
|
306
|
+
|
|
307
|
+
- [Documentation](https://modlin.dev/volter/docs)
|
|
308
|
+
- [Issue Tracker](https://github.com/modlin-dev/volter/issues)
|
|
309
|
+
- [Email Support](mailto:volter@modlin.dev)
|
package/dist/crypto.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
var c=Object.create;var{getPrototypeOf:s,defineProperty:o,getOwnPropertyNames:p}=Object;var u=Object.prototype.hasOwnProperty;var A=(e,t,r)=>{r=e!=null?c(s(e)):{};let n=t||!e||!e.__esModule?o(r,"default",{value:e,enumerable:!0}):r;for(let a of p(e))if(!u.call(n,a))o(n,a,{get:()=>e[a],enumerable:!0});return n};var d=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var m=(e,t)=>{for(var r in t)o(e,r,{get:t[r],enumerable:!0,configurable:!0,set:(n)=>t[r]=()=>n})};var x=import.meta.require;function f(e,t){return new Bun.CryptoHasher("sha256",t).update(e).digest("hex")}async function i(e){if(e){if(e instanceof CryptoKey)return e;if(e instanceof Uint8Array){if(e.length===32)return await crypto.subtle.importKey("raw",e,{name:"AES-GCM",length:256},!0,["encrypt","decrypt"]);return await crypto.subtle.importKey("raw",e,{name:"AES-GCM",length:256},!0,["encrypt","decrypt"])}if(e.length===64)return await crypto.subtle.importKey("raw",Uint8Array.fromHex(e),{name:"AES-GCM",length:256},!0,["encrypt","decrypt"]);else return await crypto.subtle.importKey("raw",Uint8Array.fromHex(f(e)),{name:"AES-GCM",length:256},!0,["encrypt","decrypt"])}else return await crypto.subtle.generateKey({name:"AES-GCM",length:256},!0,["encrypt","decrypt"])}async function C(e,t){let r=await crypto.subtle.exportKey(t,e);return new Uint8Array(r).toBase64()}async function K(){return await crypto.subtle.generateKey({name:"ECDSA",namedCurve:"P-256"},!0,["sign","verify"])}async function l(e){return await crypto.subtle.exportKey("jwk",e)}async function w(e,t){return await crypto.subtle.importKey("jwk",e,{name:"ECDSA",namedCurve:"P-256"},!0,t)}async function b(e,t){let r=new TextEncoder().encode(e),n=await crypto.subtle.sign({name:"ECDSA",hash:{name:"SHA-256"}},t,r);return new Uint8Array(n)}async function h(e,t,r){let n=new TextEncoder().encode(e);return await crypto.subtle.verify({name:"ECDSA",hash:{name:"SHA-256"}},r,t,n)}async function E(e,t){let n=new TextEncoder().encode(e),a=crypto.getRandomValues(new Uint8Array(12)),y=await crypto.subtle.encrypt({name:"AES-GCM",iv:a},await i(t),n);return{text:new Uint8Array(y).toHex(),iv:a,buffer:y}}async function S(e,t){let r=new TextDecoder,n=await crypto.subtle.decrypt({name:"AES-GCM",iv:e.iv},await i(t),Uint8Array.fromHex(e.text));return r.decode(n)}async function P(){return await crypto.subtle.generateKey({name:"RSA-OAEP",modulusLength:2048,publicExponent:new Uint8Array([1,0,1]),hash:{name:"SHA-256"}},!0,["encrypt","decrypt"])}async function U(e,t){let r=new TextEncoder().encode(e),n=await crypto.subtle.encrypt({name:"RSA-OAEP"},t,r);return new Uint8Array(n)}async function v(e,t){let r=await crypto.subtle.decrypt({name:"RSA-OAEP"},t,e);return new TextDecoder().decode(r)}export{h as verifySign,b as sign,P as keypair,w as importJWK,f as hash,K as generateECDSAKeyPair,C as exportKey,l as exportJWK,U as encrypt,v as decrypt,S as decipher,i as cryptoKey,E as cipher};
|
|
1
|
+
var u=Object.create;var{getPrototypeOf:f,defineProperty:o,getOwnPropertyNames:c,getOwnPropertyDescriptor:A}=Object,s=Object.prototype.hasOwnProperty;var m=(e,t,r)=>{r=e!=null?u(f(e)):{};let n=t||!e||!e.__esModule?o(r,"default",{value:e,enumerable:!0}):r;for(let a of c(e))if(!s.call(n,a))o(n,a,{get:()=>e[a],enumerable:!0});return n},i=new WeakMap,x=(e)=>{var t=i.get(e),r;if(t)return t;if(t=o({},"__esModule",{value:!0}),e&&typeof e==="object"||typeof e==="function")c(e).map((n)=>!s.call(t,n)&&o(t,n,{get:()=>e[n],enumerable:!(r=A(e,n))||r.enumerable}));return i.set(e,t),t},g=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var C=(e,t)=>{for(var r in t)o(e,r,{get:t[r],enumerable:!0,configurable:!0,set:(n)=>t[r]=()=>n})};var K=(e,t)=>()=>(e&&(t=e(e=0)),t);var l=((e)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,r)=>(typeof require<"u"?require:t)[r]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});function d(e,t){return new Bun.CryptoHasher("sha256",t).update(e).digest("hex")}async function p(e){if(e){if(e instanceof CryptoKey)return e;if(e instanceof Uint8Array){if(e.length===32)return await crypto.subtle.importKey("raw",e,{name:"AES-GCM",length:256},!0,["encrypt","decrypt"]);return await crypto.subtle.importKey("raw",e,{name:"AES-GCM",length:256},!0,["encrypt","decrypt"])}if(e.length===64)return await crypto.subtle.importKey("raw",Uint8Array.fromHex(e),{name:"AES-GCM",length:256},!0,["encrypt","decrypt"]);else return await crypto.subtle.importKey("raw",Uint8Array.fromHex(d(e)),{name:"AES-GCM",length:256},!0,["encrypt","decrypt"])}else return await crypto.subtle.generateKey({name:"AES-GCM",length:256},!0,["encrypt","decrypt"])}async function b(e,t){let r=await crypto.subtle.exportKey(t,e);return new Uint8Array(r).toBase64()}async function h(){return await crypto.subtle.generateKey({name:"ECDSA",namedCurve:"P-256"},!0,["sign","verify"])}async function E(e){return await crypto.subtle.exportKey("jwk",e)}async function S(e,t){return await crypto.subtle.importKey("jwk",e,{name:"ECDSA",namedCurve:"P-256"},!0,t)}async function P(e,t){let r=new TextEncoder().encode(e),n=await crypto.subtle.sign({name:"ECDSA",hash:{name:"SHA-256"}},t,r);return new Uint8Array(n)}async function U(e,t,r){let n=new TextEncoder().encode(e);return await crypto.subtle.verify({name:"ECDSA",hash:{name:"SHA-256"}},r,t,n)}async function v(e,t){let n=new TextEncoder().encode(e),a=crypto.getRandomValues(new Uint8Array(12)),y=await crypto.subtle.encrypt({name:"AES-GCM",iv:a},await p(t),n);return{text:new Uint8Array(y).toHex(),iv:a,buffer:y}}async function B(e,t){let r=new TextDecoder,n=await crypto.subtle.decrypt({name:"AES-GCM",iv:e.iv},await p(t),Uint8Array.fromHex(e.text));return r.decode(n)}async function T(){return await crypto.subtle.generateKey({name:"RSA-OAEP",modulusLength:2048,publicExponent:new Uint8Array([1,0,1]),hash:{name:"SHA-256"}},!0,["encrypt","decrypt"])}async function H(e,t){let r=new TextEncoder().encode(e),n=await crypto.subtle.encrypt({name:"RSA-OAEP"},t,r);return new Uint8Array(n)}async function D(e,t){let r=await crypto.subtle.decrypt({name:"RSA-OAEP"},t,e);return new TextDecoder().decode(r)}export{U as verifySign,P as sign,T as keypair,S as importJWK,d as hash,h as generateECDSAKeyPair,b as exportKey,E as exportJWK,H as encrypt,D as decrypt,B as decipher,p as cryptoKey,v as cipher};
|