@forgedevstack/harbor 1.0.0 → 1.5.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.
Files changed (123) hide show
  1. package/CHANGELOG.md +82 -101
  2. package/README.md +210 -794
  3. package/dist/auth/apiKey.d.ts +6 -0
  4. package/dist/auth/apiKey.d.ts.map +1 -0
  5. package/dist/auth/index.d.ts +7 -0
  6. package/dist/auth/index.d.ts.map +1 -0
  7. package/dist/auth/index.js +2 -0
  8. package/dist/auth/index.js.map +1 -0
  9. package/dist/auth/jwt.d.ts +21 -0
  10. package/dist/auth/jwt.d.ts.map +1 -0
  11. package/dist/auth/password.d.ts +6 -0
  12. package/dist/auth/password.d.ts.map +1 -0
  13. package/dist/auth/rbac.d.ts +6 -0
  14. package/dist/auth/rbac.d.ts.map +1 -0
  15. package/dist/auth/signing.d.ts +5 -0
  16. package/dist/auth/signing.d.ts.map +1 -0
  17. package/dist/auth/types/apiKey.types.d.ts +9 -0
  18. package/dist/auth/types/apiKey.types.d.ts.map +1 -0
  19. package/dist/auth/types/index.d.ts +5 -0
  20. package/dist/auth/types/index.d.ts.map +1 -0
  21. package/dist/auth/types/jwt.types.d.ts +17 -0
  22. package/dist/auth/types/jwt.types.d.ts.map +1 -0
  23. package/dist/auth/types/rbac.types.d.ts +8 -0
  24. package/dist/auth/types/rbac.types.d.ts.map +1 -0
  25. package/dist/auth/types/signing.types.d.ts +8 -0
  26. package/dist/auth/types/signing.types.d.ts.map +1 -0
  27. package/dist/cache/index.d.ts +4 -0
  28. package/dist/cache/index.d.ts.map +1 -0
  29. package/dist/cache/index.js +2 -0
  30. package/dist/cache/index.js.map +1 -0
  31. package/dist/cache/manager.d.ts +24 -0
  32. package/dist/cache/manager.d.ts.map +1 -0
  33. package/dist/cache/stores.d.ts +28 -0
  34. package/dist/cache/stores.d.ts.map +1 -0
  35. package/dist/cache/types.d.ts +23 -0
  36. package/dist/cache/types.d.ts.map +1 -0
  37. package/dist/cli/index.js +21 -22
  38. package/dist/cli/index.js.map +1 -1
  39. package/dist/core/config.d.ts.map +1 -1
  40. package/dist/core/router.d.ts +40 -2
  41. package/dist/core/router.d.ts.map +1 -1
  42. package/dist/core/server.d.ts.map +1 -1
  43. package/dist/database/connection.d.ts +1 -2
  44. package/dist/database/connection.d.ts.map +1 -1
  45. package/dist/database/index.js +2 -0
  46. package/dist/database/index.js.map +1 -0
  47. package/dist/database/model.d.ts +1 -4
  48. package/dist/database/model.d.ts.map +1 -1
  49. package/dist/docker/index.js +1 -1
  50. package/dist/http.const-BKHG1Lsj.mjs +62 -0
  51. package/dist/http.const-BKHG1Lsj.mjs.map +1 -0
  52. package/dist/http.const-Ckcy7OFp.js +2 -0
  53. package/dist/http.const-Ckcy7OFp.js.map +1 -0
  54. package/dist/index-Ca4WpLvw.js +2 -0
  55. package/dist/index-Ca4WpLvw.js.map +1 -0
  56. package/dist/index-DIVHd6rO.mjs +1054 -0
  57. package/dist/index-DIVHd6rO.mjs.map +1 -0
  58. package/dist/index.cjs.js +16 -16
  59. package/dist/index.cjs.js.map +1 -1
  60. package/dist/index.d.ts +11 -2
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.es.js +676 -1691
  63. package/dist/index.es.js.map +1 -1
  64. package/dist/logger-CZn7QxCl.mjs +102 -0
  65. package/dist/{logger-D7aJSi62.mjs.map → logger-CZn7QxCl.mjs.map} +1 -1
  66. package/dist/logger-D-lfaRWQ.js +3 -0
  67. package/dist/{logger-DEnWXtpk.js.map → logger-D-lfaRWQ.js.map} +1 -1
  68. package/dist/manager-CjcKb4P9.mjs +149 -0
  69. package/dist/{manager-B6vqJgEn.mjs.map → manager-CjcKb4P9.mjs.map} +1 -1
  70. package/dist/manager-DrF1vbJg.js +4 -0
  71. package/dist/{manager-B1UKMjXW.js.map → manager-DrF1vbJg.js.map} +1 -1
  72. package/dist/middleware/health.d.ts +65 -0
  73. package/dist/middleware/health.d.ts.map +1 -0
  74. package/dist/middleware/index.d.ts +5 -0
  75. package/dist/middleware/index.d.ts.map +1 -0
  76. package/dist/middleware/index.js +2 -0
  77. package/dist/middleware/index.js.map +1 -0
  78. package/dist/middleware/metrics.d.ts +68 -0
  79. package/dist/middleware/metrics.d.ts.map +1 -0
  80. package/dist/middleware/rateLimit.d.ts +52 -0
  81. package/dist/middleware/rateLimit.d.ts.map +1 -0
  82. package/dist/middleware/upload.d.ts +59 -0
  83. package/dist/middleware/upload.d.ts.map +1 -0
  84. package/dist/password-BXBkKbv3.js +2 -0
  85. package/dist/password-BXBkKbv3.js.map +1 -0
  86. package/dist/password-y4m307oa.mjs +223 -0
  87. package/dist/password-y4m307oa.mjs.map +1 -0
  88. package/dist/scheduler/index.d.ts +3 -0
  89. package/dist/scheduler/index.d.ts.map +1 -0
  90. package/dist/scheduler/index.js +2 -0
  91. package/dist/scheduler/index.js.map +1 -0
  92. package/dist/scheduler/scheduler.d.ts +30 -0
  93. package/dist/scheduler/scheduler.d.ts.map +1 -0
  94. package/dist/scheduler/types.d.ts +25 -0
  95. package/dist/scheduler/types.d.ts.map +1 -0
  96. package/dist/types/server.types.d.ts +7 -0
  97. package/dist/types/server.types.d.ts.map +1 -1
  98. package/dist/upload-9lCNnKK_.js +5 -0
  99. package/dist/upload-9lCNnKK_.js.map +1 -0
  100. package/dist/upload-DUjQiuq7.mjs +619 -0
  101. package/dist/upload-DUjQiuq7.mjs.map +1 -0
  102. package/dist/validation/index.js +1 -1
  103. package/dist/validation/index.js.map +1 -1
  104. package/dist/websocket/index.d.ts +3 -0
  105. package/dist/websocket/index.d.ts.map +1 -0
  106. package/dist/websocket/index.js +2 -0
  107. package/dist/websocket/index.js.map +1 -0
  108. package/dist/websocket/manager.d.ts +30 -0
  109. package/dist/websocket/manager.d.ts.map +1 -0
  110. package/dist/websocket/types.d.ts +27 -0
  111. package/dist/websocket/types.d.ts.map +1 -0
  112. package/package.json +58 -18
  113. package/templates/default/controllers/user.controller.ts +44 -64
  114. package/templates/default/package.json +9 -33
  115. package/templates/default/routes/index.ts +2 -12
  116. package/templates/default/routes/user.routes.ts +26 -19
  117. package/templates/default/server.ts +16 -35
  118. package/dist/logger-D7aJSi62.mjs +0 -102
  119. package/dist/logger-DEnWXtpk.js +0 -3
  120. package/dist/manager-B1UKMjXW.js +0 -4
  121. package/dist/manager-B6vqJgEn.mjs +0 -152
  122. package/dist/portal.d.ts +0 -13
  123. package/dist/portal.d.ts.map +0 -1
package/README.md CHANGED
@@ -1,927 +1,343 @@
1
- # Harbor 🚢
1
+ # @forgestack/harbor
2
2
 
3
- **The Pipeline for Node.js Backends**
3
+ <p align="center">
4
+ <img src="https://forgestack.dev/harbor-logo.svg" alt="Harbor Logo" width="120" />
5
+ </p>
4
6
 
5
- Harbor is a complete backend framework that replaces Express routing, Mongoose ODM, and more. Fast server creation, route management, MongoDB integration, validation, Docker orchestration, and automatic error handling — all in one lightweight, TypeScript-first package.
7
+ <p align="center">
8
+ <strong>Complete Node.js backend framework</strong><br/>
9
+ MongoDB ODM • WebSocket • Scheduling • Caching • Auth • Metrics
10
+ </p>
6
11
 
7
- [![npm version](https://img.shields.io/npm/v/harbor.svg)](https://www.npmjs.com/package/harbor)
8
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
9
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
12
+ <p align="center">
13
+ <a href="https://www.npmjs.com/package/@forgestack/harbor"><img src="https://img.shields.io/npm/v/@forgestack/harbor.svg" alt="npm"></a>
14
+ <a href="https://github.com/yaghobieh/ForgeStack"><img src="https://img.shields.io/github/stars/yaghobieh/ForgeStack.svg" alt="stars"></a>
15
+ <a href="https://github.com/yaghobieh/ForgeStack/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@forgestack/harbor.svg" alt="license"></a>
16
+ </p>
10
17
 
11
- ## Table of Contents
12
-
13
- - [Features](#features)
14
- - [Installation](#installation)
15
- - [Quick Start](#quick-start)
16
- - [Server Setup](#server-setup)
17
- - [MongoDB Connection](#mongodb-connection)
18
- - [Schema & Models](#schema--models)
19
- - [Route Management](#route-management)
20
- - [Validation](#validation)
21
- - [Error Handling](#error-handling)
22
- - [Middleware](#middleware)
23
- - [Docker Manager](#docker-manager)
24
- - [Configuration](#configuration)
25
- - [i18n (Translations)](#i18n-translations)
26
- - [API Reference](#api-reference)
18
+ ---
27
19
 
28
20
  ## Features
29
21
 
30
- - 🚀 **Fast Server Creation** - Create a production-ready server with one function call
31
- - 🛣️ **Route Management** - Fluent API with pre/post middleware, validation, and automatic error handling
32
- - 🍃 **MongoDB ODM** - Full Mongoose replacement with Schema, Model, and all query methods
33
- - **Validation** - Powerful request validation with Mongoose-compatible schemas
34
- - 🐳 **Docker Manager** - Build, push, pull images and orchestrate containers
35
- - 🌍 **i18n Support** - Built-in internationalization for all messages
36
- - 📝 **Morgan-like Logger** - HTTP request logging with customizable formats
37
- - ⚙️ **Config-Driven** - Centralized configuration via `harbor.config.json`
38
- - 📘 **TypeScript First** - Full type definitions with excellent IntelliSense
22
+ | Feature | Description |
23
+ |---------|-------------|
24
+ | **Zero-Config Server** | Create servers in seconds with Express under the hood |
25
+ | **MongoDB ODM** | Full Mongoose replacement with Schema, Model, Query |
26
+ | **Authentication** | JWT, API Key, RBAC, request signing |
27
+ | **WebSocket** | Real-time with rooms and broadcasting |
28
+ | **Scheduler** | Cron expressions and interval-based jobs |
29
+ | **Rate Limiting** | Memory and Redis stores |
30
+ | **Caching** | Memory and Redis with middleware |
31
+ | **Metrics** | Prometheus-compatible endpoint |
32
+ | **Health Checks** | MongoDB, Redis, Memory, Disk checks |
33
+ | **File Uploads** | Multipart parsing with validation |
34
+ | **i18n** | Built-in translation system |
39
35
 
40
36
  ## Installation
41
37
 
42
38
  ```bash
43
- # npm
44
- npm install harbor
45
-
46
- # yarn
47
- yarn add harbor
48
-
49
- # pnpm
50
- pnpm add harbor
51
- ```
52
-
53
- ### Peer Dependencies
54
-
55
- Harbor requires `mongodb` for database operations:
56
-
57
- ```bash
58
- npm install mongodb
39
+ npm install @forgestack/harbor
59
40
  ```
60
41
 
61
42
  ## Quick Start
62
43
 
63
- ### 1. Initialize Project
64
-
65
- ```bash
66
- npx harbor init
67
- ```
68
-
69
- This creates:
70
- - `harbor.config.json` - Configuration file
71
- - `src/server.ts` - Basic server setup
72
-
73
- ### 2. Create Your Server
74
-
75
44
  ```typescript
76
- import { createServer, GET, POST, connect } from 'harbor';
45
+ import { createServer, router, route } from '@forgestack/harbor';
77
46
 
78
- // Connect to MongoDB
79
- await connect('mongodb://localhost:27017/myapp');
80
-
81
- // Create server
82
47
  const server = createServer({ port: 3000 });
83
48
 
84
- // Add routes
85
- server.addRoute(
86
- GET('/api/health', () => ({ status: 'ok', timestamp: new Date() }))
87
- );
88
-
89
- server.addRoute(
90
- POST('/api/users', async (req) => {
91
- const { email, name } = req.validated.body;
92
- return { id: '123', email, name };
93
- }, {
94
- validation: {
95
- body: {
96
- email: { type: 'email', required: true },
97
- name: { type: 'string', required: true, min: 2 }
98
- }
99
- }
100
- })
101
- );
102
-
103
- // Server is running at http://localhost:3000
104
- ```
105
-
106
- ### 3. Run Your Server
49
+ const users = router('/api/users', [
50
+ route.get('/', async () => ({ users: [] })),
51
+ route.post('/', async (req) => ({ id: '123', ...req.body })),
52
+ route.delete('/:id', async (req) => ({ deleted: req.params.id })),
53
+ ]);
107
54
 
108
- ```bash
109
- npx ts-node src/server.ts
55
+ server.use(users);
56
+ server.listen(3000, () => console.log('Server running!'));
110
57
  ```
111
58
 
112
- ## Server Setup
59
+ ## MongoDB ODM
113
60
 
114
- ### Basic Server
61
+ Full Mongoose replacement:
115
62
 
116
63
  ```typescript
117
- import { createServer } from 'harbor';
118
-
119
- const server = createServer({
120
- port: 3000, // Default: 3000
121
- host: 'localhost', // Default: 'localhost'
122
- configPath: './harbor.config.json',
123
- autoStart: true, // Default: true
124
- onReady: (info) => console.log(`Server ready on ${info.host}:${info.port}`),
125
- onError: (error) => console.error('Server error:', error),
126
- });
127
- ```
128
-
129
- ### Server Methods
64
+ import { Schema, model, connect } from '@forgestack/harbor/database';
130
65
 
131
- ```typescript
132
- // Add routes
133
- server.addRoute(route);
134
- server.addRoutes([route1, route2]);
66
+ await connect('mongodb://localhost:27017/myapp');
135
67
 
136
- // Add middleware
137
- server.addMiddleware(middleware);
68
+ const UserSchema = new Schema({
69
+ email: { type: 'string', required: true, unique: true },
70
+ name: { type: 'string', required: true },
71
+ role: { type: 'string', enum: ['user', 'admin'], default: 'user' },
72
+ createdAt: { type: 'date', default: () => new Date() },
73
+ });
138
74
 
139
- // Get Express app instance
140
- const app = server.getApp();
75
+ const User = model('User', UserSchema);
141
76
 
142
- // Graceful shutdown
143
- await server.stop();
77
+ // All Mongoose-like methods
78
+ const user = await User.create({ email: 'john@example.com', name: 'John' });
79
+ const admins = await User.find({ role: 'admin' });
80
+ const found = await User.findOne({ email: 'john@example.com' });
81
+ await User.updateOne({ _id: user._id }, { role: 'admin' });
82
+ await User.deleteOne({ _id: user._id });
144
83
  ```
145
84
 
146
- ## MongoDB Connection
85
+ ## Authentication
147
86
 
148
- Harbor provides a complete Mongoose replacement. All the methods you know from Mongoose are available.
149
-
150
- ### Connecting to MongoDB
87
+ ### JWT
151
88
 
152
89
  ```typescript
153
- import { connect, disconnect, connection } from 'harbor';
90
+ import { JWT, jwtAuth, requireRole } from '@forgestack/harbor';
154
91
 
155
- // Connect to MongoDB
156
- await connect('mongodb://localhost:27017/myapp', {
157
- maxPoolSize: 10,
158
- serverSelectionTimeoutMS: 30000,
92
+ const jwt = new JWT({
93
+ secret: process.env.JWT_SECRET!,
94
+ expiresIn: 3600, // 1 hour
159
95
  });
160
96
 
161
- // Connection events
162
- connection.on('connected', () => console.log('Connected!'));
163
- connection.on('disconnected', () => console.log('Disconnected!'));
164
- connection.on('error', (err) => console.error('Error:', err));
97
+ // Generate token
98
+ const token = jwt.sign({ userId: '123', role: 'admin' });
165
99
 
166
- // Check connection state
167
- console.log(connection.readyState); // 0: disconnected, 1: connected, 2: connecting
100
+ // Verify token
101
+ const payload = jwt.verify(token);
168
102
 
169
- // Disconnect
170
- await disconnect();
103
+ // Protect routes
104
+ app.use('/api', jwtAuth(jwt));
105
+ app.get('/api/admin', requireRole('admin'), adminHandler);
171
106
  ```
172
107
 
173
- ### Connection Options
108
+ ### API Key
174
109
 
175
110
  ```typescript
176
- await connect(uri, {
177
- maxPoolSize: 10, // Maximum connections in pool
178
- minPoolSize: 1, // Minimum connections
179
- serverSelectionTimeoutMS: 30000,
180
- socketTimeoutMS: 45000,
181
- retryWrites: true,
182
- w: 'majority',
183
- authSource: 'admin',
184
- replicaSet: 'rs0',
185
- ssl: true,
186
- tls: true,
187
- });
188
- ```
111
+ import { apiKeyAuth, generateApiKey } from '@forgestack/harbor';
189
112
 
190
- ## Schema & Models
113
+ const key = generateApiKey(); // Generate a secure API key
191
114
 
192
- ### Defining a Schema
193
-
194
- ```typescript
195
- import { Schema, model } from 'harbor';
196
-
197
- // Define schema (same as Mongoose!)
198
- const userSchema = new Schema({
199
- email: {
200
- type: 'String',
201
- required: true,
202
- unique: true,
203
- lowercase: true,
204
- match: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
205
- },
206
- password: {
207
- type: 'String',
208
- required: true,
209
- minLength: 8
115
+ app.use(apiKeyAuth({
116
+ header: 'X-API-Key',
117
+ validator: async (key) => {
118
+ const apiKey = await db.apiKeys.findOne({ key, active: true });
119
+ return apiKey ? { valid: true, data: apiKey } : false;
210
120
  },
211
- name: {
212
- type: 'String',
213
- required: true,
214
- trim: true
215
- },
216
- role: {
217
- type: 'String',
218
- enum: ['user', 'admin', 'moderator'],
219
- default: 'user'
220
- },
221
- age: {
222
- type: 'Number',
223
- min: 0,
224
- max: 150
225
- },
226
- isActive: {
227
- type: 'Boolean',
228
- default: true
229
- },
230
- profile: {
231
- bio: 'String',
232
- avatar: 'String',
233
- social: {
234
- twitter: 'String',
235
- github: 'String'
236
- }
237
- },
238
- tags: ['String'],
239
- createdAt: 'Date',
240
- updatedAt: 'Date'
241
- }, {
242
- timestamps: true, // Auto-add createdAt/updatedAt
243
- collection: 'users', // Collection name
244
- versionKey: '__v', // Version key field
245
- });
121
+ }));
246
122
  ```
247
123
 
248
- ### Schema Types
249
-
250
- All Mongoose schema types are supported:
251
-
252
- - `String` - String values
253
- - `Number` - Numeric values
254
- - `Boolean` - true/false
255
- - `Date` - Date objects
256
- - `ObjectId` - MongoDB ObjectId
257
- - `Array` - Arrays
258
- - `Object` / `Mixed` - Any object
259
- - `Buffer` - Binary data
260
-
261
- ### Schema Options
124
+ ## WebSocket
262
125
 
263
126
  ```typescript
264
- const schema = new Schema(definition, {
265
- timestamps: true, // Add createdAt/updatedAt
266
- timestamps: { createdAt: 'created', updatedAt: 'modified' }, // Custom field names
267
- collection: 'myCollection', // Collection name
268
- strict: true, // Only save schema fields
269
- versionKey: '__v', // Version key
270
- _id: true, // Add _id field
271
- autoIndex: true, // Auto-create indexes
272
- minimize: true, // Remove empty objects
273
- });
274
- ```
275
-
276
- ### Instance Methods
127
+ import { createWebSocketServer } from '@forgestack/harbor';
277
128
 
278
- ```typescript
279
- // Define instance methods
280
- userSchema.methods.comparePassword = async function(password: string) {
281
- return bcrypt.compare(password, this.password);
282
- };
283
-
284
- userSchema.methods.generateToken = function() {
285
- return jwt.sign({ id: this._id }, SECRET);
286
- };
287
-
288
- // Or use the method() function
289
- userSchema.method('fullName', function() {
290
- return `${this.firstName} ${this.lastName}`;
129
+ const wss = createWebSocketServer({
130
+ path: '/ws',
131
+ onConnection: (client) => {
132
+ console.log('Client connected:', client.id);
133
+ wss.join(client, 'lobby');
134
+ },
135
+ onMessage: (client, data) => {
136
+ wss.broadcastToRoom('lobby', { user: client.id, message: data });
137
+ },
138
+ onClose: (client) => {
139
+ console.log('Client disconnected:', client.id);
140
+ },
291
141
  });
292
- ```
293
142
 
294
- ### Static Methods
143
+ // Attach to server
144
+ await wss.attach(server.server);
295
145
 
296
- ```typescript
297
- // Define static methods
298
- userSchema.statics.findByEmail = function(email: string) {
299
- return this.findOne({ email: email.toLowerCase() });
300
- };
301
-
302
- userSchema.statics.findActive = function() {
303
- return this.find({ isActive: true });
304
- };
305
-
306
- // Or use the static() function
307
- userSchema.static('findByRole', function(role: string) {
308
- return this.find({ role });
309
- });
146
+ // Broadcasting
147
+ wss.broadcast({ type: 'announcement', text: 'Hello everyone!' });
148
+ wss.broadcastToRoom('lobby', { type: 'chat', text: 'Hello lobby!' });
149
+ wss.send(clientId, { type: 'private', text: 'Just for you' });
310
150
  ```
311
151
 
312
- ### Virtuals
152
+ ## Scheduler
313
153
 
314
154
  ```typescript
315
- userSchema.virtual('fullName')
316
- .get(function() {
317
- return `${this.firstName} ${this.lastName}`;
318
- })
319
- .set(function(name: string) {
320
- const [firstName, lastName] = name.split(' ');
321
- this.firstName = firstName;
322
- this.lastName = lastName;
323
- });
324
- ```
325
-
326
- ### Middleware (Hooks)
155
+ import { createScheduler } from '@forgestack/harbor';
327
156
 
328
- ```typescript
329
- // Pre-save hook
330
- userSchema.pre('save', async function(next) {
331
- if (this.isModified('password')) {
332
- this.password = await bcrypt.hash(this.password, 10);
333
- }
334
- next();
157
+ const scheduler = createScheduler({
158
+ onJobComplete: (job, duration) => console.log(`${job.name} took ${duration}ms`),
335
159
  });
336
160
 
337
- // Post-save hook
338
- userSchema.post('save', function(next) {
339
- console.log('User saved:', this._id);
340
- next();
161
+ // Cron expression (daily at midnight)
162
+ scheduler.cron('cleanup', '0 0 * * *', async () => {
163
+ await db.logs.deleteMany({ createdAt: { $lt: thirtyDaysAgo } });
341
164
  });
342
165
 
343
- // Query middleware
344
- userSchema.pre('find', function(next) {
345
- // Add filter to all find queries
346
- this.where({ isDeleted: { $ne: true } });
347
- next();
166
+ // Every 5 minutes
167
+ scheduler.every('5m', 'healthCheck', async () => {
168
+ await pingServices();
348
169
  });
349
- ```
350
-
351
- ### Creating a Model
352
-
353
- ```typescript
354
- import { model } from 'harbor';
355
-
356
- // Create model
357
- const User = model('User', userSchema);
358
170
 
359
- // With custom collection name
360
- const User = model('User', userSchema, 'my_users');
361
- ```
362
-
363
- ## Query Methods
364
-
365
- All Mongoose query methods are available:
366
-
367
- ### Find Documents
368
-
369
- ```typescript
370
- // Find all
371
- const users = await User.find();
372
-
373
- // Find with filter
374
- const admins = await User.find({ role: 'admin' });
375
-
376
- // Find one
377
- const user = await User.findOne({ email: 'john@example.com' });
378
-
379
- // Find by ID
380
- const user = await User.findById('507f1f77bcf86cd799439011');
381
-
382
- // Query builder
383
- const users = await User.find()
384
- .where('age').gte(18).lte(65)
385
- .where('role').in(['user', 'admin'])
386
- .select('name email role')
387
- .sort('-createdAt')
388
- .skip(0)
389
- .limit(10)
390
- .lean();
391
-
392
- // Chain methods
393
- const users = await User.find({ isActive: true })
394
- .select('name email')
395
- .sort({ createdAt: -1 })
396
- .limit(10);
397
- ```
398
-
399
- ### Create Documents
400
-
401
- ```typescript
402
- // Create single document
403
- const user = await User.create({
404
- email: 'john@example.com',
405
- name: 'John Doe',
406
- password: 'secret123'
171
+ // One-time job
172
+ scheduler.at(new Date('2026-01-20'), 'reminder', async () => {
173
+ await sendReminder();
407
174
  });
408
175
 
409
- // Create multiple documents
410
- const users = await User.create([
411
- { email: 'user1@example.com', name: 'User 1' },
412
- { email: 'user2@example.com', name: 'User 2' }
413
- ]);
414
-
415
- // Insert many
416
- const result = await User.insertMany([
417
- { email: 'user1@example.com', name: 'User 1' },
418
- { email: 'user2@example.com', name: 'User 2' }
419
- ]);
420
-
421
- // Using new + save
422
- const user = User.new({ email: 'john@example.com', name: 'John' });
423
- await user.save();
176
+ scheduler.start();
424
177
  ```
425
178
 
426
- ### Update Documents
179
+ ## Rate Limiting
427
180
 
428
181
  ```typescript
429
- // Update one
430
- await User.updateOne(
431
- { email: 'john@example.com' },
432
- { $set: { name: 'John Smith' } }
433
- );
434
-
435
- // Update many
436
- await User.updateMany(
437
- { role: 'user' },
438
- { $set: { isActive: true } }
439
- );
440
-
441
- // Find and update (returns document)
442
- const user = await User.findOneAndUpdate(
443
- { email: 'john@example.com' },
444
- { $set: { name: 'John Smith' } },
445
- { new: true } // Return updated document
446
- );
447
-
448
- // Find by ID and update
449
- const user = await User.findByIdAndUpdate(
450
- '507f1f77bcf86cd799439011',
451
- { name: 'New Name' },
452
- { new: true }
453
- );
454
-
455
- // Replace one
456
- await User.replaceOne(
457
- { email: 'john@example.com' },
458
- { email: 'john@example.com', name: 'Replaced User' }
459
- );
460
- ```
461
-
462
- ### Delete Documents
463
-
464
- ```typescript
465
- // Delete one
466
- await User.deleteOne({ email: 'john@example.com' });
467
-
468
- // Delete many
469
- await User.deleteMany({ isActive: false });
470
-
471
- // Find and delete
472
- const deletedUser = await User.findOneAndDelete({ email: 'john@example.com' });
473
-
474
- // Find by ID and delete
475
- const deletedUser = await User.findByIdAndDelete('507f1f77bcf86cd799439011');
476
- ```
477
-
478
- ### Count & Exists
479
-
480
- ```typescript
481
- // Count documents
482
- const count = await User.countDocuments({ role: 'admin' });
483
-
484
- // Estimated count (faster, but approximate)
485
- const estimated = await User.estimatedDocumentCount();
182
+ import { rateLimit, slidingWindowRateLimit, RedisStore } from '@forgestack/harbor';
486
183
 
487
- // Check if exists
488
- const exists = await User.exists({ email: 'john@example.com' });
489
- // Returns { _id: '...' } or null
490
- ```
184
+ // Memory store (default)
185
+ app.use(rateLimit({
186
+ max: 100,
187
+ windowMs: 15 * 60 * 1000, // 15 minutes
188
+ }));
491
189
 
492
- ### Aggregation
190
+ // Strict for login
191
+ app.post('/login', rateLimit({ max: 5, windowMs: 60000 }), loginHandler);
493
192
 
494
- ```typescript
495
- const result = await User.aggregate([
496
- { $match: { isActive: true } },
497
- { $group: { _id: '$role', count: { $sum: 1 } } },
498
- { $sort: { count: -1 } }
499
- ]);
193
+ // Redis store for distributed systems
194
+ const redisStore = new RedisStore(redisClient, 15 * 60 * 1000);
195
+ app.use(rateLimit({ store: redisStore }));
500
196
  ```
501
197
 
502
- ### Distinct
198
+ ## Caching
503
199
 
504
200
  ```typescript
505
- const roles = await User.distinct('role');
506
- // ['user', 'admin', 'moderator']
507
- ```
508
-
509
- ### Indexes
201
+ import { cache, cacheResponse, createCache, RedisCache } from '@forgestack/harbor';
510
202
 
511
- ```typescript
512
- // Create index
513
- await User.createIndex({ email: 1 }, { unique: true });
203
+ // Manual caching
204
+ const products = await cache.getOrSet('products', async () => {
205
+ return await db.products.find();
206
+ }, 60000); // 1 minute TTL
514
207
 
515
- // Create compound index
516
- await User.createIndex({ name: 1, createdAt: -1 });
208
+ // Delete cache
209
+ await cache.del('products');
517
210
 
518
- // List indexes
519
- const indexes = await User.listIndexes();
211
+ // Invalidate by pattern
212
+ await cache.invalidate('products:*');
520
213
 
521
- // Drop index
522
- await User.dropIndex('email_1');
214
+ // Middleware caching
215
+ app.get('/api/products', cacheResponse({ ttl: 60000 }), handler);
523
216
 
524
- // Define index in schema
525
- userSchema.index({ email: 1 }, { unique: true });
526
- userSchema.index({ name: 'text' }); // Text index
217
+ // Redis cache
218
+ const redisCache = createCache(new RedisCache(redisClient), 3600000);
527
219
  ```
528
220
 
529
- ## Route Management
530
-
531
- ### Simple Routes
221
+ ## Metrics & Health Checks
532
222
 
533
223
  ```typescript
534
- import { GET, POST, PUT, PATCH, DELETE } from 'harbor';
535
-
536
- // GET request
537
- const getUsers = GET('/api/users', async (req) => {
538
- const users = await User.find();
539
- return { users };
540
- });
541
-
542
- // POST request with validation
543
- const createUser = POST('/api/users', async (req) => {
544
- const user = await User.create(req.validated.body);
545
- return { user };
546
- }, {
547
- validation: {
548
- body: {
549
- email: { type: 'email', required: true },
550
- name: { type: 'string', required: true }
551
- }
552
- }
553
- });
554
-
555
- // PUT request
556
- const updateUser = PUT('/api/users/:id', async (req) => {
557
- const user = await User.findByIdAndUpdate(
558
- req.validated.params.id,
559
- req.validated.body,
560
- { new: true }
561
- );
562
- return { user };
563
- });
564
-
565
- // DELETE request
566
- const deleteUser = DELETE('/api/users/:id', async (req) => {
567
- await User.findByIdAndDelete(req.validated.params.id);
568
- return { deleted: true };
569
- });
224
+ import {
225
+ metricsMiddleware,
226
+ metricsEndpoint,
227
+ healthCheck,
228
+ mongoHealthCheck,
229
+ redisHealthCheck,
230
+ memoryHealthCheck,
231
+ } from '@forgestack/harbor';
570
232
 
571
- // Add routes to server
572
- server.addRoute(getUsers);
573
- server.addRoute(createUser);
574
- server.addRoute(updateUser);
575
- server.addRoute(deleteUser);
576
- ```
233
+ // Collect metrics
234
+ app.use(metricsMiddleware());
577
235
 
578
- ### Route Options
236
+ // Prometheus scrape endpoint
237
+ app.get('/metrics', metricsEndpoint());
579
238
 
580
- ```typescript
581
- const route = POST('/api/users', handler, {
582
- // Validation
583
- validation: {
584
- body: { /* schema */ },
585
- params: { /* schema */ },
586
- query: { /* schema */ },
587
- headers: { /* schema */ }
588
- },
589
-
590
- // Middleware
591
- pre: [authMiddleware, rateLimitMiddleware],
592
- post: [logMiddleware],
593
-
594
- // Timeout
595
- timeout: 30000, // 30 seconds
596
-
597
- // Metadata
598
- tags: ['users'],
599
- description: 'Create a new user'
600
- });
239
+ // Health check endpoint
240
+ app.get('/health', healthCheck({
241
+ checks: [
242
+ mongoHealthCheck(db.connection),
243
+ redisHealthCheck(redisClient),
244
+ memoryHealthCheck(512), // Max 512MB heap
245
+ ],
246
+ }));
601
247
  ```
602
248
 
603
- ### Route Groups
249
+ ## File Uploads
604
250
 
605
251
  ```typescript
606
- // Group routes by prefix
607
- const userRoutes = [
608
- GET('/api/users', listUsers),
609
- POST('/api/users', createUser),
610
- GET('/api/users/:id', getUser),
611
- PUT('/api/users/:id', updateUser),
612
- DELETE('/api/users/:id', deleteUser),
613
- ];
614
-
615
- server.addRoutes(userRoutes);
616
- ```
252
+ import { upload } from '@forgestack/harbor';
617
253
 
618
- ## Validation
619
-
620
- ### Request Validation
621
-
622
- ```typescript
623
- const route = POST('/api/users', handler, {
624
- validation: {
625
- body: {
626
- email: { type: 'email', required: true },
627
- password: { type: 'string', required: true, min: 8, max: 100 },
628
- age: { type: 'number', min: 0, max: 150 },
629
- role: { type: 'string', enum: ['user', 'admin'] },
630
- website: { type: 'url' },
631
- phone: { type: 'string', match: /^\+?[\d\s-]+$/ }
632
- },
633
- params: {
634
- id: { type: 'objectId', required: true }
635
- },
636
- query: {
637
- page: { type: 'number', default: 1 },
638
- limit: { type: 'number', default: 20, max: 100 }
639
- }
640
- }
254
+ app.post('/upload', upload({
255
+ dest: './uploads',
256
+ limits: { fileSize: 10 * 1024 * 1024 }, // 10MB
257
+ allowedMimeTypes: ['image/jpeg', 'image/png'],
258
+ }), (req, res) => {
259
+ console.log(req.file); // { path, originalName, mimeType, size }
260
+ res.json({ uploaded: true });
641
261
  });
642
- ```
643
-
644
- ### Validation Types
645
-
646
- - `string` - String values
647
- - `number` - Numeric values
648
- - `boolean` - true/false
649
- - `email` - Valid email address
650
- - `url` - Valid URL
651
- - `objectId` - MongoDB ObjectId
652
- - `date` - Valid date
653
- - `array` - Array values
654
- - `object` - Object values
655
-
656
- ### Custom Validators
657
-
658
- ```typescript
659
- const schema = {
660
- password: {
661
- type: 'string',
662
- required: true,
663
- validate: {
664
- validator: (value) => /[A-Z]/.test(value) && /[0-9]/.test(value),
665
- message: 'Password must contain uppercase letter and number'
666
- }
667
- }
668
- };
669
- ```
670
-
671
- ## Error Handling
672
262
 
673
- ### HarborError Class
674
-
675
- ```typescript
676
- import { HarborError } from 'harbor';
677
-
678
- // Throw errors in your routes
679
- const getUser = GET('/api/users/:id', async (req) => {
680
- const user = await User.findById(req.params.id);
681
-
682
- if (!user) {
683
- throw HarborError.notFound('User not found');
684
- }
685
-
686
- if (!user.isActive) {
687
- throw HarborError.forbidden('User account is disabled');
688
- }
689
-
690
- return { user };
263
+ // Multiple files
264
+ app.post('/gallery', upload({ limits: { files: 10 } }), (req, res) => {
265
+ console.log(req.files); // Array of uploaded files
691
266
  });
692
- ```
693
-
694
- ### Available Error Methods
695
-
696
- ```typescript
697
- HarborError.badRequest(message?, details?) // 400
698
- HarborError.unauthorized(message?) // 401
699
- HarborError.forbidden(message?) // 403
700
- HarborError.notFound(message?) // 404
701
- HarborError.conflict(message?, details?) // 409
702
- HarborError.tooManyRequests(message?) // 429
703
- HarborError.internal(message?) // 500
704
- ```
705
267
 
706
- ### Error Configuration
707
-
708
- Configure error responses in `harbor.config.json`:
709
-
710
- ```json
711
- {
712
- "errors": {
713
- "401": {
714
- "message": "Please log in to continue",
715
- "json": true,
716
- "log": true
717
- },
718
- "404": {
719
- "message": "Resource not found",
720
- "json": true
721
- },
722
- "500": {
723
- "message": "Something went wrong",
724
- "json": true,
725
- "log": true
726
- }
727
- }
728
- }
729
- ```
730
-
731
- ## Middleware
732
-
733
- ### HTTP Logger (Morgan-like)
734
-
735
- ```typescript
736
- import { httpLogger } from 'harbor';
737
-
738
- // Add logging middleware
739
- server.addMiddleware(httpLogger({ format: 'dev' }));
740
-
741
- // Available formats: 'tiny', 'short', 'dev', 'combined', 'common'
742
- ```
743
-
744
- ### Custom Format
745
-
746
- ```typescript
747
- server.addMiddleware(httpLogger({
748
- format: 'custom',
749
- customFormat: (tokens) =>
750
- `${tokens.method} ${tokens.url} ${tokens.status} - ${tokens.responseTime}ms`
751
- }));
752
- ```
753
-
754
- ### Skip Requests
755
-
756
- ```typescript
757
- import { httpLogger, skipFunctions } from 'harbor';
758
-
759
- server.addMiddleware(httpLogger({
760
- format: 'dev',
761
- skip: skipFunctions.healthChecks // Skip /health and /healthz
762
- }));
763
- ```
764
-
765
- ### Pre/Post Route Middleware
766
-
767
- ```typescript
768
- const authMiddleware = async (req, res, next) => {
769
- const token = req.headers.authorization?.split(' ')[1];
770
- if (!token) {
771
- throw HarborError.unauthorized('No token provided');
772
- }
773
- req.user = await verifyToken(token);
774
- next();
775
- };
776
-
777
- const logMiddleware = async (req, res, result, next) => {
778
- console.log('Response:', result);
779
- next();
780
- };
781
-
782
- const route = GET('/api/profile', handler, {
783
- pre: [authMiddleware],
784
- post: [logMiddleware]
268
+ // Memory storage (for processing)
269
+ app.post('/process', upload({ storage: 'memory' }), (req, res) => {
270
+ const buffer = req.file.buffer;
271
+ // Process buffer...
785
272
  });
786
273
  ```
787
274
 
788
- ## Docker Manager
789
-
790
- ```typescript
791
- import { createDockerManager } from 'harbor';
792
-
793
- const docker = createDockerManager({
794
- imageName: 'my-app',
795
- tag: 'latest',
796
- dockerfile: './Dockerfile',
797
- context: '.'
798
- });
799
-
800
- // Build image
801
- await docker.build();
275
+ ## CLI
802
276
 
803
- // Push to registry
804
- await docker.push('registry.example.com');
805
-
806
- // Pull image
807
- await docker.pull('nginx:latest');
277
+ ```bash
278
+ # Initialize new project
279
+ npx @forgestack/harbor init my-api
808
280
 
809
- // Container operations
810
- await docker.startContainer('my-container');
811
- await docker.stopContainer('my-container');
812
- await docker.restartContainer('my-container');
281
+ # With template
282
+ npx @forgestack/harbor init my-api --template default
813
283
 
814
- // Docker Compose
815
- await docker.composeUp();
816
- await docker.composeDown();
284
+ # Generate files
285
+ npx @forgestack/harbor generate model User
286
+ npx @forgestack/harbor generate controller User
287
+ npx @forgestack/harbor generate route users
817
288
  ```
818
289
 
819
290
  ## Configuration
820
291
 
821
- Create `harbor.config.json` in your project root:
292
+ Create `harbor.config.json`:
822
293
 
823
294
  ```json
824
295
  {
825
296
  "server": {
826
297
  "port": 3000,
827
- "host": "localhost"
298
+ "host": "localhost",
299
+ "cors": {
300
+ "enabled": true,
301
+ "origin": "*"
302
+ }
828
303
  },
829
- "cors": {
830
- "enabled": true,
831
- "origin": "*",
832
- "methods": ["GET", "POST", "PUT", "PATCH", "DELETE"],
833
- "allowedHeaders": ["Content-Type", "Authorization"]
304
+ "database": {
305
+ "uri": "mongodb://localhost:27017/myapp"
834
306
  },
835
307
  "logger": {
836
- "level": "info",
837
- "format": "dev"
838
- },
839
- "validation": {
840
- "abortEarly": false,
841
- "stripUnknown": true
308
+ "level": "info"
842
309
  },
843
310
  "errors": {
844
- "404": { "message": "Not Found", "json": true },
845
- "500": { "message": "Internal Error", "json": true, "log": true }
846
- },
847
- "docker": {
848
- "imageName": "my-app",
849
- "tag": "latest"
311
+ "showStack": false
850
312
  }
851
313
  }
852
314
  ```
853
315
 
854
- ## i18n (Translations)
316
+ ## Subpath Imports
855
317
 
856
318
  ```typescript
857
- import { setLocale, t, addTranslations } from 'harbor';
319
+ // Core
320
+ import { createServer, router, route } from '@forgestack/harbor';
858
321
 
859
- // Set locale
860
- setLocale('he');
322
+ // Database
323
+ import { Schema, model, connect } from '@forgestack/harbor/database';
861
324
 
862
- // Use translations
863
- console.log(t('server.started', { host: 'localhost', port: 3000 }));
864
- // Output (Hebrew): השרת פועל בכתובת http://localhost:3000
325
+ // Middleware
326
+ import { rateLimit, healthCheck, upload } from '@forgestack/harbor/middleware';
865
327
 
866
- // Add custom translations
867
- addTranslations('en', {
868
- 'custom.message': 'Hello, {name}!'
869
- });
870
- ```
328
+ // Auth
329
+ import { JWT, jwtAuth, apiKeyAuth } from '@forgestack/harbor/auth';
871
330
 
872
- ### Available Locales
873
-
874
- - `en` - English (default)
875
- - `he` - Hebrew
876
-
877
- ## API Reference
878
-
879
- ### Core Functions
880
-
881
- | Function | Description |
882
- |----------|-------------|
883
- | `createServer(options)` | Create a Harbor server |
884
- | `connect(uri, options)` | Connect to MongoDB |
885
- | `disconnect()` | Disconnect from MongoDB |
886
- | `Schema(definition, options)` | Create a schema |
887
- | `model(name, schema)` | Create a model |
888
- | `GET/POST/PUT/PATCH/DELETE(path, handler, options)` | Create routes |
889
-
890
- ### Model Methods
891
-
892
- | Method | Description |
893
- |--------|-------------|
894
- | `find(filter)` | Find documents |
895
- | `findOne(filter)` | Find one document |
896
- | `findById(id)` | Find by ID |
897
- | `create(doc)` | Create document(s) |
898
- | `updateOne(filter, update)` | Update one |
899
- | `updateMany(filter, update)` | Update many |
900
- | `deleteOne(filter)` | Delete one |
901
- | `deleteMany(filter)` | Delete many |
902
- | `findOneAndUpdate(filter, update, options)` | Find and update |
903
- | `findOneAndDelete(filter)` | Find and delete |
904
- | `countDocuments(filter)` | Count documents |
905
- | `aggregate(pipeline)` | Aggregation pipeline |
906
-
907
- ### Query Methods
908
-
909
- | Method | Description |
910
- |--------|-------------|
911
- | `.select(fields)` | Select fields |
912
- | `.sort(fields)` | Sort results |
913
- | `.limit(n)` | Limit results |
914
- | `.skip(n)` | Skip results |
915
- | `.lean()` | Return plain objects |
916
- | `.where(path)` | Add condition |
917
- | `.gt/gte/lt/lte(value)` | Comparison operators |
918
- | `.in/nin(values)` | Array operators |
919
- | `.regex(pattern)` | Regex match |
331
+ // Cache
332
+ import { cache, cacheResponse } from '@forgestack/harbor/cache';
920
333
 
921
- ## License
334
+ // Scheduler
335
+ import { createScheduler } from '@forgestack/harbor/scheduler';
922
336
 
923
- MIT © Harbor Contributors
337
+ // WebSocket
338
+ import { createWebSocketServer } from '@forgestack/harbor/websocket';
339
+ ```
924
340
 
925
- ---
341
+ ## License
926
342
 
927
- **Happy Sailing! 🚢**
343
+ MIT © [John Yaghobieh](https://github.com/yaghobieh)