standard-api-core 1.0.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/LICENSE +21 -0
- package/README.md +522 -0
- package/dist/core/response-handler.d.ts +3 -0
- package/dist/core/response-handler.d.ts.map +1 -0
- package/dist/core/response-handler.js +27 -0
- package/dist/core/response-handler.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/api-documenter.d.ts +4 -0
- package/dist/middleware/api-documenter.d.ts.map +1 -0
- package/dist/middleware/api-documenter.js +31 -0
- package/dist/middleware/api-documenter.js.map +1 -0
- package/dist/swagger/builder.d.ts +4 -0
- package/dist/swagger/builder.d.ts.map +1 -0
- package/dist/swagger/builder.js +108 -0
- package/dist/swagger/builder.js.map +1 -0
- package/dist/swagger/registry.d.ts +3 -0
- package/dist/swagger/registry.d.ts.map +1 -0
- package/dist/swagger/registry.js +6 -0
- package/dist/swagger/registry.js.map +1 -0
- package/dist/types/index.d.ts +59 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/config-loader.d.ts +3 -0
- package/dist/utils/config-loader.d.ts.map +1 -0
- package/dist/utils/config-loader.js +73 -0
- package/dist/utils/config-loader.js.map +1 -0
- package/package.json +72 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Standard API Core
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
# @standard-api/core
|
|
2
|
+
|
|
3
|
+
A production-ready NPM package that standardizes API responses and automatically generates best-practice Swagger (OpenAPI) documentation for Express.js applications.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Standardized Response Format** - Consistent success/error responses across your entire API
|
|
8
|
+
- **Auto-Generated Swagger UI** - Beautiful, interactive API documentation
|
|
9
|
+
- **Configuration-Based** - Flexible configuration file support
|
|
10
|
+
- **Downloadable Specs** - Download full or per-endpoint OpenAPI specs in JSON/YAML
|
|
11
|
+
- **Headers & Security** - Built-in support for authentication headers
|
|
12
|
+
- **Type-Safe** - Full TypeScript support with Zod schemas
|
|
13
|
+
- **Zero Boilerplate** - Minimal setup, maximum productivity
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @standard-api/core express zod
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### 1. Create Configuration File
|
|
24
|
+
|
|
25
|
+
Create `standard-api.config.ts` in your project root:
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { StandardApiConfig } from "@standard-api/core";
|
|
29
|
+
|
|
30
|
+
const config: StandardApiConfig = {
|
|
31
|
+
api: {
|
|
32
|
+
title: "User Service API",
|
|
33
|
+
description: "API documentation for User Service",
|
|
34
|
+
version: "1.0.0",
|
|
35
|
+
baseUrl: "/api/v1",
|
|
36
|
+
},
|
|
37
|
+
headers: {
|
|
38
|
+
Authorization: {
|
|
39
|
+
description: "JWT Bearer token",
|
|
40
|
+
required: true,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
swagger: {
|
|
44
|
+
path: "/docs",
|
|
45
|
+
enableDownload: true,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default config;
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 2. Setup Express App
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import express from "express";
|
|
56
|
+
import {
|
|
57
|
+
setupSwagger,
|
|
58
|
+
responseMiddleware,
|
|
59
|
+
apiDoc,
|
|
60
|
+
loadConfig,
|
|
61
|
+
} from "@standard-api/core";
|
|
62
|
+
import { z } from "zod";
|
|
63
|
+
|
|
64
|
+
const app = express();
|
|
65
|
+
app.use(express.json());
|
|
66
|
+
|
|
67
|
+
// Load configuration
|
|
68
|
+
const config = loadConfig("./standard-api.config.ts");
|
|
69
|
+
|
|
70
|
+
// Setup response handlers (adds sendSuccess/sendError methods)
|
|
71
|
+
app.use(responseMiddleware);
|
|
72
|
+
|
|
73
|
+
// Setup Swagger documentation
|
|
74
|
+
setupSwagger(app, config);
|
|
75
|
+
|
|
76
|
+
// Define schemas
|
|
77
|
+
const UserSchema = z.object({
|
|
78
|
+
success: z.literal(true),
|
|
79
|
+
message: z.string(),
|
|
80
|
+
data: z.object({
|
|
81
|
+
id: z.string(),
|
|
82
|
+
name: z.string(),
|
|
83
|
+
email: z.string().email(),
|
|
84
|
+
role: z.enum(["ADMIN", "USER"]),
|
|
85
|
+
}),
|
|
86
|
+
meta: z.object({
|
|
87
|
+
timestamp: z.string(),
|
|
88
|
+
requestId: z.string(),
|
|
89
|
+
}),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Create documented endpoints
|
|
93
|
+
app.get(
|
|
94
|
+
"/api/v1/users/:id",
|
|
95
|
+
apiDoc({
|
|
96
|
+
method: "get",
|
|
97
|
+
path: "/api/v1/users/{id}",
|
|
98
|
+
summary: "Get user by ID",
|
|
99
|
+
description: "Returns a single user",
|
|
100
|
+
tags: ["Users"],
|
|
101
|
+
request: {
|
|
102
|
+
params: z.object({
|
|
103
|
+
id: z.string().describe("User ID"),
|
|
104
|
+
}),
|
|
105
|
+
},
|
|
106
|
+
response: UserSchema,
|
|
107
|
+
}),
|
|
108
|
+
(req, res) => {
|
|
109
|
+
const user = {
|
|
110
|
+
id: req.params.id,
|
|
111
|
+
name: "John Doe",
|
|
112
|
+
email: "john@example.com",
|
|
113
|
+
role: "USER",
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
res.sendSuccess({
|
|
117
|
+
message: "User fetched successfully",
|
|
118
|
+
data: user,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
app.listen(3000, () => {
|
|
124
|
+
console.log("Server running on http://localhost:3000");
|
|
125
|
+
console.log("Swagger docs available at http://localhost:3000/docs");
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## API Reference
|
|
130
|
+
|
|
131
|
+
### Response Handlers
|
|
132
|
+
|
|
133
|
+
#### `res.sendSuccess(options)`
|
|
134
|
+
|
|
135
|
+
Send a standardized success response.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
res.sendSuccess({
|
|
139
|
+
message: "Operation successful",
|
|
140
|
+
data: { id: 1, name: "John" },
|
|
141
|
+
meta: { customField: "value" }, // optional
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Response Format:**
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"success": true,
|
|
150
|
+
"message": "Operation successful",
|
|
151
|
+
"data": { "id": 1, "name": "John" },
|
|
152
|
+
"meta": {
|
|
153
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
154
|
+
"requestId": "uuid-here",
|
|
155
|
+
"customField": "value"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
#### `res.sendError(options)`
|
|
161
|
+
|
|
162
|
+
Send a standardized error response.
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
res.sendError({
|
|
166
|
+
message: "Validation failed",
|
|
167
|
+
code: "VALIDATION_ERROR",
|
|
168
|
+
error: { field: "email", issue: "invalid format" },
|
|
169
|
+
status: 400,
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Response Format:**
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"success": false,
|
|
178
|
+
"message": "Validation failed",
|
|
179
|
+
"error": {
|
|
180
|
+
"code": "VALIDATION_ERROR",
|
|
181
|
+
"details": { "field": "email", "issue": "invalid format" }
|
|
182
|
+
},
|
|
183
|
+
"meta": {
|
|
184
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
185
|
+
"requestId": "uuid-here"
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Middleware
|
|
191
|
+
|
|
192
|
+
#### `responseMiddleware`
|
|
193
|
+
|
|
194
|
+
Middleware that adds `sendSuccess` and `sendError` methods to Express response object.
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
app.use(responseMiddleware);
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### `apiDoc(config)`
|
|
201
|
+
|
|
202
|
+
Middleware that registers route documentation with Swagger.
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
app.get(
|
|
206
|
+
"/users",
|
|
207
|
+
apiDoc({
|
|
208
|
+
method: "get",
|
|
209
|
+
path: "/users",
|
|
210
|
+
summary: "Get all users",
|
|
211
|
+
description: "Returns a list of users",
|
|
212
|
+
tags: ["Users"],
|
|
213
|
+
request: {
|
|
214
|
+
query: z.object({
|
|
215
|
+
page: z.string().optional(),
|
|
216
|
+
limit: z.string().optional(),
|
|
217
|
+
}),
|
|
218
|
+
},
|
|
219
|
+
response: UserListSchema,
|
|
220
|
+
}),
|
|
221
|
+
handler
|
|
222
|
+
);
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Configuration Parameters:**
|
|
226
|
+
|
|
227
|
+
- `method` - HTTP method (get, post, put, delete, patch)
|
|
228
|
+
- `path` - Route path matching Express route
|
|
229
|
+
- `summary` - Short description of the endpoint
|
|
230
|
+
- `description` - Detailed description (optional)
|
|
231
|
+
- `tags` - Array of tags for grouping endpoints
|
|
232
|
+
- `request` - Request schema (query, params, body)
|
|
233
|
+
- `response` - Response schema (Zod schema)
|
|
234
|
+
|
|
235
|
+
### Setup Functions
|
|
236
|
+
|
|
237
|
+
#### `setupSwagger(app, config)`
|
|
238
|
+
|
|
239
|
+
Initializes Swagger UI and documentation endpoints.
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
setupSwagger(app, config);
|
|
243
|
+
**Auto-discovery:** If no path is provided, the loader searches in the following order:
|
|
244
|
+
1. `standard-api.config.ts` (root)
|
|
245
|
+
2. `standard-api.config.js` (root)
|
|
246
|
+
3. `config/standard-api.config.ts`
|
|
247
|
+
4. `config/standard-api.config.js`
|
|
248
|
+
|
|
249
|
+
##
|
|
250
|
+
|
|
251
|
+
#### `loadConfig(path?)`
|
|
252
|
+
|
|
253
|
+
Loads configuration from file. Auto-discovers if path not provided.
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
const config = loadConfig("./standard-api.config.ts");
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## 🔧 Configuration Options
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
interface StandardApiConfig {
|
|
263
|
+
api: {
|
|
264
|
+
title: string; // API title
|
|
265
|
+
description: string; // API description
|
|
266
|
+
version: string; // API version
|
|
267
|
+
seUrl: string; // Base URL for all endpoints
|
|
268
|
+
};
|
|
269
|
+
headers?: {
|
|
270
|
+
[headerName: string]: {
|
|
271
|
+
description: string;
|
|
272
|
+
required: boolean;
|
|
273
|
+
example?: string;
|
|
274
|
+
};
|
|
275
|
+
};
|
|
276
|
+
swagger: {
|
|
277
|
+
path: string; // Swagger UI path (e.g., '/docs')
|
|
278
|
+
enableDownload: boolean; // Enable spec downloads
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## 📥 Download Endpoints
|
|
284
|
+
|
|
285
|
+
WheenableDownload: true`, the following endpoints are available:
|
|
286
|
+
|
|
287
|
+
- `GET /docs/openapi.json` - Download full OpenAPI spec (JSON)
|
|
288
|
+
- `GET /docs/openapi.yaml` - Download full OpenAPI spec (YAML)
|
|
289
|
+
- `GET /docs/endpoint/:method/:path?format=json` - Download single endpoint spec
|
|
290
|
+
|
|
291
|
+
Example:
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
# Download full spec
|
|
295
|
+
curl http://localhost:3000/docs/openapi.json > openapi.json
|
|
296
|
+
|
|
297
|
+
# Download single endpoint
|
|
298
|
+
curl http://localhost:3000/docs/endpoint/get/users?format=yaml
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## 🎨 Schema Definition
|
|
302
|
+
|
|
303
|
+
Use Zod for type-safe schema definitions:
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
const CreateUserSchema = z.object({
|
|
307
|
+
success: z.literal(true),
|
|
308
|
+
message: z.string(),
|
|
309
|
+
data: z.object({
|
|
310
|
+
name: z.string().min(3),
|
|
311
|
+
email: z.string().email(),
|
|
312
|
+
role: z.enum(["ADMIN", "USER", "MODERATOR"]),
|
|
313
|
+
age: z.number().min(18).optional(),
|
|
314
|
+
}),
|
|
315
|
+
meta: z.object({
|
|
316
|
+
mestamp: z.string(),
|
|
317
|
+
requestId: z.string(),
|
|
318
|
+
}),
|
|
319
|
+
});
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## 🔐 Authentication Headers
|
|
323
|
+
|
|
324
|
+
Configure global authentication headers:
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
{
|
|
328
|
+
headers: {
|
|
329
|
+
Authorization: {
|
|
330
|
+
description: "Bearer JWT token",
|
|
331
|
+
required: true
|
|
332
|
+
},
|
|
333
|
+
"X-API-Key": {
|
|
334
|
+
description: "API Key for service authentication",
|
|
335
|
+
required: false,
|
|
336
|
+
example: "your-api-key-here"
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## 📝 Advanced Example
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
import express from "express";
|
|
346
|
+
import { z } from "zod";
|
|
347
|
+
import {
|
|
348
|
+
setupSwagger,
|
|
349
|
+
responseMiddleware,
|
|
350
|
+
apiDoc,
|
|
351
|
+
loadConfig,
|
|
352
|
+
} from "@standard-api/core";
|
|
353
|
+
|
|
354
|
+
const app = express();
|
|
355
|
+
app.use(express.json());
|
|
356
|
+
|
|
357
|
+
const config = loadConfig();
|
|
358
|
+
app.use(responseMiddleware);
|
|
359
|
+
setupSwagger(app, config);
|
|
360
|
+
|
|
361
|
+
// Response schema
|
|
362
|
+
const UserListSchema = z.object({
|
|
363
|
+
success: z.literal(true),
|
|
364
|
+
message: z.string(),
|
|
365
|
+
data: z.array(
|
|
366
|
+
z.object({
|
|
367
|
+
id: z.string(),
|
|
368
|
+
name: z.string(),
|
|
369
|
+
email: z.string().email(),
|
|
370
|
+
role: z.enum(["ADMIN", "USER"]),
|
|
371
|
+
})
|
|
372
|
+
),
|
|
373
|
+
meta: z.object({
|
|
374
|
+
timestamp: z.string(),
|
|
375
|
+
requestId: z.string(),
|
|
376
|
+
pagination: z.object({
|
|
377
|
+
page: z.number(),
|
|
378
|
+
limit: z.number(),
|
|
379
|
+
total: z.number(),
|
|
380
|
+
}),
|
|
381
|
+
}),
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// GET endpoint with query params
|
|
385
|
+
app.get(
|
|
386
|
+
"/api/v1/users",
|
|
387
|
+
apiDoc({
|
|
388
|
+
method: "get",
|
|
389
|
+
path: "/api/v1/users",
|
|
390
|
+
summary: "List all users",
|
|
391
|
+
description: "Get paginated list of users",
|
|
392
|
+
tags: ["Users"],
|
|
393
|
+
request: {
|
|
394
|
+
query: z.object({
|
|
395
|
+
page: z.string().optional().describe("Page number"),
|
|
396
|
+
limit: z.string().optional().describe("Items per page"),
|
|
397
|
+
}),
|
|
398
|
+
},
|
|
399
|
+
response: UserListSchema,
|
|
400
|
+
}),
|
|
401
|
+
(req, res) => {
|
|
402
|
+
const page = parseInt(req.query.page as string) || 1;
|
|
403
|
+
const limit = parseInt(req.query.limit as string) || 10;
|
|
404
|
+
|
|
405
|
+
res.sendSuccess({
|
|
406
|
+
message: "Users fetched successfully",
|
|
407
|
+
data: users,
|
|
408
|
+
meta: {
|
|
409
|
+
pagination: { page, limit, total: 100 },
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
// POST endpoint with body
|
|
416
|
+
const CreateUserRequestSchema = z.object({
|
|
417
|
+
name: z.string().min(3),
|
|
418
|
+
email: z.string().email(),
|
|
419
|
+
role: z.enum(["ADMIN", "USER"]),
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
app.post(
|
|
423
|
+
"/api/v1/users",
|
|
424
|
+
apiDoc({
|
|
425
|
+
method: "post",
|
|
426
|
+
path: "/api/v1/users",
|
|
427
|
+
summary: "Create new user",
|
|
428
|
+
tags: ["Users"],
|
|
429
|
+
request: {
|
|
430
|
+
body: CreateUserRequestSchema,
|
|
431
|
+
},
|
|
432
|
+
response: UserSchema,
|
|
433
|
+
}),
|
|
434
|
+
(req, res) => {
|
|
435
|
+
// Validation happens automatically via Zod
|
|
436
|
+
const newUser = {
|
|
437
|
+
id: "123",
|
|
438
|
+
...req.body,
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
res.sendSuccess({
|
|
442
|
+
message: "User created successfully",
|
|
443
|
+
data: newUser,
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
// Error handling
|
|
449
|
+
app.use((err, req, res, next) => {
|
|
450
|
+
res.sendError({
|
|
451
|
+
message: err.message || "Internal server error",
|
|
452
|
+
code: err.code || "INTERNAL_ERROR",
|
|
453
|
+
error: process.env.NODE_ENV === "development" ? err.stack : {},
|
|
454
|
+
status: err.status || 500,
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
app.listen(3000);
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
## TypeScript Configuration
|
|
462
|
+
|
|
463
|
+
Ensure your `tsconfig.json` includes:
|
|
464
|
+
|
|
465
|
+
```json
|
|
466
|
+
{
|
|
467
|
+
"compilerOptions": {
|
|
468
|
+
"target": "ES2020",
|
|
469
|
+
"module": "commonjs",
|
|
470
|
+
"lib": ["ES2020"],
|
|
471
|
+
"strict": true,
|
|
472
|
+
Error Handling
|
|
473
|
+
|
|
474
|
+
The package provides standardized error responses. Implement a global error handler:
|
|
475
|
+
|
|
476
|
+
```typescript
|
|
477
|
+
app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
478
|
+
res.sendError({
|
|
479
|
+
message: err.message || "Internal server error",
|
|
480
|
+
code: err.code || "INTERNAL_ERROR",
|
|
481
|
+
error: process.env.NODE_ENV === "development" ? err.stack : undefined,
|
|
482
|
+
status: err.status || 500,
|
|
483
|
+
});
|
|
484
|
+
});
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
## Requirements
|
|
488
|
+
|
|
489
|
+
- Node.js >= 16.0.0
|
|
490
|
+
- Express.js >= 4.18.0
|
|
491
|
+
- Zod >= 3.22.0
|
|
492
|
+
- TypeScript >= 5.0.0 (for TypeScript projects)
|
|
493
|
+
|
|
494
|
+
## License
|
|
495
|
+
|
|
496
|
+
MIT
|
|
497
|
+
|
|
498
|
+
## Contributing
|
|
499
|
+
|
|
500
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
501
|
+
|
|
502
|
+
## Support
|
|
503
|
+
|
|
504
|
+
For issues and questions, please open an issue on the GitHub repository.
|
|
505
|
+
}
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
## 📄 License
|
|
509
|
+
|
|
510
|
+
MIT
|
|
511
|
+
|
|
512
|
+
## 🤝 Contributing
|
|
513
|
+
|
|
514
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
515
|
+
|
|
516
|
+
## 📧 Support
|
|
517
|
+
|
|
518
|
+
For issues and questions, please open an issue on the GitHub repository.
|
|
519
|
+
|
|
520
|
+
---
|
|
521
|
+
|
|
522
|
+
Made with ❤️ for the Express.js community
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-handler.d.ts","sourceRoot":"","sources":["../../src/core/response-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG1D,eAAO,MAAM,kBAAkB,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,SAuBjF,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.responseMiddleware = void 0;
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
const responseMiddleware = (req, res, next) => {
|
|
6
|
+
const requestId = (0, uuid_1.v4)();
|
|
7
|
+
const timestamp = new Date().toISOString();
|
|
8
|
+
res.sendSuccess = ({ message, data = {}, meta = {} }) => {
|
|
9
|
+
res.status(200).json({
|
|
10
|
+
success: true,
|
|
11
|
+
message,
|
|
12
|
+
data,
|
|
13
|
+
meta: { ...meta, timestamp, requestId }
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
res.sendError = ({ message, error = {}, code = 'INTERNAL_ERROR', status = 500 }) => {
|
|
17
|
+
res.status(status).json({
|
|
18
|
+
success: false,
|
|
19
|
+
message,
|
|
20
|
+
error: { code, details: error },
|
|
21
|
+
meta: { timestamp, requestId }
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
next();
|
|
25
|
+
};
|
|
26
|
+
exports.responseMiddleware = responseMiddleware;
|
|
27
|
+
//# sourceMappingURL=response-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-handler.js","sourceRoot":"","sources":["../../src/core/response-handler.ts"],"names":[],"mappings":";;;AACA,+BAAoC;AAE7B,MAAM,kBAAkB,GAAG,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IACpF,MAAM,SAAS,GAAG,IAAA,SAAM,GAAE,CAAC;IAC3B,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAE3C,GAAG,CAAC,WAAW,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,EAAE,IAAI,GAAG,EAAE,EAAE,EAAE,EAAE;QACtD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,OAAO,EAAE,IAAI;YACb,OAAO;YACP,IAAI;YACJ,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE;SACxC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,GAAG,CAAC,SAAS,GAAG,CAAC,EAAE,OAAO,EAAE,KAAK,GAAG,EAAE,EAAE,IAAI,GAAG,gBAAgB,EAAE,MAAM,GAAG,GAAG,EAAE,EAAE,EAAE;QACjF,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;YACtB,OAAO,EAAE,KAAK;YACd,OAAO;YACP,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE;YAC/B,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE;SAC/B,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,IAAI,EAAE,CAAC;AACT,CAAC,CAAC;AAvBW,QAAA,kBAAkB,sBAuB7B"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./types";
|
|
2
|
+
export * from "./core/response-handler";
|
|
3
|
+
export * from "./swagger/builder";
|
|
4
|
+
export * from "./swagger/registry";
|
|
5
|
+
export * from "./middleware/api-documenter";
|
|
6
|
+
export * from "./utils/config-loader";
|
|
7
|
+
export { responseMiddleware as setupResponseHandlers } from "./core/response-handler";
|
|
8
|
+
export { apiDoc as apiResponse } from "./middleware/api-documenter";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,uBAAuB,CAAC;AAGtC,OAAO,EAAE,kBAAkB,IAAI,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACtF,OAAO,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,6BAA6B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.apiResponse = exports.setupResponseHandlers = void 0;
|
|
18
|
+
__exportStar(require("./types"), exports);
|
|
19
|
+
__exportStar(require("./core/response-handler"), exports);
|
|
20
|
+
__exportStar(require("./swagger/builder"), exports);
|
|
21
|
+
__exportStar(require("./swagger/registry"), exports);
|
|
22
|
+
__exportStar(require("./middleware/api-documenter"), exports);
|
|
23
|
+
__exportStar(require("./utils/config-loader"), exports);
|
|
24
|
+
// Re-export commonly used items for convenience
|
|
25
|
+
var response_handler_1 = require("./core/response-handler");
|
|
26
|
+
Object.defineProperty(exports, "setupResponseHandlers", { enumerable: true, get: function () { return response_handler_1.responseMiddleware; } });
|
|
27
|
+
var api_documenter_1 = require("./middleware/api-documenter");
|
|
28
|
+
Object.defineProperty(exports, "apiResponse", { enumerable: true, get: function () { return api_documenter_1.apiDoc; } });
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,0DAAwC;AACxC,oDAAkC;AAClC,qDAAmC;AACnC,8DAA4C;AAC5C,wDAAsC;AAEtC,gDAAgD;AAChD,4DAAsF;AAA7E,yHAAA,kBAAkB,OAAyB;AACpD,8DAAoE;AAA3D,6GAAA,MAAM,OAAe"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-documenter.d.ts","sourceRoot":"","sources":["../../src/middleware/api-documenter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE1D,eAAO,MAAM,MAAM,GAAI,KAAK,QAAQ,MAwB1B,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,SACxD,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.apiDoc = void 0;
|
|
4
|
+
const registry_1 = require("../swagger/registry");
|
|
5
|
+
const apiDoc = (doc) => {
|
|
6
|
+
registry_1.registry.registerPath({
|
|
7
|
+
method: doc.method,
|
|
8
|
+
path: doc.path,
|
|
9
|
+
summary: doc.summary,
|
|
10
|
+
description: doc.description,
|
|
11
|
+
tags: doc.tags,
|
|
12
|
+
request: {
|
|
13
|
+
params: doc.request?.params,
|
|
14
|
+
query: doc.request?.query,
|
|
15
|
+
body: doc.request?.body
|
|
16
|
+
? {
|
|
17
|
+
content: { "application/json": { schema: doc.request.body } },
|
|
18
|
+
}
|
|
19
|
+
: undefined,
|
|
20
|
+
},
|
|
21
|
+
responses: {
|
|
22
|
+
200: {
|
|
23
|
+
description: "Successful Response",
|
|
24
|
+
content: { "application/json": { schema: doc.response } },
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
return (req, res, next) => next();
|
|
29
|
+
};
|
|
30
|
+
exports.apiDoc = apiDoc;
|
|
31
|
+
//# sourceMappingURL=api-documenter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-documenter.js","sourceRoot":"","sources":["../../src/middleware/api-documenter.ts"],"names":[],"mappings":";;;AAAA,kDAA+C;AAIxC,MAAM,MAAM,GAAG,CAAC,GAAa,EAAE,EAAE;IACtC,mBAAQ,CAAC,YAAY,CAAC;QACpB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,OAAO,EAAE;YACP,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,MAAa;YAClC,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,KAAY;YAChC,IAAI,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI;gBACrB,CAAC,CAAC;oBACE,OAAO,EAAE,EAAE,kBAAkB,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE;iBAC9D;gBACH,CAAC,CAAC,SAAS;SACd;QACD,SAAS,EAAE;YACT,GAAG,EAAE;gBACH,WAAW,EAAE,qBAAqB;gBAClC,OAAO,EAAE,EAAE,kBAAkB,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE;aAC1D;SACF;KACF,CAAC,CAAC;IAEH,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;AACrE,CAAC,CAAC;AAzBW,QAAA,MAAM,UAyBjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/swagger/builder.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGlC,eAAO,MAAM,YAAY,GAAI,KAAK,OAAO,EAAE,QAAQ,iBAAiB,SAqHnE,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.setupSwagger = void 0;
|
|
7
|
+
const zod_to_openapi_1 = require("@asteasolutions/zod-to-openapi");
|
|
8
|
+
const registry_1 = require("./registry");
|
|
9
|
+
const swagger_ui_express_1 = __importDefault(require("swagger-ui-express"));
|
|
10
|
+
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
11
|
+
const setupSwagger = (app, config) => {
|
|
12
|
+
const generator = new zod_to_openapi_1.OpenApiGeneratorV3(registry_1.registry.definitions);
|
|
13
|
+
// Build security schemes from headers
|
|
14
|
+
const components = {};
|
|
15
|
+
const security = [];
|
|
16
|
+
if (config.headers) {
|
|
17
|
+
components.securitySchemes = {};
|
|
18
|
+
Object.entries(config.headers).forEach(([name, headerConfig]) => {
|
|
19
|
+
const schemeName = name.toLowerCase();
|
|
20
|
+
if (name.toLowerCase() === "authorization") {
|
|
21
|
+
components.securitySchemes[schemeName] = {
|
|
22
|
+
type: "http",
|
|
23
|
+
scheme: "bearer",
|
|
24
|
+
bearerFormat: "JWT",
|
|
25
|
+
description: headerConfig.description,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
components.securitySchemes[schemeName] = {
|
|
30
|
+
type: "apiKey",
|
|
31
|
+
in: "header",
|
|
32
|
+
name: name,
|
|
33
|
+
description: headerConfig.description,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (headerConfig.required) {
|
|
37
|
+
security.push({ [schemeName]: [] });
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const docConfig = {
|
|
42
|
+
openapi: "3.0.0",
|
|
43
|
+
info: {
|
|
44
|
+
title: config.api.title,
|
|
45
|
+
version: config.api.version,
|
|
46
|
+
description: config.api.description,
|
|
47
|
+
},
|
|
48
|
+
servers: [{ url: config.api.baseUrl }],
|
|
49
|
+
};
|
|
50
|
+
if (Object.keys(components).length > 0) {
|
|
51
|
+
docConfig.components = components;
|
|
52
|
+
}
|
|
53
|
+
if (security.length > 0) {
|
|
54
|
+
docConfig.security = security;
|
|
55
|
+
}
|
|
56
|
+
const spec = generator.generateDocument(docConfig);
|
|
57
|
+
// Serve Swagger UI
|
|
58
|
+
app.use(config.swagger.path, swagger_ui_express_1.default.serve, swagger_ui_express_1.default.setup(spec, {
|
|
59
|
+
customSiteTitle: config.api.title,
|
|
60
|
+
customCss: ".swagger-ui .topbar { display: none }",
|
|
61
|
+
}));
|
|
62
|
+
// Download endpoints
|
|
63
|
+
if (config.swagger.enableDownload) {
|
|
64
|
+
// Full API spec - JSON
|
|
65
|
+
app.get(`${config.swagger.path}/openapi.json`, (req, res) => {
|
|
66
|
+
res.setHeader("Content-Type", "application/json");
|
|
67
|
+
res.setHeader("Content-Disposition", 'attachment; filename="openapi.json"');
|
|
68
|
+
res.json(spec);
|
|
69
|
+
});
|
|
70
|
+
// Full API spec - YAML
|
|
71
|
+
app.get(`${config.swagger.path}/openapi.yaml`, (req, res) => {
|
|
72
|
+
res.setHeader("Content-Type", "application/x-yaml");
|
|
73
|
+
res.setHeader("Content-Disposition", 'attachment; filename="openapi.yaml"');
|
|
74
|
+
res.send(js_yaml_1.default.dump(spec));
|
|
75
|
+
});
|
|
76
|
+
// Single endpoint spec
|
|
77
|
+
app.get(`${config.swagger.path}/endpoint/:method/:path(*)?`, (req, res) => {
|
|
78
|
+
const { method, path } = req.params;
|
|
79
|
+
const format = req.query.format || "json";
|
|
80
|
+
const fullPath = path ? `/${path}` : "/";
|
|
81
|
+
const pathSpec = spec.paths?.[fullPath];
|
|
82
|
+
const methodLower = method.toLowerCase();
|
|
83
|
+
if (!pathSpec || !pathSpec[methodLower]) {
|
|
84
|
+
return res.status(404).json({ error: "Endpoint not found" });
|
|
85
|
+
}
|
|
86
|
+
const endpointSpec = {
|
|
87
|
+
openapi: spec.openapi,
|
|
88
|
+
info: spec.info,
|
|
89
|
+
servers: spec.servers,
|
|
90
|
+
components: spec.components,
|
|
91
|
+
paths: {
|
|
92
|
+
[fullPath]: {
|
|
93
|
+
[methodLower]: pathSpec[methodLower],
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
if (format === "yaml") {
|
|
98
|
+
res.setHeader("Content-Type", "application/x-yaml");
|
|
99
|
+
res.send(js_yaml_1.default.dump(endpointSpec));
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
res.json(endpointSpec);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
exports.setupSwagger = setupSwagger;
|
|
108
|
+
//# sourceMappingURL=builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder.js","sourceRoot":"","sources":["../../src/swagger/builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/swagger/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEjE,eAAO,MAAM,QAAQ,iBAAwB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registry = void 0;
|
|
4
|
+
const zod_to_openapi_1 = require("@asteasolutions/zod-to-openapi");
|
|
5
|
+
exports.registry = new zod_to_openapi_1.OpenAPIRegistry();
|
|
6
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/swagger/registry.ts"],"names":[],"mappings":";;;AAAA,mEAAiE;AAEpD,QAAA,QAAQ,GAAG,IAAI,gCAAe,EAAE,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ZodSchema } from "zod";
|
|
2
|
+
export interface HeaderConfig {
|
|
3
|
+
description: string;
|
|
4
|
+
required: boolean;
|
|
5
|
+
example?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface StandardApiConfig {
|
|
8
|
+
api: {
|
|
9
|
+
title: string;
|
|
10
|
+
description: string;
|
|
11
|
+
version: string;
|
|
12
|
+
baseUrl: string;
|
|
13
|
+
};
|
|
14
|
+
headers?: Record<string, HeaderConfig>;
|
|
15
|
+
swagger: {
|
|
16
|
+
path: string;
|
|
17
|
+
enableDownload: boolean;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export interface RouteDoc {
|
|
21
|
+
method: "get" | "post" | "put" | "delete" | "patch";
|
|
22
|
+
path: string;
|
|
23
|
+
summary: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
tags?: string[];
|
|
26
|
+
request?: {
|
|
27
|
+
body?: ZodSchema;
|
|
28
|
+
query?: ZodSchema;
|
|
29
|
+
params?: ZodSchema;
|
|
30
|
+
headers?: ZodSchema;
|
|
31
|
+
};
|
|
32
|
+
response: ZodSchema;
|
|
33
|
+
errors?: {
|
|
34
|
+
[statusCode: number]: {
|
|
35
|
+
description: string;
|
|
36
|
+
schema?: ZodSchema;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export interface SuccessOptions {
|
|
41
|
+
message: string;
|
|
42
|
+
data?: any;
|
|
43
|
+
meta?: any;
|
|
44
|
+
}
|
|
45
|
+
export interface ErrorOptions {
|
|
46
|
+
message: string;
|
|
47
|
+
error?: any;
|
|
48
|
+
code?: string;
|
|
49
|
+
status?: number;
|
|
50
|
+
}
|
|
51
|
+
declare global {
|
|
52
|
+
namespace Express {
|
|
53
|
+
interface Response {
|
|
54
|
+
sendSuccess: (options: SuccessOptions) => void;
|
|
55
|
+
sendError: (options: ErrorOptions) => void;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAEhC,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE;QACH,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACvC,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;CACH;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,SAAS,CAAC;QACjB,KAAK,CAAC,EAAE,SAAS,CAAC;QAClB,MAAM,CAAC,EAAE,SAAS,CAAC;QACnB,OAAO,CAAC,EAAE,SAAS,CAAC;KACrB,CAAC;IACF,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,CAAC,EAAE;QACP,CAAC,UAAU,EAAE,MAAM,GAAG;YACpB,WAAW,EAAE,MAAM,CAAC;YACpB,MAAM,CAAC,EAAE,SAAS,CAAC;SACpB,CAAC;KACH,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,OAAO,CAAC;QAChB,UAAU,QAAQ;YAChB,WAAW,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;YAC/C,SAAS,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;SAC5C;KACF;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/utils/config-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAI7C,eAAO,MAAM,UAAU,GAAI,aAAa,MAAM,KAAG,iBAsChD,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.loadConfig = void 0;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const loadConfig = (configPath) => {
|
|
40
|
+
if (!configPath) {
|
|
41
|
+
// Try to find config file in common locations
|
|
42
|
+
const possiblePaths = [
|
|
43
|
+
"standard-api.config.ts",
|
|
44
|
+
"standard-api.config.js",
|
|
45
|
+
"./config/standard-api.config.ts",
|
|
46
|
+
"./config/standard-api.config.js",
|
|
47
|
+
];
|
|
48
|
+
for (const p of possiblePaths) {
|
|
49
|
+
const fullPath = path.resolve(process.cwd(), p);
|
|
50
|
+
if (fs.existsSync(fullPath)) {
|
|
51
|
+
configPath = fullPath;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (!configPath) {
|
|
57
|
+
throw new Error("Config file not found. Please provide a configPath or create standard-api.config.ts/js");
|
|
58
|
+
}
|
|
59
|
+
const fullPath = path.resolve(process.cwd(), configPath);
|
|
60
|
+
if (!fs.existsSync(fullPath)) {
|
|
61
|
+
throw new Error(`Config file not found at: ${fullPath}`);
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
65
|
+
const config = require(fullPath);
|
|
66
|
+
return config.default || config;
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
throw new Error(`Failed to load config from ${fullPath}: ${error}`);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
exports.loadConfig = loadConfig;
|
|
73
|
+
//# sourceMappingURL=config-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.js","sourceRoot":"","sources":["../../src/utils/config-loader.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,2CAA6B;AAC7B,uCAAyB;AAElB,MAAM,UAAU,GAAG,CAAC,UAAmB,EAAqB,EAAE;IACnE,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,8CAA8C;QAC9C,MAAM,aAAa,GAAG;YACpB,wBAAwB;YACxB,wBAAwB;YACxB,iCAAiC;YACjC,iCAAiC;SAClC,CAAC;QAEF,KAAK,MAAM,CAAC,IAAI,aAAa,EAAE,CAAC;YAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;YAChD,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5B,UAAU,GAAG,QAAQ,CAAC;gBACtB,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CACb,wFAAwF,CACzF,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,CAAC;IAEzD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,6BAA6B,QAAQ,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,IAAI,CAAC;QACH,8DAA8D;QAC9D,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;QACjC,OAAO,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,8BAA8B,QAAQ,KAAK,KAAK,EAAE,CAAC,CAAC;IACtE,CAAC;AACH,CAAC,CAAC;AAtCW,QAAA,UAAU,cAsCrB"}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "standard-api-core",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Production-ready NPM package for standardized API responses and automatic Swagger documentation generation for Express.js",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md",
|
|
10
|
+
"LICENSE"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"dev": "tsc -w",
|
|
15
|
+
"test": "jest",
|
|
16
|
+
"test:coverage": "jest --coverage",
|
|
17
|
+
"test:watch": "jest --watch",
|
|
18
|
+
"prepublishOnly": "npm run build"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@asteasolutions/zod-to-openapi": "^8.2.0",
|
|
22
|
+
"express": "^4.22.1",
|
|
23
|
+
"js-yaml": "^4.1.0",
|
|
24
|
+
"swagger-ui-express": "^5.0.0",
|
|
25
|
+
"uuid": "^9.0.1",
|
|
26
|
+
"zod": "^4.3.4"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/express": "^5.0.6",
|
|
30
|
+
"@types/jest": "^30.0.0",
|
|
31
|
+
"@types/js-yaml": "^4.0.9",
|
|
32
|
+
"@types/node": "^25.0.3",
|
|
33
|
+
"@types/supertest": "^6.0.2",
|
|
34
|
+
"@types/swagger-ui-express": "^4.1.6",
|
|
35
|
+
"@types/uuid": "^9.0.8",
|
|
36
|
+
"jest": "^30.2.0",
|
|
37
|
+
"supertest": "^7.1.4",
|
|
38
|
+
"ts-jest": "^29.1.1",
|
|
39
|
+
"typescript": "^5.3.3"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"express": "^4.22.0",
|
|
43
|
+
"zod": "^4.3.0"
|
|
44
|
+
},
|
|
45
|
+
"keywords": [
|
|
46
|
+
"api",
|
|
47
|
+
"rest",
|
|
48
|
+
"swagger",
|
|
49
|
+
"openapi",
|
|
50
|
+
"express",
|
|
51
|
+
"middleware",
|
|
52
|
+
"response",
|
|
53
|
+
"standardization",
|
|
54
|
+
"documentation",
|
|
55
|
+
"zod",
|
|
56
|
+
"typescript"
|
|
57
|
+
],
|
|
58
|
+
"author": "",
|
|
59
|
+
"license": "MIT",
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "https://github.com/inaumanmajeed/standard-api-core"
|
|
63
|
+
},
|
|
64
|
+
"bugs": {
|
|
65
|
+
"url": "https://github.com/inaumanmajeed/standard-api-core/issues"
|
|
66
|
+
},
|
|
67
|
+
"homepage": "https://github.com/inaumanmajeed/standard-api-core#readme",
|
|
68
|
+
"engines": {
|
|
69
|
+
"node": ">=18.0.0"
|
|
70
|
+
},
|
|
71
|
+
"type": "commonjs"
|
|
72
|
+
}
|