dolphin-server-modules 1.0.0 → 1.0.2

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 ADDED
@@ -0,0 +1,140 @@
1
+ # Dolphin Server Modules 🐬
2
+
3
+ A world-class, extremely lightweight, and 100% modular backend utility package for Node.js. It provides plug-and-play modules for Authentication, standard CRUD operations, Next.js/Express Controllers, and Zod validation.
4
+
5
+ Build production-grade APIs in minutes!
6
+
7
+ ## 📦 Installation
8
+
9
+ ```bash
10
+ npm install dolphin-server-modules
11
+ ```
12
+
13
+ You will also need to install `argon2` and `zod` if you are using the auth and validation modules respectively:
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.
20
+
21
+ ```typescript
22
+ import { createAuth } from 'dolphin-server-modules/auth';
23
+ import { createCRUD } from 'dolphin-server-modules/crud';
24
+ import { createDolphinController } from 'dolphin-server-modules/controller';
25
+ import { validateStructure } from 'dolphin-server-modules/middleware/zod';
26
+ ```
27
+
28
+ ---
29
+
30
+ ## 🔒 1. Auth Module (`/auth`)
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.
32
+
33
+ ```typescript
34
+ import { createAuth } from 'dolphin-server-modules/auth';
35
+
36
+ const auth = createAuth({
37
+ secret: 'YOUR_SUPER_SECRET_KEY',
38
+ cookieMaxAge: 7 * 86400000 // 7 days
39
+ });
40
+
41
+ // Register
42
+ const user = await auth.register(db, { email: 'user@example.com', password: 'password123' });
43
+
44
+ // Login (Supports 2FA Totp)
45
+ const session = await auth.login(db, { email: 'user@example.com', password: 'password123' });
46
+ console.log(session.accessToken);
47
+ ```
48
+
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
+
54
+ ```typescript
55
+ import { createCRUD } from 'dolphin-server-modules/crud';
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 } }]
68
+ });
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
+ ```
76
+
77
+ ---
78
+
79
+ ## 🎮 3. Controller Module (`/controller`)
80
+ Easily convert your `crud` instance into instant API Controllers. Supports Next.js App Router, Pages API, and Express out of the box.
81
+
82
+ ```typescript
83
+ import { createDolphinController, createNextAppRoute } from 'dolphin-server-modules/controller';
84
+
85
+ const postController = createDolphinController(crud, 'posts');
86
+
87
+ // Next.js App Router API Route (app/api/posts/route.ts)
88
+ export const { GET, POST, PUT, DELETE } = createNextAppRoute(postController);
89
+ ```
90
+
91
+ ---
92
+
93
+ ## 🍃 4. Mongoose Adapter (`/adapters/mongoose`)
94
+ An official, fully typed Mongoose adapter for seamless integration with the Auth and CRUD modules. It handles mapping `id` to `_id`, query mapping (`$like` to `$regex`), and works independently!
95
+
96
+ ```typescript
97
+ import { createMongooseAdapter } from 'dolphin-server-modules/adapters/mongoose';
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
+ ```
110
+
111
+ ---
112
+
113
+ ## ✅ 5. Zod Validation Middleware (`/middleware/zod`)
114
+ Validate your data effortlessly using `zod`. Protect your endpoints from bad data.
115
+
116
+ ```typescript
117
+ import { z } from 'zod';
118
+ import { validateAppRoute } from 'dolphin-server-modules/middleware/zod';
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
+ ```
131
+
132
+ ---
133
+
134
+ ## 🌐 Hosting Docs via GitHub Pages
135
+ To host this documentation cleanly:
136
+ 1. Go to your repository **Settings** on GitHub.
137
+ 2. Click on **Pages** on the left sidebar.
138
+ 3. Under **Build and deployment**, select **Deploy from a branch**.
139
+ 4. Select `main` branch and `/ (root)` folder.
140
+ 5. Click **Save**. GitHub will automatically host this README as your documentation website!
@@ -0,0 +1,142 @@
1
+ import type { Model } from 'mongoose';
2
+
3
+ export interface MongooseAdapterConfig {
4
+ User: Model<any>;
5
+ RefreshToken: Model<any>;
6
+ models?: Record<string, Model<any>>;
7
+ }
8
+
9
+ export function createMongooseAdapter(config: MongooseAdapterConfig) {
10
+ // Map MongoDB document to the standard BaseDocument shape
11
+ const mapDoc = (doc: any) => {
12
+ if (!doc) return null;
13
+ const obj = doc.toObject && typeof doc.toObject === 'function' ? doc.toObject() : { ...doc };
14
+ if (obj._id) {
15
+ obj.id = obj._id.toString();
16
+ delete obj._id;
17
+ }
18
+ delete obj.__v;
19
+ return obj;
20
+ };
21
+
22
+ // Convert standard QueryFilter to Mongoose Query format
23
+ const mapQuery = (query: any) => {
24
+ if (!query) return {};
25
+ const parsed: any = {};
26
+ for (const [key, val] of Object.entries(query)) {
27
+ if (key === 'id') {
28
+ parsed['_id'] = val;
29
+ } else if (key === '$and' || key === '$or') {
30
+ parsed[key] = (val as any[]).map(mapQuery);
31
+ } else if (typeof val === 'object' && val !== null && !Array.isArray(val)) {
32
+ const ops: any = {};
33
+ for (const [op, opVal] of Object.entries(val)) {
34
+ if (op === '$like') {
35
+ ops['$regex'] = opVal;
36
+ ops['$options'] = 'i';
37
+ } else {
38
+ ops[op] = opVal;
39
+ }
40
+ }
41
+ parsed[key] = ops;
42
+ } else {
43
+ parsed[key] = val;
44
+ }
45
+ }
46
+ return parsed;
47
+ };
48
+
49
+ // Convert standard sorting to Mongoose sort format
50
+ const mapSort = (sort?: Record<string, 'asc' | 'desc'>) => {
51
+ if (!sort) return undefined;
52
+ const result: Record<string, 1 | -1> = {};
53
+ for (const [k, v] of Object.entries(sort)) {
54
+ result[k] = v === 'asc' ? 1 : -1;
55
+ }
56
+ return result;
57
+ };
58
+
59
+ // Helper to fetch generic CRUD models
60
+ const getModel = (collection: string): Model<any> => {
61
+ if (!config.models || !config.models[collection]) {
62
+ throw new Error(`Model for collection '${collection}' not found in MongooseAdapterConfig`);
63
+ }
64
+ return config.models[collection];
65
+ };
66
+
67
+ return {
68
+ // ==========================================
69
+ // AUTHENTICATION ADAPTER METHODS
70
+ // ==========================================
71
+ async createUser(data: any) {
72
+ const user = await config.User.create(data);
73
+ return mapDoc(user);
74
+ },
75
+ async findUserByEmail(email: string) {
76
+ const user = await config.User.findOne({ email });
77
+ return mapDoc(user);
78
+ },
79
+ async findUserById(id: string) {
80
+ const user = await config.User.findById(id);
81
+ return mapDoc(user);
82
+ },
83
+ async updateUser(id: string, data: any) {
84
+ const user = await config.User.findByIdAndUpdate(id, data, { new: true });
85
+ return mapDoc(user);
86
+ },
87
+ async saveRefreshToken(data: any) {
88
+ await config.RefreshToken.create(data);
89
+ },
90
+ async findRefreshToken(token: string) {
91
+ const rt = await config.RefreshToken.findOne({ token });
92
+ return mapDoc(rt);
93
+ },
94
+ async deleteRefreshToken(token: string) {
95
+ await config.RefreshToken.deleteOne({ token });
96
+ },
97
+
98
+ // ==========================================
99
+ // CRUD ADAPTER METHODS
100
+ // ==========================================
101
+ async create(collection: string, data: any) {
102
+ const Model = getModel(collection);
103
+ const doc = await Model.create(data);
104
+ return mapDoc(doc);
105
+ },
106
+ async read(collection: string, query: any) {
107
+ const Model = getModel(collection);
108
+ const docs = await Model.find(mapQuery(query));
109
+ return docs.map(mapDoc);
110
+ },
111
+ async update(collection: string, query: any, data: any) {
112
+ const Model = getModel(collection);
113
+ await Model.updateMany(mapQuery(query), data);
114
+
115
+ // Return updated documents if we can map them back, otherwise minimal info
116
+ // Since updateMany doesn't return the updated docs in mongoose, we can just return a generic success object
117
+ // or fetch them. The interface expects Promise<any>. We will fetch them to match typical behavior.
118
+ const docs = await Model.find(mapQuery({ ...query, ...data }));
119
+ return docs.map(mapDoc);
120
+ },
121
+ async delete(collection: string, query: any) {
122
+ const Model = getModel(collection);
123
+ await Model.deleteMany(mapQuery(query));
124
+ return { deleted: true };
125
+ },
126
+ async advancedRead(collection: string, query: any, options: any) {
127
+ const Model = getModel(collection);
128
+ let mQuery = Model.find(mapQuery(query));
129
+ if (options.sort) {
130
+ mQuery = mQuery.sort(mapSort(options.sort));
131
+ }
132
+ if (options.offset !== undefined) {
133
+ mQuery = mQuery.skip(options.offset);
134
+ }
135
+ if (options.limit !== undefined) {
136
+ mQuery = mQuery.limit(options.limit);
137
+ }
138
+ const docs = await mQuery;
139
+ return docs.map(mapDoc);
140
+ }
141
+ };
142
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dolphin-server-modules",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Core utility modules for Auth, CRUD, and Controllers",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -9,7 +9,8 @@
9
9
  "./auth": "./dist/auth/auth.js",
10
10
  "./controller": "./dist/controller/controller.js",
11
11
  "./crud": "./dist/curd/crud.js",
12
- "./middleware/zod": "./dist/middleware/zod.js"
12
+ "./middleware/zod": "./dist/middleware/zod.js",
13
+ "./adapters/mongoose": "./dist/adapters/mongoose/index.js"
13
14
  },
14
15
  "scripts": {
15
16
  "build": "tsc",
@@ -33,6 +34,7 @@
33
34
  "@types/jest": "^29.5.12",
34
35
  "@types/node": "^20.11.30",
35
36
  "jest": "^29.7.0",
37
+ "mongoose": "^9.3.2",
36
38
  "ts-jest": "^29.1.2",
37
39
  "typescript": "^5.4.3"
38
40
  }