dolphin-server-modules 1.0.3 → 1.0.4
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 +51 -108
- package/dist/router/router.d.ts +1 -1
- package/dist/server/server.d.ts +6 -6
- package/dist/server/server.js +24 -9
- package/dist/server/server.js.map +1 -1
- package/package.json +1 -1
- package/router/router.ts +1 -1
- package/scripts/benchmark.js +12 -0
- package/scripts/benchmark.ts +12 -0
- package/scripts/test-dolphin.js +27 -0
- package/server/server.ts +26 -9
package/README.md
CHANGED
|
@@ -1,140 +1,83 @@
|
|
|
1
|
-
# Dolphin
|
|
1
|
+
# Dolphin Framework 🐬
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Dolphin** is a world-class, ultra-lightweight, and 100% modular backend framework built on native Node.js. It is designed for extreme performance, minimal boilerplate, and a developer-first experience.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> "Close to native Node.js speed, with the developer experience of a premium framework."
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
---
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
## 🚀 Core Philosophy
|
|
10
|
+
- **Zero-Dependency Core**: Built on the native Node.js `http` module. No Express, no Fastify overhead.
|
|
11
|
+
- **Extreme Modularity**: Use only what you need. Auth, CRUD, and Routing are all independent.
|
|
12
|
+
- **Performance First**: Optimized matching engines and minimal object allocation.
|
|
13
|
+
- **Type-Safe by Design**: First-class TypeScript support across all modules.
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
npm install argon2 zod
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## 🚀 100% Modular Structure
|
|
19
|
-
You can import exactly what you need without bloating your project.
|
|
15
|
+
---
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
import { createDolphinController } from 'dolphin-server-modules/controller';
|
|
25
|
-
import { validateStructure } from 'dolphin-server-modules/middleware/zod';
|
|
17
|
+
## 📦 Installation
|
|
18
|
+
```bash
|
|
19
|
+
npm install dolphin-server-modules
|
|
26
20
|
```
|
|
27
21
|
|
|
28
22
|
---
|
|
29
23
|
|
|
30
|
-
##
|
|
31
|
-
Production-ready authentication supporting **argon2 password hashing**, **JWT**, and **TOTP (2FA)**. It also ships with internal memory LRU caches for protecting against token-reuse and rate-limiting.
|
|
24
|
+
## 🛠️ Key Features
|
|
32
25
|
|
|
26
|
+
### ⚡ 1. Native High-Performance Server (`/server`)
|
|
27
|
+
A thin wrapper around native `http` with a modern `Context` (ctx) based API.
|
|
33
28
|
```typescript
|
|
34
|
-
import {
|
|
29
|
+
import { createDolphinServer } from 'dolphin-server-modules/server';
|
|
35
30
|
|
|
36
|
-
const
|
|
37
|
-
secret: 'YOUR_SUPER_SECRET_KEY',
|
|
38
|
-
cookieMaxAge: 7 * 86400000 // 7 days
|
|
39
|
-
});
|
|
31
|
+
const app = createDolphinServer();
|
|
40
32
|
|
|
41
|
-
|
|
42
|
-
const user = await auth.register(db, { email: 'user@example.com', password: 'password123' });
|
|
33
|
+
app.get('/ping', (ctx) => ctx.json({ message: 'pong' }));
|
|
43
34
|
|
|
44
|
-
|
|
45
|
-
const session = await auth.login(db, { email: 'user@example.com', password: 'password123' });
|
|
46
|
-
console.log(session.accessToken);
|
|
35
|
+
app.listen(3000);
|
|
47
36
|
```
|
|
48
37
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
## 💾 2. CRUD Module (`/crud`)
|
|
52
|
-
A powerful Factory interface for databases providing instant pagination, soft-deletes, and ownership-checks out of the box.
|
|
53
|
-
|
|
38
|
+
### 🛣️ 2. Intelligent Routing (`/router`)
|
|
39
|
+
Uses a hybrid Radix Tree + Static Map approach for $O(1)$ and $O(L)$ matching. Supports dynamic path parameters out of the box.
|
|
54
40
|
```typescript
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const crud = createCRUD(myDatabaseAdapter, {
|
|
58
|
-
softDelete: true,
|
|
59
|
-
enforceOwnership: true
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// Automatically creates a record with generated IDs and createdAt fields
|
|
63
|
-
const newPost = await crud.create('posts', { title: 'Hello World' }, 'user_id_1');
|
|
64
|
-
|
|
65
|
-
// Read with Advanced Filtering
|
|
66
|
-
const posts = await crud.read('posts', {
|
|
67
|
-
$or: [{ title: { $like: 'Hello' } }, { rating: { $gt: 4 } }]
|
|
41
|
+
app.get('/users/:id', (ctx) => {
|
|
42
|
+
return ctx.json({ userId: ctx.params.id });
|
|
68
43
|
});
|
|
69
|
-
|
|
70
|
-
// Soft Delete
|
|
71
|
-
await crud.deleteOne('posts', newPost.id, 'user_id_1');
|
|
72
|
-
|
|
73
|
-
// Restore Soft Deleted
|
|
74
|
-
await crud.restore('posts', newPost.id, 'user_id_1');
|
|
75
44
|
```
|
|
76
45
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
import { createDolphinController, createNextAppRoute } from 'dolphin-server-modules/controller';
|
|
46
|
+
### 🔒 3. Advanced Auth Module (`/auth`)
|
|
47
|
+
Production-ready security with zero external bloat:
|
|
48
|
+
- Argon2 Hashing & JWT (Timing-safe)
|
|
49
|
+
- Refresh Token Rotation & Reuse Detection
|
|
50
|
+
- 2FA (TOTP) + Recovery Code Management
|
|
84
51
|
|
|
85
|
-
|
|
52
|
+
### 💾 4. Adapter-Based CRUD & Database (`/crud`)
|
|
53
|
+
Seamlessly switch between databases with the Adapter pattern.
|
|
54
|
+
- **Mongoose Adapter**: Included by default.
|
|
55
|
+
- **Automated CRUD**: Just define a schema and get a full API.
|
|
86
56
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
```
|
|
57
|
+
### ✅ 5. Zod-Powered Validation (`/middleware/zod`)
|
|
58
|
+
Validate payloads and params with 100% type inference.
|
|
90
59
|
|
|
91
60
|
---
|
|
92
61
|
|
|
93
|
-
##
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
import { User, RefreshToken, Post } from './my-mongoose-models';
|
|
99
|
-
|
|
100
|
-
const dbAdapter = createMongooseAdapter({
|
|
101
|
-
User,
|
|
102
|
-
RefreshToken,
|
|
103
|
-
models: {
|
|
104
|
-
posts: Post // Maps the 'posts' collection to the generic Post model
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// Now pass dbAdapter to createAuth or createCRUD initializers!
|
|
109
|
-
```
|
|
62
|
+
## 🗺️ Roadmap & Future Vision
|
|
63
|
+
1. **`defineModel` Engine**: Define a schema once, auto-generate CRUD, validation, and types.
|
|
64
|
+
2. **Plugin System**: A robust "hook" based system for extending the framework core.
|
|
65
|
+
3. **IoT & WebSocket Support**: Dedicated ingestion layer for high-throughput data.
|
|
66
|
+
4. **CLI Presets**: `npx dolphin init` for instant project scaffolding.
|
|
110
67
|
|
|
111
68
|
---
|
|
112
69
|
|
|
113
|
-
##
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const userSchema = z.object({
|
|
121
|
-
name: z.string().min(2),
|
|
122
|
-
age: z.number().gte(18)
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
// App Router Handler wrapped with validation
|
|
126
|
-
const myPostHandler = validateAppRoute(userSchema, async (req, validatedData) => {
|
|
127
|
-
console.log(validatedData.name); // 100% typed and safe
|
|
128
|
-
return new Response('Success');
|
|
129
|
-
});
|
|
130
|
-
```
|
|
70
|
+
## 📊 Performance Comparison
|
|
71
|
+
| Metric | Express | Fastify | **Dolphin** |
|
|
72
|
+
| :--- | :--- | :--- | :--- |
|
|
73
|
+
| **Overhead** | High | Low | **Ultra-Low (Native)** |
|
|
74
|
+
| **Modularity** | Low | Medium | **Extreme** |
|
|
75
|
+
| **DX** | Good | Excellent | **Premium** |
|
|
131
76
|
|
|
132
77
|
---
|
|
133
78
|
|
|
134
|
-
## 🌐
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
4. Select `main` branch and `/ (root)` folder.
|
|
140
|
-
5. Click **Save**. GitHub will automatically host this README as your documentation website!
|
|
79
|
+
## 🌐 Documentation
|
|
80
|
+
Detailed documentation is automatically hosted via GitHub Pages from this README.
|
|
81
|
+
|
|
82
|
+
## 📄 License
|
|
83
|
+
ISC © 2026 Dolphin Team
|
package/dist/router/router.d.ts
CHANGED
package/dist/server/server.d.ts
CHANGED
|
@@ -6,13 +6,13 @@ export declare function createDolphinServer(options?: {
|
|
|
6
6
|
use: (mw: any) => number;
|
|
7
7
|
listen: (port?: number, callback?: () => void) => void;
|
|
8
8
|
close: () => http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
|
|
9
|
-
get: (path: string, handler: (
|
|
10
|
-
post: (path: string, handler: (
|
|
11
|
-
put: (path: string, handler: (
|
|
12
|
-
delete: (path: string, handler: (
|
|
13
|
-
patch: (path: string, handler: (
|
|
9
|
+
get: (path: string, handler: (ctx: any) => Promise<void> | void) => void;
|
|
10
|
+
post: (path: string, handler: (ctx: any) => Promise<void> | void) => void;
|
|
11
|
+
put: (path: string, handler: (ctx: any) => Promise<void> | void) => void;
|
|
12
|
+
delete: (path: string, handler: (ctx: any) => Promise<void> | void) => void;
|
|
13
|
+
patch: (path: string, handler: (ctx: any) => Promise<void> | void) => void;
|
|
14
14
|
match(method: string, url: string): {
|
|
15
|
-
handler: (
|
|
15
|
+
handler: (ctx: any) => Promise<void> | void;
|
|
16
16
|
params: Record<string, string>;
|
|
17
17
|
} | null;
|
|
18
18
|
};
|
package/dist/server/server.js
CHANGED
|
@@ -15,12 +15,26 @@ function createDolphinServer(options = {}) {
|
|
|
15
15
|
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
16
16
|
res.end(JSON.stringify(data));
|
|
17
17
|
};
|
|
18
|
+
const host = req.headers.host || 'localhost';
|
|
19
|
+
const parsedUrl = new URL(req.url, `http://${host}`);
|
|
20
|
+
const ctx = {
|
|
21
|
+
req,
|
|
22
|
+
res,
|
|
23
|
+
params: {},
|
|
24
|
+
query: Object.fromEntries(parsedUrl.searchParams),
|
|
25
|
+
body: {},
|
|
26
|
+
state: {},
|
|
27
|
+
json: res.json,
|
|
28
|
+
status: (code) => {
|
|
29
|
+
res.statusCode = code;
|
|
30
|
+
return ctx;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
18
33
|
// Global middleware execution
|
|
19
34
|
for (const mw of middlewares) {
|
|
20
|
-
const next = new Promise(resolve => mw(req, res, resolve));
|
|
21
|
-
await next;
|
|
22
35
|
if (res.writableEnded)
|
|
23
36
|
return;
|
|
37
|
+
await new Promise(resolve => mw(ctx, resolve));
|
|
24
38
|
}
|
|
25
39
|
// Body parsing for POST/PUT/PATCH
|
|
26
40
|
if (['POST', 'PUT', 'PATCH'].includes(req.method) && req.headers['content-type']?.includes('application/json')) {
|
|
@@ -28,25 +42,26 @@ function createDolphinServer(options = {}) {
|
|
|
28
42
|
for await (const chunk of req)
|
|
29
43
|
chunks.push(chunk);
|
|
30
44
|
try {
|
|
31
|
-
|
|
45
|
+
ctx.body = JSON.parse(Buffer.concat(chunks).toString());
|
|
32
46
|
}
|
|
33
47
|
catch {
|
|
34
|
-
|
|
48
|
+
ctx.body = {};
|
|
35
49
|
}
|
|
36
50
|
}
|
|
37
51
|
// Matching route
|
|
38
|
-
const match = router.match(req.method,
|
|
52
|
+
const match = router.match(req.method, parsedUrl.pathname);
|
|
39
53
|
if (match) {
|
|
40
|
-
|
|
54
|
+
ctx.params = match.params;
|
|
41
55
|
try {
|
|
42
|
-
await match.handler(
|
|
56
|
+
await match.handler(ctx);
|
|
43
57
|
}
|
|
44
58
|
catch (err) {
|
|
45
|
-
|
|
59
|
+
console.error('Server Handler Error:', err);
|
|
60
|
+
ctx.json({ error: err.message || 'Internal Server Error' }, err.status || 500);
|
|
46
61
|
}
|
|
47
62
|
}
|
|
48
63
|
else {
|
|
49
|
-
|
|
64
|
+
ctx.json({ error: 'Not Found' }, 404);
|
|
50
65
|
}
|
|
51
66
|
});
|
|
52
67
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../server/server.ts"],"names":[],"mappings":";;;;;AAGA,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../server/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/router/router.ts
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const { createDolphinServer } = require('../dist/server/server');
|
|
2
|
+
|
|
3
|
+
const app = createDolphinServer();
|
|
4
|
+
|
|
5
|
+
app.get('/health', (ctx) => {
|
|
6
|
+
ctx.json({ status: 'ok' });
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const PORT = 5008;
|
|
10
|
+
app.listen(PORT, () => {
|
|
11
|
+
console.log(`Benchmark server running on port ${PORT}`);
|
|
12
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createDolphinServer } from '../server/server';
|
|
2
|
+
|
|
3
|
+
const app = createDolphinServer();
|
|
4
|
+
|
|
5
|
+
app.get('/health', (ctx: any) => {
|
|
6
|
+
ctx.json({ status: 'ok' });
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const PORT = 5002;
|
|
10
|
+
app.listen(PORT, () => {
|
|
11
|
+
console.log(`Benchmark server running on port ${PORT}`);
|
|
12
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const { createDolphinServer } = require('../dist/server/server');
|
|
2
|
+
const http = require('node:http');
|
|
3
|
+
|
|
4
|
+
const app = createDolphinServer();
|
|
5
|
+
|
|
6
|
+
app.get('/test', (ctx) => {
|
|
7
|
+
ctx.json({ success: true });
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const PORT = 5005;
|
|
11
|
+
app.listen(PORT, () => {
|
|
12
|
+
console.log(`Test server running on port ${PORT}`);
|
|
13
|
+
|
|
14
|
+
// Make a request immediately
|
|
15
|
+
http.get(`http://127.0.0.1:${PORT}/test`, (res) => {
|
|
16
|
+
let data = '';
|
|
17
|
+
res.on('data', chunk => data += chunk);
|
|
18
|
+
res.on('end', () => {
|
|
19
|
+
console.log('STATUS:', res.statusCode);
|
|
20
|
+
console.log('BODY:', data);
|
|
21
|
+
process.exit(0);
|
|
22
|
+
});
|
|
23
|
+
}).on('error', (err) => {
|
|
24
|
+
console.error('Request Error:', err.message);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
});
|
|
27
|
+
});
|
package/server/server.ts
CHANGED
|
@@ -12,11 +12,27 @@ export function createDolphinServer(options: { port?: number; host?: string } =
|
|
|
12
12
|
res.end(JSON.stringify(data));
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
+
const host = req.headers.host || 'localhost';
|
|
16
|
+
const parsedUrl = new URL(req.url!, `http://${host}`);
|
|
17
|
+
|
|
18
|
+
const ctx: any = {
|
|
19
|
+
req,
|
|
20
|
+
res,
|
|
21
|
+
params: {},
|
|
22
|
+
query: Object.fromEntries(parsedUrl.searchParams),
|
|
23
|
+
body: {},
|
|
24
|
+
state: {},
|
|
25
|
+
json: res.json,
|
|
26
|
+
status: (code: number) => {
|
|
27
|
+
res.statusCode = code;
|
|
28
|
+
return ctx;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
15
32
|
// Global middleware execution
|
|
16
33
|
for (const mw of middlewares) {
|
|
17
|
-
const next = new Promise(resolve => mw(req, res, resolve));
|
|
18
|
-
await next;
|
|
19
34
|
if (res.writableEnded) return;
|
|
35
|
+
await new Promise(resolve => mw(ctx, resolve));
|
|
20
36
|
}
|
|
21
37
|
|
|
22
38
|
// Body parsing for POST/PUT/PATCH
|
|
@@ -24,23 +40,24 @@ export function createDolphinServer(options: { port?: number; host?: string } =
|
|
|
24
40
|
const chunks: any[] = [];
|
|
25
41
|
for await (const chunk of req) chunks.push(chunk);
|
|
26
42
|
try {
|
|
27
|
-
|
|
43
|
+
ctx.body = JSON.parse(Buffer.concat(chunks).toString());
|
|
28
44
|
} catch {
|
|
29
|
-
|
|
45
|
+
ctx.body = {};
|
|
30
46
|
}
|
|
31
47
|
}
|
|
32
48
|
|
|
33
49
|
// Matching route
|
|
34
|
-
const match = router.match(req.method!,
|
|
50
|
+
const match = router.match(req.method!, parsedUrl.pathname);
|
|
35
51
|
if (match) {
|
|
36
|
-
|
|
52
|
+
ctx.params = match.params;
|
|
37
53
|
try {
|
|
38
|
-
await match.handler(
|
|
54
|
+
await match.handler(ctx);
|
|
39
55
|
} catch (err: any) {
|
|
40
|
-
|
|
56
|
+
console.error('Server Handler Error:', err);
|
|
57
|
+
ctx.json({ error: err.message || 'Internal Server Error' }, err.status || 500);
|
|
41
58
|
}
|
|
42
59
|
} else {
|
|
43
|
-
|
|
60
|
+
ctx.json({ error: 'Not Found' }, 404);
|
|
44
61
|
}
|
|
45
62
|
});
|
|
46
63
|
|