@engjts/nexus 0.1.8 → 0.1.9
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/package.json +1 -1
- package/BENCHMARK_REPORT.md +0 -343
- package/documentation/01-getting-started.md +0 -240
- package/documentation/02-context.md +0 -335
- package/documentation/03-routing.md +0 -397
- package/documentation/04-middleware.md +0 -483
- package/documentation/05-validation.md +0 -514
- package/documentation/06-error-handling.md +0 -465
- package/documentation/07-performance.md +0 -364
- package/documentation/08-adapters.md +0 -470
- package/documentation/09-api-reference.md +0 -548
- package/documentation/10-examples.md +0 -582
- package/documentation/11-deployment.md +0 -477
- package/documentation/12-sentry.md +0 -620
- package/documentation/13-sentry-data-storage.md +0 -996
- package/documentation/14-sentry-data-reference.md +0 -457
- package/documentation/15-sentry-summary.md +0 -409
- package/documentation/16-alerts-system.md +0 -745
- package/documentation/17-alert-adapters.md +0 -696
- package/documentation/18-alerts-implementation-summary.md +0 -385
- package/documentation/19-class-based-routing.md +0 -840
- package/documentation/20-websocket-realtime.md +0 -813
- package/documentation/21-cache-system.md +0 -510
- package/documentation/22-job-queue.md +0 -772
- package/documentation/23-sentry-plugin.md +0 -551
- package/documentation/24-testing-utilities.md +0 -1287
- package/documentation/25-api-versioning.md +0 -533
- package/documentation/26-context-store.md +0 -607
- package/documentation/27-dependency-injection.md +0 -329
- package/documentation/28-lifecycle-hooks.md +0 -521
- package/documentation/29-package-structure.md +0 -196
- package/documentation/30-plugin-system.md +0 -414
- package/documentation/31-jwt-authentication.md +0 -597
- package/documentation/32-cli.md +0 -268
- package/documentation/ALERTS-COMPLETE-SUMMARY.md +0 -429
- package/documentation/ALERTS-INDEX.md +0 -330
- package/documentation/ALERTS-QUICK-REFERENCE.md +0 -286
- package/documentation/README.md +0 -178
- package/documentation/index.html +0 -34
- package/modern_framework_paper.md +0 -1870
- package/public/css/style.css +0 -87
- package/public/index.html +0 -34
- package/public/js/app.js +0 -27
- package/src/advanced/cache/InMemoryCacheStore.ts +0 -68
- package/src/advanced/cache/MultiTierCache.ts +0 -194
- package/src/advanced/cache/RedisCacheStore.ts +0 -341
- package/src/advanced/cache/index.ts +0 -5
- package/src/advanced/cache/types.ts +0 -40
- package/src/advanced/graphql/SimpleDataLoader.ts +0 -42
- package/src/advanced/graphql/index.ts +0 -22
- package/src/advanced/graphql/server.ts +0 -252
- package/src/advanced/graphql/types.ts +0 -42
- package/src/advanced/jobs/InMemoryQueueStore.ts +0 -68
- package/src/advanced/jobs/JobQueue.ts +0 -556
- package/src/advanced/jobs/RedisQueueStore.ts +0 -367
- package/src/advanced/jobs/index.ts +0 -5
- package/src/advanced/jobs/types.ts +0 -70
- package/src/advanced/observability/APMManager.ts +0 -163
- package/src/advanced/observability/AlertManager.ts +0 -109
- package/src/advanced/observability/MetricRegistry.ts +0 -151
- package/src/advanced/observability/ObservabilityCenter.ts +0 -304
- package/src/advanced/observability/StructuredLogger.ts +0 -154
- package/src/advanced/observability/TracingManager.ts +0 -117
- package/src/advanced/observability/adapters.ts +0 -304
- package/src/advanced/observability/createObservabilityMiddleware.ts +0 -63
- package/src/advanced/observability/index.ts +0 -11
- package/src/advanced/observability/types.ts +0 -174
- package/src/advanced/playground/extractPathParams.ts +0 -6
- package/src/advanced/playground/generateFieldExample.ts +0 -31
- package/src/advanced/playground/generatePlaygroundHTML.ts +0 -1956
- package/src/advanced/playground/generateSummary.ts +0 -19
- package/src/advanced/playground/getTagFromPath.ts +0 -9
- package/src/advanced/playground/index.ts +0 -8
- package/src/advanced/playground/playground.ts +0 -250
- package/src/advanced/playground/types.ts +0 -49
- package/src/advanced/playground/zodToExample.ts +0 -16
- package/src/advanced/playground/zodToParams.ts +0 -15
- package/src/advanced/postman/buildAuth.ts +0 -31
- package/src/advanced/postman/buildBody.ts +0 -15
- package/src/advanced/postman/buildQueryParams.ts +0 -27
- package/src/advanced/postman/buildRequestItem.ts +0 -36
- package/src/advanced/postman/buildResponses.ts +0 -11
- package/src/advanced/postman/buildUrl.ts +0 -33
- package/src/advanced/postman/capitalize.ts +0 -4
- package/src/advanced/postman/generateCollection.ts +0 -59
- package/src/advanced/postman/generateEnvironment.ts +0 -34
- package/src/advanced/postman/generateExampleFromZod.ts +0 -21
- package/src/advanced/postman/generateFieldExample.ts +0 -45
- package/src/advanced/postman/generateName.ts +0 -20
- package/src/advanced/postman/generateUUID.ts +0 -11
- package/src/advanced/postman/getTagFromPath.ts +0 -10
- package/src/advanced/postman/index.ts +0 -28
- package/src/advanced/postman/postman.ts +0 -156
- package/src/advanced/postman/slugify.ts +0 -7
- package/src/advanced/postman/types.ts +0 -140
- package/src/advanced/realtime/index.ts +0 -18
- package/src/advanced/realtime/websocket.ts +0 -231
- package/src/advanced/sentry/index.ts +0 -1236
- package/src/advanced/sentry/types.ts +0 -355
- package/src/advanced/static/generateDirectoryListing.ts +0 -47
- package/src/advanced/static/generateETag.ts +0 -7
- package/src/advanced/static/getMimeType.ts +0 -9
- package/src/advanced/static/index.ts +0 -32
- package/src/advanced/static/isSafePath.ts +0 -13
- package/src/advanced/static/publicDir.ts +0 -21
- package/src/advanced/static/serveStatic.ts +0 -225
- package/src/advanced/static/spa.ts +0 -24
- package/src/advanced/static/types.ts +0 -159
- package/src/advanced/swagger/SwaggerGenerator.ts +0 -66
- package/src/advanced/swagger/buildOperation.ts +0 -61
- package/src/advanced/swagger/buildParameters.ts +0 -61
- package/src/advanced/swagger/buildRequestBody.ts +0 -21
- package/src/advanced/swagger/buildResponses.ts +0 -54
- package/src/advanced/swagger/capitalize.ts +0 -5
- package/src/advanced/swagger/convertPath.ts +0 -9
- package/src/advanced/swagger/createSwagger.ts +0 -12
- package/src/advanced/swagger/generateOperationId.ts +0 -21
- package/src/advanced/swagger/generateSpec.ts +0 -105
- package/src/advanced/swagger/generateSummary.ts +0 -24
- package/src/advanced/swagger/generateSwaggerUI.ts +0 -70
- package/src/advanced/swagger/generateThemeCss.ts +0 -53
- package/src/advanced/swagger/index.ts +0 -25
- package/src/advanced/swagger/swagger.ts +0 -237
- package/src/advanced/swagger/types.ts +0 -206
- package/src/advanced/swagger/zodFieldToOpenAPI.ts +0 -94
- package/src/advanced/swagger/zodSchemaToOpenAPI.ts +0 -50
- package/src/advanced/swagger/zodToOpenAPI.ts +0 -22
- package/src/advanced/testing/factory.ts +0 -509
- package/src/advanced/testing/harness.ts +0 -612
- package/src/advanced/testing/index.ts +0 -430
- package/src/advanced/testing/load-test.ts +0 -618
- package/src/advanced/testing/mock-server.ts +0 -498
- package/src/advanced/testing/mock.ts +0 -670
- package/src/cli/bin.ts +0 -9
- package/src/cli/cli.ts +0 -158
- package/src/cli/commands/add.ts +0 -178
- package/src/cli/commands/build.ts +0 -73
- package/src/cli/commands/create.ts +0 -166
- package/src/cli/commands/dev.ts +0 -85
- package/src/cli/commands/generate.ts +0 -99
- package/src/cli/commands/help.ts +0 -95
- package/src/cli/commands/init.ts +0 -91
- package/src/cli/commands/version.ts +0 -38
- package/src/cli/index.ts +0 -6
- package/src/cli/templates/generators.ts +0 -359
- package/src/cli/templates/index.ts +0 -680
- package/src/cli/utils/exec.ts +0 -52
- package/src/cli/utils/file-system.ts +0 -78
- package/src/cli/utils/logger.ts +0 -111
- package/src/core/adapter.ts +0 -88
- package/src/core/application.ts +0 -1453
- package/src/core/context-pool.ts +0 -79
- package/src/core/context.ts +0 -856
- package/src/core/index.ts +0 -94
- package/src/core/middleware.ts +0 -272
- package/src/core/performance/buffer-pool.ts +0 -108
- package/src/core/performance/middleware-optimizer.ts +0 -162
- package/src/core/plugin/PluginManager.ts +0 -435
- package/src/core/plugin/builder.ts +0 -358
- package/src/core/plugin/index.ts +0 -50
- package/src/core/plugin/types.ts +0 -214
- package/src/core/router/file-router.ts +0 -623
- package/src/core/router/index.ts +0 -260
- package/src/core/router/radix-tree.ts +0 -242
- package/src/core/serializer.ts +0 -397
- package/src/core/store/index.ts +0 -30
- package/src/core/store/registry.ts +0 -178
- package/src/core/store/request-store.ts +0 -240
- package/src/core/store/types.ts +0 -233
- package/src/core/types.ts +0 -616
- package/src/database/adapter.ts +0 -35
- package/src/database/adapters/index.ts +0 -1
- package/src/database/adapters/mysql.ts +0 -669
- package/src/database/database.ts +0 -70
- package/src/database/dialect.ts +0 -388
- package/src/database/index.ts +0 -12
- package/src/database/migrations.ts +0 -86
- package/src/database/optimizer.ts +0 -125
- package/src/database/query-builder.ts +0 -404
- package/src/database/realtime.ts +0 -53
- package/src/database/schema.ts +0 -71
- package/src/database/transactions.ts +0 -56
- package/src/database/types.ts +0 -87
- package/src/deployment/cluster.ts +0 -471
- package/src/deployment/config.ts +0 -454
- package/src/deployment/docker.ts +0 -599
- package/src/deployment/graceful-shutdown.ts +0 -373
- package/src/deployment/index.ts +0 -56
- package/src/index.ts +0 -281
- package/src/security/adapter.ts +0 -318
- package/src/security/auth/JWTPlugin.ts +0 -234
- package/src/security/auth/JWTProvider.ts +0 -316
- package/src/security/auth/adapter.ts +0 -12
- package/src/security/auth/jwt.ts +0 -234
- package/src/security/auth/middleware.ts +0 -188
- package/src/security/csrf.ts +0 -220
- package/src/security/headers.ts +0 -108
- package/src/security/index.ts +0 -60
- package/src/security/rate-limit/adapter.ts +0 -7
- package/src/security/rate-limit/memory.ts +0 -108
- package/src/security/rate-limit/middleware.ts +0 -181
- package/src/security/sanitization.ts +0 -75
- package/src/security/types.ts +0 -240
- package/src/security/utils.ts +0 -52
- package/tsconfig.json +0 -39
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
import { existsSync, statSync, createReadStream } from 'fs';
|
|
2
|
-
import { resolve, join, extname } from 'path/posix';
|
|
3
|
-
import { generateDirectoryListing } from './generateDirectoryListing';
|
|
4
|
-
import { isSafePath } from './isSafePath';
|
|
5
|
-
import { generateETag } from './generateETag';
|
|
6
|
-
import { getMimeType } from './getMimeType';
|
|
7
|
-
import { Plugin, Handler, Response } from '../../core/types';
|
|
8
|
-
import { StaticConfig } from './types';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Create static file serving feature
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
export function serveStatic(config: StaticConfig = {}): Plugin {
|
|
15
|
-
const {
|
|
16
|
-
root = './public', prefix = '', index = 'index.html', directory = false, maxAge = 86400, immutable = false, etag = true, lastModified = true, extensions, dotfiles = 'ignore', fallback, headers = {}, precompressed = false
|
|
17
|
-
} = config;
|
|
18
|
-
|
|
19
|
-
const rootPath = resolve(process.cwd(), root);
|
|
20
|
-
const normalizedPrefix = prefix.startsWith('/') ? prefix : prefix ? `/${prefix}` : '';
|
|
21
|
-
|
|
22
|
-
return {
|
|
23
|
-
name: 'static',
|
|
24
|
-
version: '1.0.0',
|
|
25
|
-
|
|
26
|
-
install(app: any) {
|
|
27
|
-
// Create the static file handler
|
|
28
|
-
const staticHandler: Handler = async (ctx) => {
|
|
29
|
-
let requestPath = ctx.path;
|
|
30
|
-
|
|
31
|
-
// Remove prefix if set
|
|
32
|
-
if (normalizedPrefix && requestPath.startsWith(normalizedPrefix)) {
|
|
33
|
-
requestPath = requestPath.slice(normalizedPrefix.length) || '/';
|
|
34
|
-
} else if (normalizedPrefix) {
|
|
35
|
-
// Path doesn't match prefix, skip
|
|
36
|
-
return { statusCode: 404, headers: {}, body: 'Not Found' };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Decode URL
|
|
40
|
-
try {
|
|
41
|
-
requestPath = decodeURIComponent(requestPath);
|
|
42
|
-
} catch {
|
|
43
|
-
return { statusCode: 400, headers: {}, body: 'Bad Request' };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Remove leading slash for joining
|
|
47
|
-
const cleanPath = requestPath.startsWith('/') ? requestPath.slice(1) : requestPath;
|
|
48
|
-
|
|
49
|
-
// Security: Check for directory traversal
|
|
50
|
-
if (!isSafePath(cleanPath, rootPath)) {
|
|
51
|
-
return { statusCode: 403, headers: {}, body: 'Forbidden' };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const filePath = join(rootPath, cleanPath);
|
|
55
|
-
|
|
56
|
-
// Handle dotfiles
|
|
57
|
-
const basename = cleanPath.split('/').pop() || '';
|
|
58
|
-
if (basename.startsWith('.')) {
|
|
59
|
-
if (dotfiles === 'deny') {
|
|
60
|
-
return { statusCode: 403, headers: {}, body: 'Forbidden' };
|
|
61
|
-
}
|
|
62
|
-
if (dotfiles === 'ignore') {
|
|
63
|
-
return { statusCode: 404, headers: {}, body: 'Not Found' };
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Check if file/directory exists
|
|
68
|
-
if (!existsSync(filePath)) {
|
|
69
|
-
// Try fallback for SPA
|
|
70
|
-
if (fallback) {
|
|
71
|
-
const fallbackPath = join(rootPath, fallback);
|
|
72
|
-
if (existsSync(fallbackPath)) {
|
|
73
|
-
return serveFile(fallbackPath, ctx);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return { statusCode: 404, headers: {}, body: 'Not Found' };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const stats = statSync(filePath);
|
|
80
|
-
|
|
81
|
-
// Handle directory
|
|
82
|
-
if (stats.isDirectory()) {
|
|
83
|
-
// Try index file(s)
|
|
84
|
-
if (index !== false) {
|
|
85
|
-
const indexFiles = Array.isArray(index) ? index : [index];
|
|
86
|
-
for (const indexFile of indexFiles) {
|
|
87
|
-
const indexPath = join(filePath, indexFile);
|
|
88
|
-
if (existsSync(indexPath)) {
|
|
89
|
-
return serveFile(indexPath, ctx);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Directory listing
|
|
95
|
-
if (directory) {
|
|
96
|
-
const html = await generateDirectoryListing(filePath, requestPath);
|
|
97
|
-
return {
|
|
98
|
-
statusCode: 200,
|
|
99
|
-
headers: { 'Content-Type': 'text/html; charset=utf-8' },
|
|
100
|
-
body: html
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return { statusCode: 404, headers: {}, body: 'Not Found' };
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Check extension whitelist
|
|
108
|
-
const ext = extname(filePath);
|
|
109
|
-
if (extensions && !extensions.includes(ext)) {
|
|
110
|
-
return { statusCode: 403, headers: {}, body: 'Forbidden' };
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return serveFile(filePath, ctx);
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
// Helper to serve a file
|
|
117
|
-
async function serveFile(filePath: string, ctx: any): Promise<Response> {
|
|
118
|
-
const stats = statSync(filePath);
|
|
119
|
-
const ext = extname(filePath);
|
|
120
|
-
const mimeType = getMimeType(ext);
|
|
121
|
-
|
|
122
|
-
const responseHeaders: Record<string, string> = {
|
|
123
|
-
'Content-Type': mimeType,
|
|
124
|
-
'Content-Length': stats.size.toString(),
|
|
125
|
-
...headers
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
// Cache headers
|
|
129
|
-
let cacheControl = `public, max-age=${maxAge}`;
|
|
130
|
-
if (immutable) {
|
|
131
|
-
cacheControl += ', immutable';
|
|
132
|
-
}
|
|
133
|
-
responseHeaders['Cache-Control'] = cacheControl;
|
|
134
|
-
|
|
135
|
-
// ETag
|
|
136
|
-
if (etag) {
|
|
137
|
-
const etagValue = generateETag(stats);
|
|
138
|
-
responseHeaders['ETag'] = etagValue;
|
|
139
|
-
|
|
140
|
-
// Check If-None-Match
|
|
141
|
-
const ifNoneMatch = ctx.headers['if-none-match'];
|
|
142
|
-
if (ifNoneMatch === etagValue) {
|
|
143
|
-
return { statusCode: 304, headers: responseHeaders, body: '' };
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Last-Modified
|
|
148
|
-
if (lastModified) {
|
|
149
|
-
const lastMod = stats.mtime.toUTCString();
|
|
150
|
-
responseHeaders['Last-Modified'] = lastMod;
|
|
151
|
-
|
|
152
|
-
// Check If-Modified-Since
|
|
153
|
-
const ifModifiedSince = ctx.headers['if-modified-since'];
|
|
154
|
-
if (ifModifiedSince) {
|
|
155
|
-
const ifModDate = new Date(ifModifiedSince);
|
|
156
|
-
if (stats.mtime <= ifModDate) {
|
|
157
|
-
return { statusCode: 304, headers: responseHeaders, body: '' };
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Check for pre-compressed version
|
|
163
|
-
if (precompressed) {
|
|
164
|
-
const acceptEncoding = ctx.headers['accept-encoding'] || '';
|
|
165
|
-
const compressions = Array.isArray(precompressed)
|
|
166
|
-
? precompressed
|
|
167
|
-
: ['br', 'gzip'];
|
|
168
|
-
|
|
169
|
-
for (const encoding of compressions) {
|
|
170
|
-
if (acceptEncoding.includes(encoding)) {
|
|
171
|
-
const ext = encoding === 'br' ? '.br' : '.gz';
|
|
172
|
-
const compressedPath = filePath + ext;
|
|
173
|
-
if (existsSync(compressedPath)) {
|
|
174
|
-
const compressedStats = statSync(compressedPath);
|
|
175
|
-
responseHeaders['Content-Encoding'] = encoding;
|
|
176
|
-
responseHeaders['Content-Length'] = compressedStats.size.toString();
|
|
177
|
-
responseHeaders['Vary'] = 'Accept-Encoding';
|
|
178
|
-
|
|
179
|
-
return {
|
|
180
|
-
statusCode: 200,
|
|
181
|
-
headers: responseHeaders,
|
|
182
|
-
body: '',
|
|
183
|
-
stream: createReadStream(compressedPath)
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Stream the file
|
|
191
|
-
return {
|
|
192
|
-
statusCode: 200,
|
|
193
|
-
headers: responseHeaders,
|
|
194
|
-
body: '',
|
|
195
|
-
stream: createReadStream(filePath)
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Register static file handler
|
|
200
|
-
if (normalizedPrefix) {
|
|
201
|
-
// With prefix: only serve files under that prefix as routes
|
|
202
|
-
app.get(`${normalizedPrefix}`, staticHandler);
|
|
203
|
-
app.get(`${normalizedPrefix}/*`, staticHandler);
|
|
204
|
-
} else {
|
|
205
|
-
// Without prefix: register as fallback handler
|
|
206
|
-
// This is called when no routes match, before 404
|
|
207
|
-
if (typeof app.setFallbackHandler === 'function') {
|
|
208
|
-
app.setFallbackHandler(staticHandler);
|
|
209
|
-
} else {
|
|
210
|
-
// Fallback: register as low-priority wildcard routes
|
|
211
|
-
// NOTE: This should be registered LAST after all API routes
|
|
212
|
-
app.get('/', staticHandler);
|
|
213
|
-
app.get('/*', staticHandler);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (app.config?.debug) {
|
|
218
|
-
console.log(`📁 Static files: ${rootPath}`);
|
|
219
|
-
if (normalizedPrefix) {
|
|
220
|
-
console.log(` Prefix: ${normalizedPrefix}`);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { Plugin } from '../../core/types';
|
|
2
|
-
import { serveStatic } from './serveStatic';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Create SPA (Single Page Application) static serving
|
|
6
|
-
* Falls back to index.html for client-side routing
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```typescript
|
|
10
|
-
* app.plugin(spa()); // SPA from ./public
|
|
11
|
-
* app.plugin(spa('./dist')); // SPA from ./dist
|
|
12
|
-
* ```
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
export function spa(root = './public'): Plugin {
|
|
16
|
-
return serveStatic({
|
|
17
|
-
root,
|
|
18
|
-
fallback: 'index.html',
|
|
19
|
-
maxAge: 0, // No cache for HTML
|
|
20
|
-
etag: true
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export type { Plugin };
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MIME types mapping
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export const MIME_TYPES: Record<string, string> = {
|
|
6
|
-
// Text
|
|
7
|
-
'.html': 'text/html; charset=utf-8',
|
|
8
|
-
'.htm': 'text/html; charset=utf-8',
|
|
9
|
-
'.css': 'text/css; charset=utf-8',
|
|
10
|
-
'.js': 'text/javascript; charset=utf-8',
|
|
11
|
-
'.mjs': 'text/javascript; charset=utf-8',
|
|
12
|
-
'.json': 'application/json; charset=utf-8',
|
|
13
|
-
'.xml': 'application/xml; charset=utf-8',
|
|
14
|
-
'.txt': 'text/plain; charset=utf-8',
|
|
15
|
-
'.md': 'text/markdown; charset=utf-8',
|
|
16
|
-
'.csv': 'text/csv; charset=utf-8',
|
|
17
|
-
|
|
18
|
-
// Images
|
|
19
|
-
'.png': 'image/png',
|
|
20
|
-
'.jpg': 'image/jpeg',
|
|
21
|
-
'.jpeg': 'image/jpeg',
|
|
22
|
-
'.gif': 'image/gif',
|
|
23
|
-
'.webp': 'image/webp',
|
|
24
|
-
'.svg': 'image/svg+xml',
|
|
25
|
-
'.ico': 'image/x-icon',
|
|
26
|
-
'.bmp': 'image/bmp',
|
|
27
|
-
'.avif': 'image/avif',
|
|
28
|
-
|
|
29
|
-
// Fonts
|
|
30
|
-
'.woff': 'font/woff',
|
|
31
|
-
'.woff2': 'font/woff2',
|
|
32
|
-
'.ttf': 'font/ttf',
|
|
33
|
-
'.otf': 'font/otf',
|
|
34
|
-
'.eot': 'application/vnd.ms-fontobject',
|
|
35
|
-
|
|
36
|
-
// Audio
|
|
37
|
-
'.mp3': 'audio/mpeg',
|
|
38
|
-
'.wav': 'audio/wav',
|
|
39
|
-
'.ogg': 'audio/ogg',
|
|
40
|
-
'.m4a': 'audio/mp4',
|
|
41
|
-
'.flac': 'audio/flac',
|
|
42
|
-
|
|
43
|
-
// Video
|
|
44
|
-
'.mp4': 'video/mp4',
|
|
45
|
-
'.webm': 'video/webm',
|
|
46
|
-
'.avi': 'video/x-msvideo',
|
|
47
|
-
'.mov': 'video/quicktime',
|
|
48
|
-
'.mkv': 'video/x-matroska',
|
|
49
|
-
|
|
50
|
-
// Documents
|
|
51
|
-
'.pdf': 'application/pdf',
|
|
52
|
-
'.doc': 'application/msword',
|
|
53
|
-
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
54
|
-
'.xls': 'application/vnd.ms-excel',
|
|
55
|
-
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
56
|
-
'.ppt': 'application/vnd.ms-powerpoint',
|
|
57
|
-
'.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
58
|
-
|
|
59
|
-
// Archives
|
|
60
|
-
'.zip': 'application/zip',
|
|
61
|
-
'.rar': 'application/vnd.rar',
|
|
62
|
-
'.7z': 'application/x-7z-compressed',
|
|
63
|
-
'.tar': 'application/x-tar',
|
|
64
|
-
'.gz': 'application/gzip',
|
|
65
|
-
|
|
66
|
-
// Data
|
|
67
|
-
'.wasm': 'application/wasm',
|
|
68
|
-
'.map': 'application/json',
|
|
69
|
-
|
|
70
|
-
// Default
|
|
71
|
-
'': 'application/octet-stream'
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Static file serving configuration
|
|
77
|
-
*/
|
|
78
|
-
export interface StaticConfig {
|
|
79
|
-
/**
|
|
80
|
-
* Root directory to serve files from
|
|
81
|
-
* @default './public'
|
|
82
|
-
*/
|
|
83
|
-
root?: string;
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* URL prefix for static files
|
|
87
|
-
* @default '' (serve from root)
|
|
88
|
-
* @example '/static' → /static/image.png serves ./public/image.png
|
|
89
|
-
*/
|
|
90
|
-
prefix?: string;
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Default file to serve for directory requests
|
|
94
|
-
* @default 'index.html'
|
|
95
|
-
*/
|
|
96
|
-
index?: string | string[] | false;
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Enable directory listing
|
|
100
|
-
* @default false
|
|
101
|
-
*/
|
|
102
|
-
directory?: boolean;
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Maximum age for Cache-Control header (in seconds)
|
|
106
|
-
* @default 86400 (1 day)
|
|
107
|
-
*/
|
|
108
|
-
maxAge?: number;
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Enable immutable caching (for versioned assets)
|
|
112
|
-
* @default false
|
|
113
|
-
*/
|
|
114
|
-
immutable?: boolean;
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Enable ETag generation
|
|
118
|
-
* @default true
|
|
119
|
-
*/
|
|
120
|
-
etag?: boolean;
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Enable Last-Modified header
|
|
124
|
-
* @default true
|
|
125
|
-
*/
|
|
126
|
-
lastModified?: boolean;
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Allowed file extensions (whitelist)
|
|
130
|
-
* @default undefined (allow all)
|
|
131
|
-
* @example ['.html', '.css', '.js', '.png']
|
|
132
|
-
*/
|
|
133
|
-
extensions?: string[];
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Hidden files (dotfiles) handling
|
|
137
|
-
* @default 'ignore'
|
|
138
|
-
*/
|
|
139
|
-
dotfiles?: 'allow' | 'deny' | 'ignore';
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Fallback file for SPA routing
|
|
143
|
-
* @default undefined
|
|
144
|
-
* @example 'index.html' for SPA
|
|
145
|
-
*/
|
|
146
|
-
fallback?: string;
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Custom headers to add to responses
|
|
150
|
-
*/
|
|
151
|
-
headers?: Record<string, string>;
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Enable gzip/brotli pre-compressed files
|
|
155
|
-
* Looks for .gz or .br versions of files
|
|
156
|
-
* @default false
|
|
157
|
-
*/
|
|
158
|
-
precompressed?: boolean | ('gzip' | 'br')[];
|
|
159
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { RouteConfig } from '../../core/types';
|
|
2
|
-
import { generateSpec } from './generateSpec';
|
|
3
|
-
import { generateSwaggerUI } from './generateSwaggerUI';
|
|
4
|
-
import { SwaggerConfig, StoredRoute, OpenAPISchema, OpenAPISpec } from './types';
|
|
5
|
-
|
|
6
|
-
// ============================================
|
|
7
|
-
// LEGACY EXPORTS (backward compatibility)
|
|
8
|
-
// ============================================
|
|
9
|
-
/**
|
|
10
|
-
* SwaggerGenerator class for advanced/manual usage
|
|
11
|
-
* @deprecated Use swagger() plugin instead
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export class SwaggerGenerator {
|
|
16
|
-
private config: SwaggerConfig;
|
|
17
|
-
private routes: StoredRoute[] = [];
|
|
18
|
-
private schemas: Map<string, OpenAPISchema> = new Map();
|
|
19
|
-
|
|
20
|
-
constructor(config: SwaggerConfig = {}) {
|
|
21
|
-
this.config = {
|
|
22
|
-
path: '/docs',
|
|
23
|
-
specPath: '/openapi.json',
|
|
24
|
-
...config,
|
|
25
|
-
info: {
|
|
26
|
-
title: 'API Documentation',
|
|
27
|
-
version: '1.0.0',
|
|
28
|
-
...config.info
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
registerRoutes(routes: RouteConfig[]): void {
|
|
34
|
-
this.routes.push(...routes.map(r => ({
|
|
35
|
-
method: r.method,
|
|
36
|
-
path: r.path,
|
|
37
|
-
schema: r.schema,
|
|
38
|
-
meta: r.meta
|
|
39
|
-
})));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
registerRoute(route: RouteConfig): void {
|
|
43
|
-
this.routes.push({
|
|
44
|
-
method: route.method,
|
|
45
|
-
path: route.path,
|
|
46
|
-
schema: route.schema,
|
|
47
|
-
meta: route.meta
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
registerSchema(name: string, schema: OpenAPISchema): void {
|
|
52
|
-
this.schemas.set(name, schema);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
generateSpec(): OpenAPISpec {
|
|
56
|
-
return generateSpec(this.routes, this.schemas, this.config, '');
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
generateSwaggerUI(): string {
|
|
60
|
-
return generateSwaggerUI(this.config);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
getConfig(): SwaggerConfig {
|
|
64
|
-
return this.config;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { buildRequestBody } from './buildRequestBody';
|
|
2
|
-
import { buildParameters } from './buildParameters';
|
|
3
|
-
import { buildResponses } from './buildResponses';
|
|
4
|
-
import { capitalize } from './capitalize';
|
|
5
|
-
import { generateOperationId } from './generateOperationId';
|
|
6
|
-
import { generateSummary } from './generateSummary';
|
|
7
|
-
import { StoredRoute, SwaggerConfig, OpenAPIOperation } from './types';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Build OpenAPI operation from route
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export function buildOperation(route: StoredRoute, config: SwaggerConfig): OpenAPIOperation {
|
|
15
|
-
const meta = route.meta || {};
|
|
16
|
-
|
|
17
|
-
const operation: OpenAPIOperation = {
|
|
18
|
-
responses: buildResponses(meta.responses, route.method)
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
// Auto-generate summary from path if not provided
|
|
22
|
-
if (meta.summary) {
|
|
23
|
-
operation.summary = meta.summary;
|
|
24
|
-
} else {
|
|
25
|
-
operation.summary = generateSummary(route.method, route.path);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (meta.description) {
|
|
29
|
-
operation.description = meta.description;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (meta.tags && meta.tags.length > 0) {
|
|
33
|
-
operation.tags = meta.tags;
|
|
34
|
-
} else if (!config.hideUntagged) {
|
|
35
|
-
// Auto-tag based on first path segment
|
|
36
|
-
const firstSegment = route.path.split('/').filter(Boolean)[0];
|
|
37
|
-
if (firstSegment && !firstSegment.startsWith(':')) {
|
|
38
|
-
operation.tags = [capitalize(firstSegment)];
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (meta.deprecated) {
|
|
43
|
-
operation.deprecated = true;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Generate operation ID
|
|
47
|
-
operation.operationId = generateOperationId(route.method, route.path);
|
|
48
|
-
|
|
49
|
-
// Build parameters from schema
|
|
50
|
-
const parameters = buildParameters(route);
|
|
51
|
-
if (parameters.length > 0) {
|
|
52
|
-
operation.parameters = parameters;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Build request body for POST/PUT/PATCH
|
|
56
|
-
if (route.schema?.body && ['POST', 'PUT', 'PATCH'].includes(route.method)) {
|
|
57
|
-
operation.requestBody = buildRequestBody(route.schema);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return operation;
|
|
61
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { zodToOpenAPI } from './zodToOpenAPI';
|
|
2
|
-
import { zodSchemaToOpenAPI } from './zodSchemaToOpenAPI';
|
|
3
|
-
import { capitalize } from './capitalize';
|
|
4
|
-
import { StoredRoute, OpenAPIParameter, OpenAPISchema } from './types';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Build parameters from route schema
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export function buildParameters(route: StoredRoute): OpenAPIParameter[] {
|
|
12
|
-
const parameters: OpenAPIParameter[] = [];
|
|
13
|
-
|
|
14
|
-
// Extract path parameters
|
|
15
|
-
const pathParams = route.path.match(/:(\w+)/g);
|
|
16
|
-
if (pathParams) {
|
|
17
|
-
for (const param of pathParams) {
|
|
18
|
-
const name = param.slice(1);
|
|
19
|
-
const schemaFromZod = zodToOpenAPI(route.schema?.params, name);
|
|
20
|
-
parameters.push({
|
|
21
|
-
name,
|
|
22
|
-
in: 'path',
|
|
23
|
-
required: true,
|
|
24
|
-
description: `${capitalize(name)} parameter`,
|
|
25
|
-
schema: schemaFromZod || { type: 'string' }
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Add query parameters from schema
|
|
31
|
-
if (route.schema?.query) {
|
|
32
|
-
const querySchema = zodSchemaToOpenAPI(route.schema.query);
|
|
33
|
-
if (querySchema.properties) {
|
|
34
|
-
for (const [name, schema] of Object.entries(querySchema.properties)) {
|
|
35
|
-
parameters.push({
|
|
36
|
-
name,
|
|
37
|
-
in: 'query',
|
|
38
|
-
required: querySchema.required?.includes(name) || false,
|
|
39
|
-
schema: schema as OpenAPISchema
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Add header parameters from schema
|
|
46
|
-
if (route.schema?.headers) {
|
|
47
|
-
const headerSchema = zodSchemaToOpenAPI(route.schema.headers);
|
|
48
|
-
if (headerSchema.properties) {
|
|
49
|
-
for (const [name, schema] of Object.entries(headerSchema.properties)) {
|
|
50
|
-
parameters.push({
|
|
51
|
-
name,
|
|
52
|
-
in: 'header',
|
|
53
|
-
required: headerSchema.required?.includes(name) || false,
|
|
54
|
-
schema: schema as OpenAPISchema
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return parameters;
|
|
61
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { zodSchemaToOpenAPI } from './zodSchemaToOpenAPI';
|
|
2
|
-
import { SchemaConfig } from '../../core/types';
|
|
3
|
-
import { OpenAPIRequestBody, OpenAPISchema } from './types';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Build request body from schema
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export function buildRequestBody(schema: SchemaConfig): OpenAPIRequestBody {
|
|
11
|
-
const bodySchema = schema.body ? zodSchemaToOpenAPI(schema.body) : { type: 'object' };
|
|
12
|
-
|
|
13
|
-
return {
|
|
14
|
-
required: true,
|
|
15
|
-
content: {
|
|
16
|
-
'application/json': {
|
|
17
|
-
schema: bodySchema as OpenAPISchema
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { HTTPMethod } from '../../core/types';
|
|
2
|
-
import { OpenAPIResponse } from './types';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Build response objects
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export function buildResponses(
|
|
10
|
-
responses?: Record<number, string>,
|
|
11
|
-
method?: HTTPMethod
|
|
12
|
-
): Record<string, OpenAPIResponse> {
|
|
13
|
-
const result: Record<string, OpenAPIResponse> = {};
|
|
14
|
-
|
|
15
|
-
if (responses) {
|
|
16
|
-
for (const [code, description] of Object.entries(responses)) {
|
|
17
|
-
result[code] = {
|
|
18
|
-
description,
|
|
19
|
-
content: {
|
|
20
|
-
'application/json': {
|
|
21
|
-
schema: { type: 'object' }
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Add default responses based on method
|
|
29
|
-
if (!result['200'] && !result['201'] && !result['204']) {
|
|
30
|
-
if (method === 'POST') {
|
|
31
|
-
result['201'] = {
|
|
32
|
-
description: 'Created successfully',
|
|
33
|
-
content: { 'application/json': { schema: { type: 'object' } } }
|
|
34
|
-
};
|
|
35
|
-
} else if (method === 'DELETE') {
|
|
36
|
-
result['204'] = { description: 'Deleted successfully' };
|
|
37
|
-
} else {
|
|
38
|
-
result['200'] = {
|
|
39
|
-
description: 'Successful response',
|
|
40
|
-
content: { 'application/json': { schema: { type: 'object' } } }
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Add common error responses
|
|
46
|
-
if (!result['400']) {
|
|
47
|
-
result['400'] = { description: 'Bad request' };
|
|
48
|
-
}
|
|
49
|
-
if (!result['500']) {
|
|
50
|
-
result['500'] = { description: 'Internal server error' };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return result;
|
|
54
|
-
}
|